(initialWork *contextualItem[T])
| 93 | } |
| 94 | |
| 95 | func (wp *workerPool[T]) workerBody(initialWork *contextualItem[T]) func(context.Context) error { |
| 96 | worker := func(ctx context.Context) error { |
| 97 | var timeout bool |
| 98 | defer func() { |
| 99 | if timeout { |
| 100 | return |
| 101 | } |
| 102 | atomic.AddInt32(&wp.workers, -1) |
| 103 | select { |
| 104 | case <-ctx.Done(): |
| 105 | case <-wp.Done(): |
| 106 | default: |
| 107 | // Since the task did not finish due to a context cancellation |
| 108 | // or a timeout, the worker body must have panicked. As such |
| 109 | // we attempt to spawn a replacement worker in order to avoid |
| 110 | // stalling the queue indefinitely. |
| 111 | wp.spawnWorker(nil) |
| 112 | } |
| 113 | }() |
| 114 | |
| 115 | defer wp.wg.Done() |
| 116 | defer registerWorkerStopped(wp.Name) |
| 117 | |
| 118 | registerWorkerIdle(wp.Name) |
| 119 | defer registerWorkerBusy(wp.Name) |
| 120 | |
| 121 | if initialWork != nil { |
| 122 | wp.handle(initialWork) |
| 123 | } |
| 124 | |
| 125 | for { |
| 126 | select { |
| 127 | case <-wp.Done(): |
| 128 | return wp.Err() |
| 129 | |
| 130 | case <-ctx.Done(): |
| 131 | return ctx.Err() |
| 132 | |
| 133 | case <-time.After(wp.WorkerIdleTimeout): |
| 134 | if decrementIfGreaterThan(&wp.workers, int32(wp.MinWorkers)) { |
| 135 | timeout = true |
| 136 | return nil |
| 137 | } |
| 138 | |
| 139 | case item := <-wp.fastQueue: |
| 140 | wp.handle(item) |
| 141 | |
| 142 | case item := <-wp.mainQueue: |
| 143 | registerWorkDequeued(wp.Name, item.queuedAt) |
| 144 | wp.handle(item) |
| 145 | } |
| 146 | } |
| 147 | } |
| 148 | return worker |
| 149 | } |
| 150 | |
| 151 | // spawnWorker spawns a worker task, if a worker slot is available. |
| 152 | func (wp *workerPool[T]) spawnWorker(initialWork *contextualItem[T]) bool { |
no test coverage detected