Handle worker Goexit cleanup and discard orphaned queued tasks
This commit is contained in:
36
pool.go
36
pool.go
@@ -84,8 +84,10 @@ func (p *Pool) Submit(task func()) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Close stops accepting tasks and waits for accepted tasks and workers to
|
||||
// finish. It is safe to call Close concurrently or more than once.
|
||||
// Close stops accepting tasks and waits for workers to exit. Tasks accepted
|
||||
// before Close normally finish before it returns. If a task calls
|
||||
// runtime.Goexit and that causes the last worker to exit, queued tasks are
|
||||
// discarded. It is safe to call Close concurrently or more than once.
|
||||
func (p *Pool) Close() {
|
||||
p.mu.Lock()
|
||||
p.closed = true
|
||||
@@ -116,12 +118,19 @@ func (p *Pool) startWorkerLocked(task func(), withTask bool) {
|
||||
}
|
||||
|
||||
func (p *Pool) run(w *worker) {
|
||||
normalExit := false
|
||||
defer func() {
|
||||
if !normalExit {
|
||||
p.workerExitedAbnormally()
|
||||
}
|
||||
}()
|
||||
for {
|
||||
task, ok := <-w.inbox
|
||||
if !ok {
|
||||
p.mu.Lock()
|
||||
p.total--
|
||||
p.workerExitedLocked()
|
||||
normalExit = true
|
||||
p.mu.Unlock()
|
||||
return
|
||||
}
|
||||
@@ -138,6 +147,7 @@ func (p *Pool) run(w *worker) {
|
||||
otherIdle := len(p.idle)
|
||||
if p.closed || (p.total > p.min && otherIdle > p.min/2) {
|
||||
p.total--
|
||||
normalExit = true
|
||||
if !p.closed {
|
||||
p.submitCond.Signal()
|
||||
} else {
|
||||
@@ -180,6 +190,28 @@ func (p *Pool) workerExitedLocked() {
|
||||
}
|
||||
}
|
||||
|
||||
// workerExitedAbnormally accounts for a worker terminated by runtime.Goexit.
|
||||
// If other workers remain, they retain the FIFO queue. The last worker clears
|
||||
// the queue because no worker remains to consume it; all of this is protected
|
||||
// by mu so Submit and Close observe the same state transition.
|
||||
func (p *Pool) workerExitedAbnormally() {
|
||||
p.mu.Lock()
|
||||
p.total--
|
||||
if p.total == 0 {
|
||||
for i := range p.queue {
|
||||
p.queue[i] = nil
|
||||
}
|
||||
p.queueHead = 0
|
||||
p.queueTail = 0
|
||||
p.queueLen = 0
|
||||
p.submitCond.Broadcast()
|
||||
} else {
|
||||
p.submitCond.Signal()
|
||||
}
|
||||
p.workerExitedLocked()
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
func runTask(task func()) {
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
|
||||
Reference in New Issue
Block a user