If at 13 in the else clause, if I first test for a non-nil wg.ops and close 
it if it's open, I think that stops that channel leak. 

On Sunday, 5 May 2019 18:32:14 UTC+2, Marcin Romaszewicz wrote:
>
> I've done quite a bit of MP programming over 20+ years now, and skimming 
> your code, I see a number of issues. There are probably a lot more that I 
> don't. In the latest go playground link
>
> Line14: if "if wg.started {" has a race condition, both on accessing the 
> variable, and logically, in that two goroutines can come into the function 
> and both go down the wg.started = false path. You can't do this without a 
> synchronization primitive.
>
> Line 17:  (wg.ops = make(chan int))  Because of line 14, you could create 
> more than one channel here, and different go routines will read a different 
> wg.ops, so your WaitGroup won't work. You also have a data race in 
> assigning wg.ops because wg.ops isn't an atomic type.
>
> Line 41: op, ok := <-wg.ops . You could be waiting forever because of 
> leaked channel in line 17
>
> It seems like your code works for a simple test, but if you really hammer 
> on this thing, it's going to fail.
>
> -- Marcin
>
>
> On Sun, May 5, 2019 at 8:33 AM Louki Sumirniy <louki.sumi...@gmail.com 
> <javascript:>> wrote:
>
>> just had to drop an update, I further improved it, now when it stops it 
>> resets itself and you can use Add again, I removed the unnecessary 
>> positive/negative checks and condensed the Add and Done function into an 
>> Add that can take a negative (tried to think of a better word but Modify 
>> and Change... just didn't quite fit)
>>
>> https://play.golang.org/p/cO3sV1w9-Re
>>
>> One could add back separate Add and Done functions, even conveniences 
>> that just inc 1 dec 1 for each but I don't see the point in that. It just 
>> simply allows you to make Wait block while the count is above zero, which 
>> is all you need to prevent goroutine leaks.
>>
>> On Sunday, 5 May 2019 17:18:52 UTC+2, Louki Sumirniy wrote:
>>>
>>> I figured out that the API of my design had a flaw separating starting 
>>> the goroutine and adding a new item, so as you can see in this code, I have 
>>> merged them and notice that there is basically no extraneous 'helper' 
>>> functions also:
>>>
>>> https://play.golang.org/p/hR1sTOAwkOm
>>>
>>> The flaw I made relates to API abuse - the contract of the library is 
>>> quite simply to concurrently keep track of the adjacent 'go' calls starting 
>>> goroutines, which doesn't apply until there is an increment. So you should 
>>> not be able to abuse this one the way you did the last.
>>>
>>> I'm pretty sure at somewhere between 50 and 100 worker routines the lack 
>>> of complex extra code makes this a better implementation. Probably the 
>>> original is fine for smaller worker pools.
>>>
>>> On Sunday, 5 May 2019 13:01:58 UTC+2, Jan Mercl wrote:
>>>>
>>>> On Sun, May 5, 2019 at 12:45 PM Louki Sumirniy 
>>>> <louki.sumi...@gmail.com> wrote: 
>>>> > 
>>>> > https://play.golang.org/p/5KwJQcTsUPg 
>>>> > 
>>>> > I fixed it. 
>>>>
>>>> Not really. You've introduced a data race. 
>>>>
>>>> jnml@4670:~/src/tmp> cat main.go 
>>>> package main 
>>>>
>>>> type WaitGroup struct { 
>>>>         workers int 
>>>>         ops     chan int 
>>>> } 
>>>>
>>>> func New() *WaitGroup { 
>>>>         wg := new(WaitGroup) 
>>>>         return wg 
>>>> } 
>>>>
>>>> func startWait(wg *WaitGroup) { 
>>>>         wg.ops = make(chan int) 
>>>>
>>>>         go func() { 
>>>>                 done := false 
>>>>                 for !done { 
>>>>                         select { 
>>>>                         case op := <-wg.ops: 
>>>>                                 wg.workers += op 
>>>>                                 if wg.workers < 1 { 
>>>>                                         done = true 
>>>>                                         close(wg.ops) 
>>>>                                 } 
>>>>                         } 
>>>>                 } 
>>>>         }() 
>>>> } 
>>>>
>>>> // Add adds a non-negative number 
>>>> func (wg *WaitGroup) Add(delta int) { 
>>>>         if delta < 0 { 
>>>>                 return 
>>>>         } 
>>>>         if wg.ops == nil { 
>>>>                 startWait(wg) 
>>>>         } 
>>>>         wg.ops <- delta 
>>>> } 
>>>>
>>>> // Done subtracts a non-negative value from the workers count 
>>>> func (wg *WaitGroup) Done(delta int) { 
>>>>         if delta < 0 { 
>>>>                 return 
>>>>         } 
>>>>         wg.ops <- -delta 
>>>> } 
>>>>
>>>> // Wait blocks until the waitgroup decrements to zero 
>>>> func (wg *WaitGroup) Wait() { 
>>>>         for { 
>>>>                 if wg.workers < 1 { 
>>>>                         break 
>>>>                 } 
>>>>                 op, ok := <-wg.ops 
>>>>                 if !ok { 
>>>>                         break 
>>>>                 } else { 
>>>>                         wg.ops <- op 
>>>>                 } 
>>>>         } 
>>>> } 
>>>>
>>>> var wg = New() 
>>>>
>>>> func main() { 
>>>>         for i := 0; i < 2; i++ { 
>>>>                 wg.Add(1) 
>>>>                 go f() 
>>>>         } 
>>>>         wg.Wait() 
>>>> } 
>>>>
>>>> func f() { 
>>>>         defer wg.Done(1) 
>>>>
>>>>         for i := 0; i < 2; i++ { 
>>>>                 wg.Add(1) 
>>>>                 go g() 
>>>>         } 
>>>> } 
>>>>
>>>> func g() { wg.Done(1) } 
>>>> jnml@4670:~/src/tmp> go run -race main.go 
>>>> ================== 
>>>> WARNING: DATA RACE 
>>>> Read at 0x00c00008a000 by main goroutine: 
>>>>   main.(*WaitGroup).Wait() 
>>>>       /home/jnml/src/tmp/main.go:53 +0x6f 
>>>>   main.main() 
>>>>       /home/jnml/src/tmp/main.go:72 +0x104 
>>>>
>>>> Previous write at 0x00c00008a000 by goroutine 5: 
>>>>   main.startWait.func1() 
>>>>       /home/jnml/src/tmp/main.go:21 +0xbb 
>>>>
>>>> Goroutine 5 (running) created at: 
>>>>   main.startWait() 
>>>>       /home/jnml/src/tmp/main.go:16 +0x9e 
>>>>   main.main() 
>>>>       /home/jnml/src/tmp/main.go:37 +0xdf 
>>>> ================== 
>>>> Found 1 data race(s) 
>>>> exit status 66 
>>>> jnml@4670:~/src/tmp> 
>>>>
>>> -- 
>> You received this message because you are subscribed to the Google Groups 
>> "golang-nuts" group.
>> To unsubscribe from this group and stop receiving emails from it, send an 
>> email to golan...@googlegroups.com <javascript:>.
>> For more options, visit https://groups.google.com/d/optout.
>>
>

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

Reply via email to