Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

os/exec: run "echo xxx" hung up #21232

Closed
mei-rune opened this issue Jul 31, 2017 · 12 comments
Closed

os/exec: run "echo xxx" hung up #21232

mei-rune opened this issue Jul 31, 2017 · 12 comments

Comments

@mei-rune
Copy link

What version of Go are you using (go version)?

go version go1.8.1 windows/amd64

What operating system and processor architecture are you using (go env)?

set GOARCH=amd64
set GOBIN=
set GOEXE=.exe
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOOS=windows
set GOPATH=d:\developing\go\meijing;d:\developing\go\meijing\tpt_vendor;d:\dev
eloping\go\meijing\src\cn\com\hengwei\Godeps_workspace
set GORACE=
set GOROOT=d:\tools\go_amd64
set GOTOOLDIR=d:\tools\go_amd64\pkg\tool\windows_amd64
set GCCGO=gccgo
set CC=gcc
set GOGCCFLAGS=-m64 -mthreads -fmessage-length=0 -fdebug-prefix-map=C:\Users\mei
fakun\AppData\Local\Temp\go-build280858219=/tmp/go-build -gno-record-gcc-switche
s
set CXX=g++
set CGO_ENABLED=1
set PKG_CONFIG=pkg-config
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2

What did you do?

run "echo xxx" command and read console ouput of comand

https://play.golang.org/p/Dob0vjP-L6

What did you expect to see?

excpect output: abc
don't output: timeout

What did you see instead?

process is hung up util timeout

@davecheney
Copy link
Contributor

davecheney commented Jul 31, 2017 via email

@mei-rune
Copy link
Author

mei-rune commented Jul 31, 2017

@davecheney Yes, it is a tcp conn.

I rename createPipe to createConn
https://play.golang.org/p/67tDZbIKzd

@davecheney
Copy link
Contributor

Pipes have two ends, createPipe only returns one end of the pipe, the other is lost during the call to accept.

@mei-rune
Copy link
Author

mei-rune commented Jul 31, 2017

@davecheney Yes, the other end should give to my another app1 in my real world, it will send some chars to "echo" command by the pipe, "echo" is my another app2.
I hope "if app2 is exited, then cmd.Wait() is return, and I should close the conn.

@davecheney
Copy link
Contributor

davecheney commented Jul 31, 2017 via email

@mei-rune
Copy link
Author

@davecheney No, "echo" is not echo of linux(windows), echo is a my app, it read message from stdio and do some things. I replace my app name with "echo" in the example.
sometimes the my app will crash, but cmd.Wait() will hung up.

@mei-rune
Copy link
Author

mei-rune commented Jul 31, 2017

I hope Wait() don't wait stdin if process is exited, and I self close the stdin.
Becase sometimes stdin may not close forever

@mattn
Copy link
Member

mattn commented Jul 31, 2017

Because the client connection that returned from l.Accept() is not GC collected. You can confirm connection will be closed with

func createPipe() net.Conn {
	defer runtime.GC()
	l, err := net.Listen("tcp", "127.0.0.1:52354")
	if err != nil {
		panic(err)
	}
	go func() {
		l.Accept()
		// Client Conneciton will be closed by runtime.GC above
	}()
	fmt.Println(l.Addr().String())

	conn, err := net.Dial("tcp", "127.0.0.1:52354")
	if err != nil {
		panic(err)
	}
	return conn
}

@mei-rune
Copy link
Author

mei-rune commented Jul 31, 2017

@mattn Yes, the client connection is lose and not close in the example, But the client connection should give to my another app1 in my real world, it will send some chars to the "echo" app by the pipe, "echo" is my another app2.

I hope Wait() don't wait stdin if process is exited, and I self close the stdin.

I am chinese, english is bad, I draw a picture to explain it at tomorrow.

@mattn
Copy link
Member

mattn commented Jul 31, 2017

cmd.Wait wait goroutines which is spawned for reading pipes. If you want to just wait the process without waiting pipes, please use cmd.Process.Wait

// You can edit this code!
// Click here and start typing.
package main

import (
	"bytes"
	"fmt"
	"net"
	"os/exec"
	"runtime"
	"time"
)

func createPipe() net.Conn {
	l, err := net.Listen("tcp", "127.0.0.1:52354")
	if err != nil {
		panic(err)
	}
	go func() {
		l.Accept()
	}()
	fmt.Println(l.Addr().String())

	conn, err := net.Dial("tcp", "127.0.0.1:52354")
	if err != nil {
		panic(err)
	}
	return conn
}

func main() {
	var buf bytes.Buffer
	var cmd *exec.Cmd
	if runtime.GOOS == "windows" {
		cmd = exec.Command("cmd", "/c", "echo", "abc")
	} else {
		cmd = exec.Command("echo", "abc")
	}

	pipe := createPipe() // cmd.Wait() will not exit if pipe don't close()

	cmd.Stdin = pipe
	cmd.Stdout = &buf
	cmd.Stderr = &buf
	if err := cmd.Start(); err != nil {
		panic(err)
	}
	time.AfterFunc(1*time.Minute, func() {
		cmd.Process.Kill()
		fmt.Println("timeout")
	})
	if _, err := cmd.Process.Wait(); err != nil {
		panic(err)
	} else {
		fmt.Println(buf.String())
	}
}

@mei-rune
Copy link
Author

mei-rune commented Aug 2, 2017

@mattn Yes, it is ok.

Please add a document to describe it.
in addition you cannot call cmd.Wait () after calling cmd.Process.Wait (), otherwise it will blocked, it should be warned in the document.

@ianlancetaylor
Copy link
Contributor

Merging into #23019.

@golang golang locked and limited conversation to collaborators Mar 30, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants