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.sumirniy.stal...@gmail.com> 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 golang-nuts+unsubscr...@googlegroups.com.
> 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