2021-01-17 21:46:25 +08:00
|
|
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
2018-07-27 15:48:49 +08:00
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
2019-02-02 16:18:25 +08:00
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-07-27 15:48:49 +08:00
|
|
|
|
2019-01-16 13:02:59 +08:00
|
|
|
// Package grpool implements a goroutine reusable pool.
|
2018-07-27 15:48:49 +08:00
|
|
|
package grpool
|
|
|
|
|
|
|
|
import (
|
2021-08-24 21:18:59 +08:00
|
|
|
"github.com/gogf/gf/errors/gcode"
|
2021-06-26 18:34:26 +08:00
|
|
|
"github.com/gogf/gf/errors/gerror"
|
2019-06-22 15:05:15 +08:00
|
|
|
|
2019-07-29 21:01:19 +08:00
|
|
|
"github.com/gogf/gf/container/glist"
|
|
|
|
"github.com/gogf/gf/container/gtype"
|
2018-07-27 15:48:49 +08:00
|
|
|
)
|
|
|
|
|
2019-06-01 15:11:32 +08:00
|
|
|
// Goroutine Pool
|
2018-07-27 15:48:49 +08:00
|
|
|
type Pool struct {
|
2019-06-19 09:06:52 +08:00
|
|
|
limit int // Max goroutine count limit.
|
|
|
|
count *gtype.Int // Current running goroutine count.
|
|
|
|
list *glist.List // Job list for asynchronous job adding purpose.
|
|
|
|
closed *gtype.Bool // Is pool closed or not.
|
2018-07-27 15:48:49 +08:00
|
|
|
}
|
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// Default goroutine pool.
|
|
|
|
var pool = New()
|
2018-07-27 15:48:49 +08:00
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// New creates and returns a new goroutine pool object.
|
2019-06-11 20:57:43 +08:00
|
|
|
// The parameter <limit> is used to limit the max goroutine count,
|
2019-06-01 19:34:03 +08:00
|
|
|
// which is not limited in default.
|
2019-06-19 09:06:52 +08:00
|
|
|
func New(limit ...int) *Pool {
|
|
|
|
p := &Pool{
|
|
|
|
limit: -1,
|
|
|
|
count: gtype.NewInt(),
|
2019-07-23 23:20:27 +08:00
|
|
|
list: glist.New(true),
|
2019-06-19 09:06:52 +08:00
|
|
|
closed: gtype.NewBool(),
|
|
|
|
}
|
|
|
|
if len(limit) > 0 && limit[0] > 0 {
|
|
|
|
p.limit = limit[0]
|
|
|
|
}
|
|
|
|
return p
|
2018-07-27 15:48:49 +08:00
|
|
|
}
|
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// Add pushes a new job to the pool using default goroutine pool.
|
|
|
|
// The job will be executed asynchronously.
|
2019-06-22 15:05:15 +08:00
|
|
|
func Add(f func()) error {
|
|
|
|
return pool.Add(f)
|
2018-07-27 15:48:49 +08:00
|
|
|
}
|
|
|
|
|
2020-08-12 23:53:05 +08:00
|
|
|
// AddWithRecover pushes a new job to the pool with specified recover function.
|
|
|
|
// The optional <recoverFunc> is called when any panic during executing of <userFunc>.
|
|
|
|
// If <recoverFunc> is not passed or given nil, it ignores the panic from <userFunc>.
|
|
|
|
// The job will be executed asynchronously.
|
|
|
|
func AddWithRecover(userFunc func(), recoverFunc ...func(err error)) error {
|
|
|
|
return pool.AddWithRecover(userFunc, recoverFunc...)
|
|
|
|
}
|
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// Size returns current goroutine count of default goroutine pool.
|
2018-07-27 15:48:49 +08:00
|
|
|
func Size() int {
|
2019-06-22 15:05:15 +08:00
|
|
|
return pool.Size()
|
2018-07-27 15:48:49 +08:00
|
|
|
}
|
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// Jobs returns current job count of default goroutine pool.
|
2018-07-27 15:48:49 +08:00
|
|
|
func Jobs() int {
|
2019-06-22 15:05:15 +08:00
|
|
|
return pool.Jobs()
|
2018-07-27 15:48:49 +08:00
|
|
|
}
|
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// Add pushes a new job to the pool.
|
|
|
|
// The job will be executed asynchronously.
|
2019-06-22 15:05:15 +08:00
|
|
|
func (p *Pool) Add(f func()) error {
|
|
|
|
for p.closed.Val() {
|
2021-08-24 21:18:59 +08:00
|
|
|
return gerror.NewCode(gcode.CodeInvalidOperation, "pool closed")
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2019-06-22 15:05:15 +08:00
|
|
|
p.list.PushFront(f)
|
2019-10-28 17:16:50 +08:00
|
|
|
// Check whether fork new goroutine or not.
|
2019-06-22 15:05:15 +08:00
|
|
|
var n int
|
|
|
|
for {
|
|
|
|
n = p.count.Val()
|
|
|
|
if p.limit != -1 && n >= p.limit {
|
2019-10-28 17:16:50 +08:00
|
|
|
// No need fork new goroutine.
|
2019-06-22 15:05:15 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if p.count.Cas(n, n+1) {
|
2019-10-28 17:16:50 +08:00
|
|
|
// Use CAS to guarantee atomicity.
|
2019-06-22 15:05:15 +08:00
|
|
|
break
|
|
|
|
}
|
2019-06-03 09:09:40 +08:00
|
|
|
}
|
|
|
|
p.fork()
|
2019-06-22 15:05:15 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-08-12 23:53:05 +08:00
|
|
|
// AddWithRecover pushes a new job to the pool with specified recover function.
|
|
|
|
// The optional <recoverFunc> is called when any panic during executing of <userFunc>.
|
|
|
|
// If <recoverFunc> is not passed or given nil, it ignores the panic from <userFunc>.
|
|
|
|
// The job will be executed asynchronously.
|
|
|
|
func (p *Pool) AddWithRecover(userFunc func(), recoverFunc ...func(err error)) error {
|
|
|
|
return p.Add(func() {
|
|
|
|
defer func() {
|
2021-07-20 23:02:02 +08:00
|
|
|
if exception := recover(); exception != nil {
|
2020-08-12 23:53:05 +08:00
|
|
|
if len(recoverFunc) > 0 && recoverFunc[0] != nil {
|
2021-07-20 23:02:02 +08:00
|
|
|
if err, ok := exception.(error); ok {
|
|
|
|
recoverFunc[0](err)
|
|
|
|
} else {
|
2021-08-24 21:18:59 +08:00
|
|
|
recoverFunc[0](gerror.NewCodef(gcode.CodeInternalError, `%v`, exception))
|
2021-07-20 23:02:02 +08:00
|
|
|
}
|
2020-08-12 23:53:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
userFunc()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-06-22 15:05:15 +08:00
|
|
|
// Cap returns the capacity of the pool.
|
|
|
|
// This capacity is defined when pool is created.
|
2019-10-28 17:16:50 +08:00
|
|
|
// It returns -1 if there's no limit.
|
2019-06-22 15:05:15 +08:00
|
|
|
func (p *Pool) Cap() int {
|
|
|
|
return p.limit
|
2018-07-27 15:48:49 +08:00
|
|
|
}
|
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// Size returns current goroutine count of the pool.
|
2018-07-27 15:48:49 +08:00
|
|
|
func (p *Pool) Size() int {
|
2019-06-19 09:06:52 +08:00
|
|
|
return p.count.Val()
|
2018-07-27 15:48:49 +08:00
|
|
|
}
|
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// Jobs returns current job count of the pool.
|
2019-10-28 17:16:50 +08:00
|
|
|
// Note that, it does not return worker/goroutine count but the job/task count.
|
2018-07-27 15:48:49 +08:00
|
|
|
func (p *Pool) Jobs() int {
|
2019-06-19 09:06:52 +08:00
|
|
|
return p.list.Size()
|
2018-07-27 15:48:49 +08:00
|
|
|
}
|
|
|
|
|
2019-10-28 17:16:50 +08:00
|
|
|
// fork creates a new goroutine worker.
|
|
|
|
// Note that the worker dies if the job function panics.
|
2019-05-31 22:54:57 +08:00
|
|
|
func (p *Pool) fork() {
|
2019-06-19 09:06:52 +08:00
|
|
|
go func() {
|
|
|
|
defer p.count.Add(-1)
|
2019-10-28 17:16:50 +08:00
|
|
|
|
|
|
|
var job interface{}
|
2019-06-19 09:06:52 +08:00
|
|
|
for !p.closed.Val() {
|
|
|
|
if job = p.list.PopBack(); job != nil {
|
|
|
|
job.(func())()
|
|
|
|
} else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
2018-07-27 15:48:49 +08:00
|
|
|
}
|
|
|
|
|
2019-06-22 15:05:15 +08:00
|
|
|
// IsClosed returns if pool is closed.
|
|
|
|
func (p *Pool) IsClosed() bool {
|
|
|
|
return p.closed.Val()
|
|
|
|
}
|
|
|
|
|
2019-06-01 19:34:03 +08:00
|
|
|
// Close closes the goroutine pool, which makes all goroutines exit.
|
2018-07-27 15:48:49 +08:00
|
|
|
func (p *Pool) Close() {
|
2019-06-01 15:11:32 +08:00
|
|
|
p.closed.Set(true)
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|