So, I have this package: https://github.com/nubunto/ports Basically, a test that provides an API for a interface in Go to a external program. There's a test in there. That test is failing, with a deadlock. Any insights on the why?
Thanks in advance, reddit gophers.
评论:
skidooer:
Logiraptorr:Your
Launch
function returns an error, which results in the inner goroutine not being executed. Then, when you try to read from the channel, with no other goroutines operating, you are effectively doing this:func main() { ch := make(chan int) <-ch // deadlock }
One way to fix this is:
diff --git a/ports_test.go b/ports_test.go index 7d4c47f..76ae1c0 100644 --- a/ports_test.go +++ b/ports_test.go @@ -18,6 +18,7 @@ func TestLaunch(t *testing.T) { err := Launch(test.program, ch) if err != nil { t.Errorf("launch returned error: %v\n", err) + continue } ret := <-ch if !bytes.Equal(test.expected, ret) {
To elaborate on /u/skidooer's answer:
Launch returns the error
fork/exec echo "hello world": no such file or directory
.This is because you're passing
echo "hello world"
as thePath
field to the exec.Command. The exec package expects that to be the path to an executable, not including any arguments to the executable. To fix this, you'll have to pass arguments separately as the Args field. See: exec.Cmd.Now more general comments:
It is probably more useful to make Launch take arguments as a separate parameter. You can see in the Cmd struct docs above that the intended way to use it is to call
exec.Command
, passing both the path and args. You can then set fields on the returned value if you need to customize things.It may just be a prototype, but in this example you're passing a bytes.Buffer as both stdout and stderr. If all you want is to get both output streams as a byte buffer, you can use
Cmd.CombinedOutput
. This will start the program as well as collect the output for you.In general, it's unusual to use channels in the way you are doing here. Typically, if a function starts a go routine and wants to communicate about its status later, it will create it's own channel internally and return it. In your case specifically, it would not be clear to me that Launch closes the channel passed as the second argument. However, if Launch creates the channel, then Launch is the "owner" so to speak, and I would expect it to close the channel at some point in the future.
I hope you found this useful. Here is a sample of how I might change this code as it is now (without knowing what the long term plan is). ports.go, ports_test.go
