4 ms·
https://dave.cheney.net/practical-go/presentations/qcon-china.html#_never_start_a_goroutine_without_when_it_will_stop https://dave.cheney.net/practical-go/prese
by Groxx 8y ago
https://dave.cheney.net/practical-go/presentations/qcon-china.html#_never_start_a_goroutine_without_when_it_will_stop https://dave.cheney.net/practical-go/presentations/qcon-chin...
> 8.3. Never start a goroutine without [knowing] when it will stop.
100% agreed with the concept, but even the final example is flawed. That'll unblock and continue immediately after `close(stop)`, without being able to do two important things: it can't tell you when it's done shutting down, and it can't tell you if it encountered an error. Fixing this makes it even more complex.
Both of those are common and often necessary things to do - e.g. if you need to drain traffic, you need to wait until it's actually done before killing your process. Same goes for closing files (you might need to flush / sync first), OS resources that might survive your process, etc. This example will just kill your process as soon as everyone has been informed that it should stop, not when they're done.
---
Yeah it's a bit nitpicky, but it's even called out as "asking a http.Server to shut down is a little involved, so I’ve spun that logic out into a helper function". Except that the helper function can't ensure the involved server shutdown process actually shut down the server. That's a dangerous pattern to encourage, and it's an over-simplification that's absolutely rampant in go tutorials and code I encounter.
The workgroup lib he links doesn't (arguably?) have this flaw, but it has some strange / incomplete error handling details... E.g. if you Add 5 funcs, you'll only receive the error result of the first func to complete, not the first err or anything more predictable. If the first succeeds but all the rest fail, you'll see no error. Go's errgroup does "first non-nil error" at least, but you can still only get one: https://godoc.org/golang.org/x/sync/errgroup https://godoc.org/golang.org/x/sync/errgroup
- coder543 8y ago> 100% agreed with the concept, but even the final example is flawed. That'll unblock and continue immediately after `close(stop)`, without being able to do two important things: it can't tell you when it's done shutting down, and it can't tell you if it encountered an error. Fixing this makes it even more complex. The final example actually does both of these things already, but maybe you're confusing the purposes of some of the channels? The loop at the end of main will wait until it receives one (possibly nil) error value from every task that was launched. Upon receiving the first error, it will close the done channel, which will then cause any remaining tasks to run their Shutdown function, which will attempt to gracefully shutdown the server. The error of each shutdown function is then returned and printed if non-nil. There is no immediate termination, and all errors are communicated. It doesn't let the process die until every task has sent its error value. Obviously, there are two semantics not enforced at compile time: each task must send exactly one error value, and each task is responsible for its own shutdown. If a task sends more than one error, there will be early termination. If a task doesn't shut itself down properly, that is its own fault.
- Groxx 8y agoShutdown blocks until draining is complete, but ListenAndServe returns immediately when Shutdown is called: https://golang.org/pkg/net/http/#Server.Shutdown https://golang.org/pkg/net/http/#Server.Shutdown >When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS immediately return ErrServerClosed. Make sure the program doesn't exit and waits instead for Shutdown to return. So no, this writes to the "done" channel essentially immediately once `close(stop)` is called, and ends the process prematurely.