You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
111 lines
2.4 KiB
111 lines
2.4 KiB
package gocron |
|
|
|
import ( |
|
"context" |
|
"sync" |
|
|
|
"golang.org/x/sync/semaphore" |
|
) |
|
|
|
const ( |
|
// RescheduleMode - the default is that if a limit on maximum |
|
// concurrent jobs is set and the limit is reached, a job will |
|
// skip it's run and try again on the next occurrence in the schedule |
|
RescheduleMode limitMode = iota |
|
|
|
// WaitMode - if a limit on maximum concurrent jobs is set |
|
// and the limit is reached, a job will wait to try and run |
|
// until a spot in the limit is freed up. |
|
// |
|
// Note: this mode can produce unpredictable results as |
|
// job execution order isn't guaranteed. For example, a job that |
|
// executes frequently may pile up in the wait queue and be executed |
|
// many times back to back when the queue opens. |
|
WaitMode |
|
) |
|
|
|
type executor struct { |
|
jobFunctions chan jobFunction |
|
stopCh chan struct{} |
|
limitMode limitMode |
|
maxRunningJobs *semaphore.Weighted |
|
} |
|
|
|
func newExecutor() executor { |
|
return executor{ |
|
jobFunctions: make(chan jobFunction, 1), |
|
stopCh: make(chan struct{}, 1), |
|
} |
|
} |
|
|
|
func (e *executor) start() { |
|
stopCtx, cancel := context.WithCancel(context.Background()) |
|
runningJobsWg := sync.WaitGroup{} |
|
|
|
for { |
|
select { |
|
case f := <-e.jobFunctions: |
|
runningJobsWg.Add(1) |
|
go func() { |
|
defer runningJobsWg.Done() |
|
|
|
if e.maxRunningJobs != nil { |
|
if !e.maxRunningJobs.TryAcquire(1) { |
|
|
|
switch e.limitMode { |
|
case RescheduleMode: |
|
return |
|
case WaitMode: |
|
for { |
|
select { |
|
case <-stopCtx.Done(): |
|
return |
|
case <-f.ctx.Done(): |
|
return |
|
default: |
|
} |
|
|
|
if e.maxRunningJobs.TryAcquire(1) { |
|
break |
|
} |
|
} |
|
} |
|
} |
|
|
|
defer e.maxRunningJobs.Release(1) |
|
} |
|
|
|
switch f.runConfig.mode { |
|
case defaultMode: |
|
f.incrementRunState() |
|
callJobFuncWithParams(f.function, f.parameters) |
|
f.decrementRunState() |
|
case singletonMode: |
|
_, _, _ = f.limiter.Do("main", func() (interface{}, error) { |
|
select { |
|
case <-stopCtx.Done(): |
|
return nil, nil |
|
case <-f.ctx.Done(): |
|
return nil, nil |
|
default: |
|
} |
|
f.incrementRunState() |
|
callJobFuncWithParams(f.function, f.parameters) |
|
f.decrementRunState() |
|
return nil, nil |
|
}) |
|
} |
|
}() |
|
case <-e.stopCh: |
|
cancel() |
|
runningJobsWg.Wait() |
|
e.stopCh <- struct{}{} |
|
return |
|
} |
|
} |
|
} |
|
|
|
func (e *executor) stop() { |
|
e.stopCh <- struct{}{} |
|
<-e.stopCh |
|
}
|
|
|