Hi Egon, I appreciate the effort you've (and everyone has) put in thus-far!
Correct me if I'm wrong, but I think you're pointing out the race condition John pointed. That's a tradeoff we've accepted so we don't need to reimplement much of http.Serve so that we can add a waitgroup Add or Rlock before the goroutine is spawned (among other sync requirements to ensure no new connections are accepted, etc). Thanks again, Evan On Tuesday, 13 September 2016 13:11:17 UTC-7, Egon wrote: > > On Tuesday, 13 September 2016 22:52:27 UTC+3, Evan Digby wrote: >> >> Hi Egon, >> >> This is essentially the strategy I'm taking; however, I am hoping to >> avoid the "Sleep and Pray" method. Reliable in practice, but not >> guaranteed. Also in a CI of thousands of tests, adding time arbitrarily can >> extend out the time it takes to test quite a bit. >> >> That said, if a sleep is the only way, a sleep is the only way. I hope it >> isn't! >> > > You would need to modify the handler to make it work other ways. > Alternatively you need to write a code-rewriter for injecting code into > the handler. > > The only reliable concurrent software I've seen is one that is easy to > understand, that is backed by a proof (either formal or informal). > > I took a deeper look into the taskHandler code -- and it doesn't finish > all the tasks before everything: > > I.e. > > R1: Line 24 // go func is delayed for some reason > R2: Line 35 // close is called in main > R2: Line 36 > R2: Line 37 > R1: Line 25 // go func is started > R1: Line 28 // task is executed > R1: Line 26 > > ATM. too tired to implement a proper solution, will take a look at it > tomorrow. > > + Egon > > >> Thanks! >> >> Evan >> >> On Tuesday, 13 September 2016 12:47:40 UTC-7, Egon wrote: >>> >>> counter := intr64(N) >>> release := make(chan struct{}) >>> >>> ... >>> for i := 0; i < N; i ++ { >>> h.Handle(func() { >>> <-release >>> atomic.AddInt64(&counter, -1) >>> }) >>> } >>> ... >>> >>> go func(){ >>> time.Sleep(time.Millisecond) // so we would certainly hit h.Close, >>> before we continue >>> for i := 0; i < N; i++ { release <- struct{}{}; >>> time.Sleep(time.Millisecond) } >>> // alternatively use runtime.Gosched() instead of Sleep >>> }() >>> >>> h.Close() >>> >>> if atomic.LoadInt64(&counter) > 0 { >>> // fail >>> } >>> >>> It's not completely fool-proof, but should work well enough in practice. >>> >>> On Tuesday, 13 September 2016 21:56:08 UTC+3, Evan Digby wrote: >>>> >>>> Has anyone come across a good way, non-racy way to ensure that N tasks >>>> are guaranteed to be completed after a function is called? Essentially I >>>> have a “Close” function that must be guaranteed to block until all tasks >>>> are finished. Achieving this was pretty simple: wrap each task in an >>>> RLock, >>>> and then a Lock on close. >>>> >>>> Example: https://play.golang.org/p/7lhBPUhkUE >>>> >>>> Now I want to write a solid test to guarantee Close will meet that >>>> requirement of all tasks must finish first for posterity. In that example, >>>> try commenting out the RLock/RUnlock on lines 25/26. You'll see that it no >>>> longer outputs many, if any, lines. I'm trying to prevent that from >>>> happening in the future by some cowboy refactor! >>>> >>> >>>> All of the ways I can come up with involve Sleeping or launching more >>>> tasks than I _think_ can be finished in time--obviously not good! >>>> >>>> I feel like I must be missing some obvious way to test this and I'll >>>> end up feeling silly once someone replies with the solution. I'm okay with >>>> that! >>>> >>> -- 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.