WaitGroup.Done vs Context.Done

2,094 views
Skip to first unread message

Brad Fitzpatrick

unread,
Sep 18, 2015, 8:23:11 PM9/18/15
to golang-dev
It's kinda unfortunate that sync.WaitGroup.Done() is a method that does an action:

http://golang.org/pkg/sync/#WaitGroup.Done - "Done decrements the WaitGroup counter."

But x/net.Context.Done is a read-only accessor only:

https://godoc.org/golang.org/x/net/context#Context - "Done returns a channel that's closed when work done on behalf of this context should be canceled.  Done may return nil if this context can  never be canceled.  Successive calls to Done return the same value."

I bring this up because I find myself writing a little sync.WaitGroup.Wait -> channel adapter, so I can select on a WaitGroup, along with some other things.

It would be nice if sync.WaitGroup had a method to return a close-only notification channel (<-chan struct{}) when Wait is complete.

But there's no great name for it.  (Done would be the obvious choice, to mirror x/net.Context, but WaitGroup.Done has just about the opposite meaning...)


(musing aloud)

Caleb Spare

unread,
Sep 18, 2015, 8:47:03 PM9/18/15
to Brad Fitzpatrick, golang-dev
> It would be nice if sync.WaitGroup had a method to return a close-only
> notification channel (<-chan struct{}) when Wait is complete.

Agreed. I quite often find myself making a goroutine+channel just so
that I can select on wg.Wait.

> But there's no great name for it. (Done would be the obvious choice, to mirror x/net.Context, but WaitGroup.Done has just about the opposite meaning...)

C? WaitC? Not that great, I know :\

-Caleb

David Symonds

unread,
Sep 19, 2015, 2:03:51 AM9/19/15
to Brad Fitzpatrick, golang-dev
One difficulty is that a WaitGroup can become "done" more than once.
It's a semaphore that starts "done", and becomes "done" again every
time its count hits zero. Is such a method that you propose going to
return the same channel each time (which is what Context.Done does)?
If so, it can't be used if the WaitGroup has an Add after it is
"done". If not, getting that channel is potentially racy, since you
might end up waiting on it being done from a previous cycle.

Brad Fitzpatrick

unread,
Sep 19, 2015, 7:55:44 PM9/19/15
to David Symonds, golang-dev
Yeah. But you could imagine a method on WaitGroup to return a channel that was closed when the _current_ state of the WaitGroup transitioned to 0. But if the WaitGroup again went positive, the channel method would return a different channel.

So proper usage would be to get the channel before starting anything that might make it go back to zero:

   var wg sync.WaitGroup
   wg.Add(1)
   done := wg.DoneChan() // before the Go
   go func() { defer wg.Done(); .... }()

   select {
   case <-done:
      ....
   case <-other:
      ....
   }

This is easy enough to do on your own but is just tedious to do every time, and distract the reader of the code from seeing the more interesting code nearby.

I can just move this to a "syncutil" package, though.


Yongjian Xu

unread,
Sep 20, 2015, 1:50:27 PM9/20/15
to Brad Fitzpatrick, David Symonds, golang-dev
Is the whole idea to allow WaitGroup to participate select loop?

How about

func (wg *WaitGroup) Finish() <-chan struct{} {
     ch := make(chan struct{})
     go func() { wg.Wait(); close(ch)}
     return ch
}


--
You received this message because you are subscribed to the Google Groups "golang-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-dev+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Burcu Dogan

unread,
Sep 21, 2015, 2:21:51 PM9/21/15
to Brad Fitzpatrick, David Symonds, golang-dev
On Sat, Sep 19, 2015 at 4:55 PM, Brad Fitzpatrick <brad...@golang.org> wrote:
But if the WaitGroup again went positive, the channel method would return a different channel.

Would this constraint require me to access to the DoneChan() before each Add? How will I be notified that the done channel is now different so I can consume the right channel. This limitation is hard to predict and document well. I fear that it will be error prone as it is.

WaitGroup has no behavioral differentiation in Wait wether it achieved for the first time or not. So why would we switch to a different channel after each time Wait is achieved? Could you provide more context?
 
So proper usage would be to get the channel before starting anything that might make it go back to zero:

   var wg sync.WaitGroup
   wg.Add(1)
   done := wg.DoneChan() // before the Go
   go func() { defer wg.Done(); .... }()

   select {
   case <-done:
      ....
   case <-other:
      ....
   }

This is easy enough to do on your own but is just tedious to do every time, and distract the reader of the code from seeing the more interesting code nearby.

I can just move this to a "syncutil" package, though.



On Fri, Sep 18, 2015 at 11:03 PM, David Symonds <dsym...@golang.org> wrote:
One difficulty is that a WaitGroup can become "done" more than once.
It's a semaphore that starts "done", and becomes "done" again every
time its count hits zero. Is such a method that you propose going to
return the same channel each time (which is what Context.Done does)?
If so, it can't be used if the WaitGroup has an Add after it is
"done". If not, getting that channel is potentially racy, since you
might end up waiting on it being done from a previous cycle.

Brad Fitzpatrick

unread,
Sep 21, 2015, 2:25:58 PM9/21/15
to Burcu Dogan, David Symonds, golang-dev
I'm not actually proposing anything.

Mostly I was sad about the Done name.

I (and others) can build the helpers we need elsewhere.


Sameer Ajmani

unread,
Sep 25, 2015, 2:59:51 PM9/25/15
to Yongjian Xu, Brad Fitzpatrick, David Symonds, golang-dev
I like the idea of just having people write these three lines themselves.  We do this already in pipelines when we start several goroutines to send on a channel, then start one more goroutine that does wg.Wait(); close(c), so that downstream consumers can range on the channel.  It seems like users can just do the same thing when they need a channel in a select.

Yongjian Xu

unread,
Sep 25, 2015, 7:55:41 PM9/25/15
to Sameer Ajmani, Brad Fitzpatrick, David Symonds, golang-dev
yeah, I agree. It seems trivial enough.
Reply all
Reply to author
Forward
0 new messages