Repository: go-co-op/gocron
Pull request #233 (2021-09-10 20:03:13)

add IsRunning() to report the job's state
### What does this do?
add IsRunning() to report the job's state

### Which issue(s) does this PR fix/relate to?
<!--- Put `Resolves #XXX` here to auto-close the issue that your PR fixes (if such) --->
#225 

### List any changes that modify/break current functionality


### Have you included tests for your changes?
👍 

### Did you document any new/modified functionality?

- [x] Updated `example_test.go`
- [ ] Updated `README.md`

### Notes



Patch:
diff --git a/executor.go b/executor.go
index e1f46ee..88fd079 100644
--- a/executor.go
+++ b/executor.go
@@ -3,6 +3,7 @@ package gocron
 import (
 	"context"
 	"sync"
+	"sync/atomic"
 
 	"golang.org/x/sync/semaphore"
 )
@@ -77,7 +78,9 @@ func (e *executor) start() {
 
 				switch f.runConfig.mode {
 				case defaultMode:
+					atomic.AddInt64(f.runState, 1)
 					callJobFuncWithParams(f.function, f.parameters)
+					atomic.AddInt64(f.runState, -1)
 				case singletonMode:
 					_, _, _ = f.limiter.Do("main", func() (interface{}, error) {
 						select {
@@ -87,7 +90,9 @@ func (e *executor) start() {
 							return nil, nil
 						default:
 						}
+						atomic.AddInt64(f.runState, 1)
 						callJobFuncWithParams(f.function, f.parameters)
+						atomic.AddInt64(f.runState, -1)
 						return nil, nil
 					})
 				}
diff --git a/job.go b/job.go
index dac8a43..c8df85c 100644
--- a/job.go
+++ b/job.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"fmt"
 	"sync"
+	"sync/atomic"
 	"time"
 
 	"github.com/robfig/cron/v3"
@@ -39,6 +40,7 @@ type jobFunction struct {
 	limiter    *singleflight.Group // limits inflight runs of job to one
 	ctx        context.Context     // for cancellation
 	cancel     context.CancelFunc  // for cancellation
+	runState   *int64              // will be non-zero when jobs are running
 }
 
 type runConfig struct {
@@ -61,14 +63,16 @@ const (
 // newJob creates a new Job with the provided interval
 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,
+			ctx:      ctx,
+			cancel:   cancel,
+			runState: &zero,
 		},
 		tags:              []string{},
 		startsImmediately: startImmediately,
@@ -267,3 +271,8 @@ func (j *Job) stop() {
 	}
 	j.cancel()
 }
+
+// IsRunning reports whether any instances of the job function are currently running
+func (j *Job) IsRunning() bool {
+	return atomic.LoadInt64(j.runState) != 0
+}


Test patch:
diff --git a/example_test.go b/example_test.go
index 69dc684..c412566 100644
--- a/example_test.go
+++ b/example_test.go
@@ -22,6 +22,28 @@ func ExampleJob_Error() {
 	// the given time format is not supported
 }
 
+func ExampleJob_IsRunning() {
+	s := gocron.NewScheduler(time.UTC)
+	j, _ := s.Every(10).Seconds().Do(func() { time.Sleep(2 * time.Second) })
+
+	fmt.Println(j.IsRunning())
+
+	s.StartAsync()
+
+	time.Sleep(time.Second)
+	fmt.Println(j.IsRunning())
+
+	time.Sleep(time.Second)
+	s.Stop()
+
+	time.Sleep(1 * time.Second)
+	fmt.Println(j.IsRunning())
+	// Output:
+	// false
+	// true
+	// false
+}
+
 func ExampleJob_LastRun() {
 	s := gocron.NewScheduler(time.UTC)
 	job, _ := s.Every(1).Second().Do(task)
diff --git a/job_test.go b/job_test.go
index f80f016..b09c708 100644
--- a/job_test.go
+++ b/job_test.go
@@ -21,6 +21,23 @@ func TestTags(t *testing.T) {
 	assert.ElementsMatch(t, j.Tags(), []string{"tags", "tag", "some"})
 }
 
+func TestJob_IsRunning(t *testing.T) {
+	s := NewScheduler(time.UTC)
+	j, err := s.Every(10).Seconds().Do(func() { time.Sleep(2 * time.Second) })
+	require.NoError(t, err)
+	assert.False(t, j.IsRunning())
+
+	s.StartAsync()
+
+	time.Sleep(time.Second)
+	assert.True(t, j.IsRunning())
+
+	time.Sleep(time.Second)
+	s.Stop()
+
+	assert.False(t, j.IsRunning())
+}
+
 func TestGetScheduledTime(t *testing.T) {
 	t.Run("valid", func(t *testing.T) {
 		j, err := NewScheduler(time.UTC).Every(1).Day().At("10:30").Do(task)
@@ -127,12 +144,16 @@ func TestJob_CommonExports(t *testing.T) {
 	assert.True(t, j.NextRun().IsZero())
 
 	s.StartAsync()
+	s.Stop()
+
 	assert.False(t, j.NextRun().IsZero())
 
 	j.runCount = 5
 	assert.Equal(t, 5, j.RunCount())
 
 	lastRun := time.Now()
+	j.mu.Lock()
 	j.lastRun = lastRun
+	j.mu.Unlock()
 	assert.Equal(t, lastRun, j.LastRun())
 }