-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add WorkerTaskQueue#WaitForNoActiveTasks() for tests #284
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ package taskqueue | |
|
||
import ( | ||
"context" | ||
"sync/atomic" | ||
"time" | ||
|
||
"github.com/ipfs/go-peertaskqueue" | ||
|
@@ -32,7 +33,9 @@ type WorkerTaskQueue struct { | |
cancelFn func() | ||
peerTaskQueue *peertaskqueue.PeerTaskQueue | ||
workSignal chan struct{} | ||
noTaskSignal chan struct{} | ||
ticker *time.Ticker | ||
activeTasks int32 | ||
} | ||
|
||
// NewTaskQueue initializes a new queue | ||
|
@@ -43,6 +46,7 @@ func NewTaskQueue(ctx context.Context) *WorkerTaskQueue { | |
cancelFn: cancelFn, | ||
peerTaskQueue: peertaskqueue.New(), | ||
workSignal: make(chan struct{}, 1), | ||
noTaskSignal: make(chan struct{}, 1), | ||
ticker: time.NewTicker(thawSpeed), | ||
} | ||
} | ||
|
@@ -88,6 +92,16 @@ func (tq *WorkerTaskQueue) Shutdown() { | |
tq.cancelFn() | ||
} | ||
|
||
func (tq *WorkerTaskQueue) WaitForNoActiveTasks() { | ||
for atomic.LoadInt32(&tq.activeTasks) > 0 { | ||
select { | ||
case <-tq.ctx.Done(): | ||
return | ||
case <-tq.noTaskSignal: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. shouldn't this case Perhaps what you're trying to do is double-check that with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you apply this change, then you can also simplify the code by using an |
||
} | ||
} | ||
} | ||
|
||
func (tq *WorkerTaskQueue) worker(executor Executor) { | ||
targetWork := 1 | ||
for { | ||
|
@@ -104,7 +118,14 @@ func (tq *WorkerTaskQueue) worker(executor Executor) { | |
} | ||
} | ||
for _, task := range tasks { | ||
atomic.AddInt32(&tq.activeTasks, 1) | ||
terminate := executor.ExecuteTask(tq.ctx, pid, task) | ||
if atomic.AddInt32(&tq.activeTasks, -1) == 0 { | ||
select { | ||
case tq.noTaskSignal <- struct{}{}: | ||
default: | ||
} | ||
} | ||
if terminate { | ||
return | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My two cents, and this might be why the method isn't working as expected for you:
worker
only tries to signal via the channel when finishing a task results inactiveTasks
being 0WaitForNoActiveTasks
consumes that channel signalSo, for instance, if two
WaitForNoActiveTasks
calls come in and the worker performs one task, it will only send one signal to the channel, and only one of the twoWaitForNoActiveTasks
calls will grab that signal - the other call will potentially block forever.You want a "broadcast signal", which unfortunately doesn't have a primitive in Go right now - channels are one-to-one. You can use
close
as a way to broadcast to infinite receivers, but that closes the channel forever, not allowing its reuse.If you really only call
WaitForNoActiveTasks
once at a time, then your code isn't buggy, but I'd still argue it's prone to misuse :) So I would adapt its code to make it panic if called while it's already waiting.If you do want to support concurrent Wait calls, then we'd need a different approach. Happy to bounce some ideas if that's the case.