github.com/gogf/gf/v2@v2.7.4/os/grpool/grpool_pool.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package grpool 8 9 import ( 10 "context" 11 12 "github.com/gogf/gf/v2/errors/gcode" 13 "github.com/gogf/gf/v2/errors/gerror" 14 ) 15 16 // Add pushes a new job to the pool. 17 // The job will be executed asynchronously. 18 func (p *Pool) Add(ctx context.Context, f Func) error { 19 for p.closed.Val() { 20 return gerror.NewCode( 21 gcode.CodeInvalidOperation, 22 "goroutine defaultPool is already closed", 23 ) 24 } 25 p.list.PushFront(&localPoolItem{ 26 Ctx: ctx, 27 Func: f, 28 }) 29 // Check and fork new worker. 30 p.checkAndForkNewGoroutineWorker() 31 return nil 32 } 33 34 // AddWithRecover pushes a new job to the pool with specified recover function. 35 // 36 // The optional `recoverFunc` is called when any panic during executing of `userFunc`. 37 // If `recoverFunc` is not passed or given nil, it ignores the panic from `userFunc`. 38 // The job will be executed asynchronously. 39 func (p *Pool) AddWithRecover(ctx context.Context, userFunc Func, recoverFunc RecoverFunc) error { 40 return p.Add(ctx, func(ctx context.Context) { 41 defer func() { 42 if exception := recover(); exception != nil { 43 if recoverFunc != nil { 44 if v, ok := exception.(error); ok && gerror.HasStack(v) { 45 recoverFunc(ctx, v) 46 } else { 47 recoverFunc(ctx, gerror.NewCodef(gcode.CodeInternalPanic, "%+v", exception)) 48 } 49 } 50 } 51 }() 52 userFunc(ctx) 53 }) 54 } 55 56 // Cap returns the capacity of the pool. 57 // This capacity is defined when pool is created. 58 // It returns -1 if there's no limit. 59 func (p *Pool) Cap() int { 60 return p.limit 61 } 62 63 // Size returns current goroutine count of the pool. 64 func (p *Pool) Size() int { 65 return p.count.Val() 66 } 67 68 // Jobs returns current job count of the pool. 69 // Note that, it does not return worker/goroutine count but the job/task count. 70 func (p *Pool) Jobs() int { 71 return p.list.Size() 72 } 73 74 // IsClosed returns if pool is closed. 75 func (p *Pool) IsClosed() bool { 76 return p.closed.Val() 77 } 78 79 // Close closes the goroutine pool, which makes all goroutines exit. 80 func (p *Pool) Close() { 81 p.closed.Set(true) 82 } 83 84 // checkAndForkNewGoroutineWorker checks and creates a new goroutine worker. 85 // Note that the worker dies if the job function panics and the job has no recover handling. 86 func (p *Pool) checkAndForkNewGoroutineWorker() { 87 // Check whether fork new goroutine or not. 88 var n int 89 for { 90 n = p.count.Val() 91 if p.limit != -1 && n >= p.limit { 92 // No need fork new goroutine. 93 return 94 } 95 if p.count.Cas(n, n+1) { 96 // Use CAS to guarantee atomicity. 97 break 98 } 99 } 100 101 // Create job function in goroutine. 102 go p.asynchronousWorker() 103 } 104 105 func (p *Pool) asynchronousWorker() { 106 defer p.count.Add(-1) 107 108 var ( 109 listItem interface{} 110 poolItem *localPoolItem 111 ) 112 // Harding working, one by one, job never empty, worker never die. 113 for !p.closed.Val() { 114 listItem = p.list.PopBack() 115 if listItem == nil { 116 return 117 } 118 poolItem = listItem.(*localPoolItem) 119 poolItem.Func(poolItem.Ctx) 120 } 121 }