This package provides a version of sync.WaitGroup that allows calling Add and Wait in different goroutines.
sync.WaitGroup is designed to be used only in this kind of scenario:
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
// do something
wg.Done()
}()
}
wg.Wait()It is critical that Add and Wait are in the same goroutine. This is not well-enough documented behavior, but if you're interested, you can check:
The concwg.WaitGroup works similarly to the standard version, but has one, crucial change to the interface: Add returns a bool value.
Since Add and Wait methods could be called asynchronously, there is no way to guarantee that Add won't be called accidentally after the Wait.
So in some cases you must have a way to know if it is still safe to schedule a job after the call to Add.
That's why after Wait is called, Add always returns false. In this case you can't schedule a job and be sure that Wait will wait for it to finish.
Example use case:
type myWorker struct {
wg *concwg.WaitGroup
}
func newWorker() *myWorker {
return &myWorker{
wg: concwg.New(),
}
}
func (s *myWorker) HandleTask(name string) error {
if ok := s.wg.Add(1); !ok {
return errors.New("server is closing")
}
defer s.wg.Done()
// Simulate doing some work.
time.Sleep(time.Second)
fmt.Printf("task '%s' done", name)
return nil
}
func (s *myWorker) Stop() {
s.wg.Wait()
}
// This example shows the simple use case of the concwg.WaitGroup
func ExampleWaitGroup() {
worker := newWorker()
handler := func(w http.ResponseWriter, r *http.Request) {
err := worker.HandleTask(r.URL.Path)
if err != nil {
log.Printf("calling worer: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusAccepted)
}
// Start a server.
srv := httptest.NewServer(http.HandlerFunc(handler))
// Handle a request.
resp, err := http.DefaultClient.Get(srv.URL + "/foo")
if err != nil {
panic(err)
}
if resp.StatusCode != http.StatusAccepted {
panic("unexpected status code")
}
// Close the server
srv.Close()
// Stop the worker, wait for all tasks to be finished.
worker.Stop()
// You can safely exit the program now.
// Output:
// task '/foo' done
}