func (j *Job) SingletonMode() {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.runConfig.mode = singletonMode
	j.jobFunction.limiter = &singleflight.Group{}
}
SingletonMode prevents a new job from starting if the prior job has not yet
completed it's run
Note: If a job is added to a running scheduler and this method is then used
you may see the job run overrun itself as job is scheduled immediately
by default upon being added to the scheduler. It is recommended to use the
SingletonMode() func on the scheduler chain when scheduling the job.
package gocron
import (
	"context"
	"fmt"
	"sync"
	"sync/atomic"
	"time"
	"github.com/robfig/cron/v3"
	"golang.org/x/sync/singleflight"
)
type Job struct {
	mu sync.RWMutex
	jobFunction
	interval          int             
	duration          time.Duration   
	unit              schedulingUnit  
	startsImmediately bool            
	atTime            time.Duration   
	startAtTime       time.Time       
	error             error           
	lastRun           time.Time       
	nextRun           time.Time       
	scheduledWeekdays []time.Weekday  
	daysOfTheMonth    []int           
	tags              []string        
	runCount          int             
	timer             *time.Timer     
	cronSchedule      cron.Schedule   
}
type jobFunction struct {
	function   interface{}          
	parameters []interface{}        
	name       string               
	runConfig  runConfig            
	limiter    *singleflight.Group  
	ctx        context.Context      
	cancel     context.CancelFunc   
	runState   *int64               
}
type runConfig struct {
	finiteRuns bool
	maxRuns    int
	mode       mode
}
type mode int8
const (
	defaultMode mode = iota
	singletonMode
)
func newJob(interval int, startImmediately bool) *Job {
	ctx, cancel := context.WithCancel(context.Background())
	var zero int64
	return &Job{
		interval: interval,
		unit:     seconds,
		lastRun:  time.Time{},
		nextRun:  time.Time{},
		jobFunction: jobFunction{
			ctx:      ctx,
			cancel:   cancel,
			runState: &zero,
		},
		tags:              []string{},
		startsImmediately: startImmediately,
	}
}
func (j *Job) neverRan() bool {
	return j.lastRun.IsZero()
}
func (j *Job) getStartsImmediately() bool {
	return j.startsImmediately
}
func (j *Job) setStartsImmediately(b bool) {
	j.startsImmediately = b
}
func (j *Job) setTimer(t *time.Timer) {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.timer = t
}
func (j *Job) getAtTime() time.Duration {
	return j.atTime
}
func (j *Job) setAtTime(t time.Duration) {
	j.atTime = t
}
func (j *Job) getStartAtTime() time.Time {
	return j.startAtTime
}
func (j *Job) setStartAtTime(t time.Time) {
	j.startAtTime = t
}
func (j *Job) getUnit() schedulingUnit {
	j.mu.RLock()
	defer j.mu.RUnlock()
	return j.unit
}
func (j *Job) setUnit(t schedulingUnit) {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.unit = t
}
func (j *Job) getDuration() time.Duration {
	j.mu.RLock()
	defer j.mu.RUnlock()
	return j.duration
}
func (j *Job) setDuration(t time.Duration) {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.duration = t
}
func (j *Job) Error() error {
	return j.error
}
func (j *Job) Tag(tags ...string) {
	j.tags = append(j.tags, tags...)
}
func (j *Job) Untag(t string) {
	var newTags []string
	for _, tag := range j.tags {
		if t != tag {
			newTags = append(newTags, tag)
		}
	}
	j.tags = newTags
}
func (j *Job) Tags() []string {
	return j.tags
}
func (j *Job) ScheduledTime() time.Time {
	j.mu.RLock()
	defer j.mu.RUnlock()
	return j.nextRun
}
func (j *Job) ScheduledAtTime() string {
	return fmt.Sprintf("%d:%d", j.atTime/time.Hour, (j.atTime%time.Hour)/time.Minute)
}
func (j *Job) Weekday() (time.Weekday, error) {
	if len(j.scheduledWeekdays) == 0 {
		return time.Sunday, ErrNotScheduledWeekday
	}
	return j.scheduledWeekdays[0], nil
}
func (j *Job) Weekdays() []time.Weekday {
	if len(j.scheduledWeekdays) == 0 {
		return []time.Weekday{time.Sunday}
	}
	return j.scheduledWeekdays
}
func (j *Job) LimitRunsTo(n int) {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.runConfig.finiteRuns = true
	j.runConfig.maxRuns = n
}