forked from mirror/ants
🎏Fix a bug where invokers get stuck in waiting idle workers
This commit is contained in:
parent
4f88d413b4
commit
af70ed0660
35
pool.go
35
pool.go
|
@ -28,8 +28,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pool accept the tasks from client,it limits the total
|
// Pool accept the tasks from client, it limits the total of goroutines to a given number by recycling goroutines.
|
||||||
// of goroutines to a given number by recycling goroutines.
|
|
||||||
type Pool struct {
|
type Pool struct {
|
||||||
// capacity of the pool.
|
// capacity of the pool.
|
||||||
capacity int32
|
capacity int32
|
||||||
|
@ -89,6 +88,13 @@ func (p *Pool) periodicallyPurge() {
|
||||||
}
|
}
|
||||||
p.workers = idleWorkers[:m]
|
p.workers = idleWorkers[:m]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There might be a situation that all workers have been cleaned up
|
||||||
|
// while some invokers still get stuck in "p.cond.Wait()",
|
||||||
|
// then it ought to wakes all those invokers.
|
||||||
|
if len(p.workers) == 0 {
|
||||||
|
p.cond.Broadcast()
|
||||||
|
}
|
||||||
p.lock.Unlock()
|
p.lock.Unlock()
|
||||||
|
|
||||||
// Notify obsolete workers to stop.
|
// Notify obsolete workers to stop.
|
||||||
|
@ -207,6 +213,17 @@ func (p *Pool) decRunning() {
|
||||||
// retrieveWorker returns a available worker to run the tasks.
|
// retrieveWorker returns a available worker to run the tasks.
|
||||||
func (p *Pool) retrieveWorker() *Worker {
|
func (p *Pool) retrieveWorker() *Worker {
|
||||||
var w *Worker
|
var w *Worker
|
||||||
|
spawnWorker := func() {
|
||||||
|
if cacheWorker := p.workerCache.Get(); cacheWorker != nil {
|
||||||
|
w = cacheWorker.(*Worker)
|
||||||
|
} else {
|
||||||
|
w = &Worker{
|
||||||
|
pool: p,
|
||||||
|
task: make(chan func(), workerChanCap),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.run()
|
||||||
|
}
|
||||||
|
|
||||||
p.lock.Lock()
|
p.lock.Lock()
|
||||||
idleWorkers := p.workers
|
idleWorkers := p.workers
|
||||||
|
@ -218,18 +235,14 @@ func (p *Pool) retrieveWorker() *Worker {
|
||||||
p.lock.Unlock()
|
p.lock.Unlock()
|
||||||
} else if p.Running() < p.Cap() {
|
} else if p.Running() < p.Cap() {
|
||||||
p.lock.Unlock()
|
p.lock.Unlock()
|
||||||
if cacheWorker := p.workerCache.Get(); cacheWorker != nil {
|
spawnWorker()
|
||||||
w = cacheWorker.(*Worker)
|
|
||||||
} else {
|
|
||||||
w = &Worker{
|
|
||||||
pool: p,
|
|
||||||
task: make(chan func(), workerChanCap),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.run()
|
|
||||||
} else {
|
} else {
|
||||||
Reentry:
|
Reentry:
|
||||||
p.cond.Wait()
|
p.cond.Wait()
|
||||||
|
if p.Running() == 0 {
|
||||||
|
spawnWorker()
|
||||||
|
return w
|
||||||
|
}
|
||||||
l := len(p.workers) - 1
|
l := len(p.workers) - 1
|
||||||
if l < 0 {
|
if l < 0 {
|
||||||
goto Reentry
|
goto Reentry
|
||||||
|
|
35
pool_func.go
35
pool_func.go
|
@ -28,8 +28,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PoolWithFunc accept the tasks from client,it limits the total
|
// PoolWithFunc accept the tasks from client, it limits the total of goroutines to a given number by recycling goroutines.
|
||||||
// of goroutines to a given number by recycling goroutines.
|
|
||||||
type PoolWithFunc struct {
|
type PoolWithFunc struct {
|
||||||
// capacity of the pool.
|
// capacity of the pool.
|
||||||
capacity int32
|
capacity int32
|
||||||
|
@ -92,6 +91,13 @@ func (p *PoolWithFunc) periodicallyPurge() {
|
||||||
}
|
}
|
||||||
p.workers = idleWorkers[:m]
|
p.workers = idleWorkers[:m]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// There might be a situation that all workers have been cleaned up
|
||||||
|
// while some invokers still get stuck in "p.cond.Wait()",
|
||||||
|
// then it ought to wakes all those invokers.
|
||||||
|
if len(p.workers) == 0 {
|
||||||
|
p.cond.Broadcast()
|
||||||
|
}
|
||||||
p.lock.Unlock()
|
p.lock.Unlock()
|
||||||
|
|
||||||
// Notify obsolete workers to stop.
|
// Notify obsolete workers to stop.
|
||||||
|
@ -212,6 +218,17 @@ func (p *PoolWithFunc) decRunning() {
|
||||||
// retrieveWorker returns a available worker to run the tasks.
|
// retrieveWorker returns a available worker to run the tasks.
|
||||||
func (p *PoolWithFunc) retrieveWorker() *WorkerWithFunc {
|
func (p *PoolWithFunc) retrieveWorker() *WorkerWithFunc {
|
||||||
var w *WorkerWithFunc
|
var w *WorkerWithFunc
|
||||||
|
spawnWorker := func() {
|
||||||
|
if cacheWorker := p.workerCache.Get(); cacheWorker != nil {
|
||||||
|
w = cacheWorker.(*WorkerWithFunc)
|
||||||
|
} else {
|
||||||
|
w = &WorkerWithFunc{
|
||||||
|
pool: p,
|
||||||
|
args: make(chan interface{}, workerChanCap),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.run()
|
||||||
|
}
|
||||||
|
|
||||||
p.lock.Lock()
|
p.lock.Lock()
|
||||||
idleWorkers := p.workers
|
idleWorkers := p.workers
|
||||||
|
@ -223,18 +240,14 @@ func (p *PoolWithFunc) retrieveWorker() *WorkerWithFunc {
|
||||||
p.lock.Unlock()
|
p.lock.Unlock()
|
||||||
} else if p.Running() < p.Cap() {
|
} else if p.Running() < p.Cap() {
|
||||||
p.lock.Unlock()
|
p.lock.Unlock()
|
||||||
if cacheWorker := p.workerCache.Get(); cacheWorker != nil {
|
spawnWorker()
|
||||||
w = cacheWorker.(*WorkerWithFunc)
|
|
||||||
} else {
|
|
||||||
w = &WorkerWithFunc{
|
|
||||||
pool: p,
|
|
||||||
args: make(chan interface{}, workerChanCap),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
w.run()
|
|
||||||
} else {
|
} else {
|
||||||
Reentry:
|
Reentry:
|
||||||
p.cond.Wait()
|
p.cond.Wait()
|
||||||
|
if p.Running() == 0 {
|
||||||
|
spawnWorker()
|
||||||
|
return w
|
||||||
|
}
|
||||||
l := len(p.workers) - 1
|
l := len(p.workers) - 1
|
||||||
if l < 0 {
|
if l < 0 {
|
||||||
goto Reentry
|
goto Reentry
|
||||||
|
|
Loading…
Reference in New Issue