From 2bed03bce65989a084dd67d3da1cf09a532ee727 Mon Sep 17 00:00:00 2001 From: Ethan Tang Date: Thu, 2 Aug 2018 11:38:09 +0800 Subject: [PATCH 1/2] Fixed issue of duplicate entryID when running schedule in multiple goroutines --- cron.go | 13 +++++++------ cron_test.go | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/cron.go b/cron.go index 9f4a1d66..fe32aa59 100644 --- a/cron.go +++ b/cron.go @@ -5,6 +5,7 @@ import ( "log" "runtime" "sort" + "sync/atomic" "time" ) @@ -19,7 +20,7 @@ type Cron struct { snapshot chan []*Entry running bool location *time.Location - nextID EntryID + nextID uint32 ErrorLog *log.Logger } @@ -36,7 +37,7 @@ type Schedule interface { } // EntryID identifies an entry within a Cron instance -type EntryID int +type EntryID uint32 // Entry consists of a schedule and the func to execute on that schedule. type Entry struct { @@ -144,10 +145,10 @@ func (c *Cron) AddJobN(name, spec string, cmd Job) (EntryID, error) { // Schedule adds a Job to the Cron to be run on the given schedule. func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID { - c.nextID++ + nextID := atomic.AddUint32(&c.nextID, 1) entry := &Entry{ - ID: c.nextID, + ID: EntryID(nextID), Schedule: schedule, Job: cmd, } @@ -163,10 +164,10 @@ func (c *Cron) Schedule(schedule Schedule, cmd Job) EntryID { // ScheduleN adds a Job to the Cron to be run on the given schedule. func (c *Cron) ScheduleN(name, spec string, schedule Schedule, cmd Job) EntryID { - c.nextID++ + nextID := atomic.AddUint32(&c.nextID, 1) entry := &Entry{ - ID: c.nextID, + ID: EntryID(nextID), Name: name, Spec: spec, Schedule: schedule, diff --git a/cron_test.go b/cron_test.go index 6a76fa5c..38f76511 100644 --- a/cron_test.go +++ b/cron_test.go @@ -161,6 +161,52 @@ func TestRemoveWhileRunning(t *testing.T) { } } +// Schedule within multiple go routines +func TestParallelSchedule(t *testing.T) { + var wg sync.WaitGroup + const numRoutine = 4 + + errSlice := make([]error, numRoutine) + idSlice := make([][]EntryID, numRoutine) + cron := New() + cron.Start() + for i := 0; i < numRoutine; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + const numTry = 100 + for iTry := 0; iTry < numTry; iTry++ { + sched, err := Parse("* * * * * ?") + if err != nil { + errSlice[i] = err + return + } + var job testJob + entryID := cron.Schedule(sched, job) + idSlice[i] = append(idSlice[i], entryID) + } + }(i) + } + wg.Wait() + + for i, err := range errSlice { + if err != nil { + t.Fatal("Error in #", i, ":", err) + } + } + + idMap := make(map[EntryID]bool) + for i, ids := range idSlice { + for j, id := range ids { + if idMap[id] { + t.Fatal("id duplicate in #", i, "No:", j, "id:", id) + } + idMap[id] = true + } + } + cron.Stop() +} + // Test timing with Entries. func TestSnapshotEntries(t *testing.T) { wg := &sync.WaitGroup{} From 8fdb00cdfc2204b4025c78d0aa50692d01fb9ad3 Mon Sep 17 00:00:00 2001 From: Ethan Tang Date: Wed, 27 Nov 2019 16:17:55 +0800 Subject: [PATCH 2/2] go module support --- .vscode/launch.json | 99 --------------------------------------------- go.mod | 3 ++ 2 files changed, 3 insertions(+), 99 deletions(-) delete mode 100644 .vscode/launch.json create mode 100644 go.mod diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 91227673..00000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,99 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Tests", - "type": "go", - "request": "launch", - "mode": "test", - "remotePath": "", - "port": 2345, - "host": "127.0.0.1", - "program": "${workspaceRoot}", - "env": {}, - "args": [], - "showLog": true - }, - { - "name": "Test (TestRange)", - "type": "go", - "request": "launch", - "mode": "test", - "remotePath": "", - "port": 2345, - "host": "127.0.0.1", - "program": "${workspaceRoot}", - "env": {}, - "args": ["^TestRange$"], - "showLog": true - }, - { - "name": "Test (TestField)", - "type": "go", - "request": "launch", - "mode": "test", - "remotePath": "", - "port": 2345, - "host": "127.0.0.1", - "program": "${workspaceRoot}", - "env": {}, - "args": ["^TestField$"], - "showLog": true - }, - { - "name": "Test (TestAll)", - "type": "go", - "request": "launch", - "mode": "test", - "remotePath": "", - "port": 2345, - "host": "127.0.0.1", - "program": "${workspaceRoot}", - "env": {}, - "args": ["^TestAll$"], - "showLog": true - }, - { - "name": "Test (TestBits)", - "type": "go", - "request": "launch", - "mode": "test", - "remotePath": "", - "port": 2345, - "host": "127.0.0.1", - "program": "${workspaceRoot}", - "env": {}, - "args": ["^TestBits$"], - "showLog": true - }, - { - "name": "Test (TestParse)", - "type": "go", - "request": "launch", - "mode": "test", - "remotePath": "", - "port": 2345, - "host": "127.0.0.1", - "program": "${workspaceRoot}", - "env": {}, - "args": ["^TestParse$"], - "showLog": true - }, - { - "name": "Test (TestStandardSpecSchedule)", - "type": "go", - "request": "launch", - "mode": "test", - "remotePath": "", - "port": 2345, - "host": "127.0.0.1", - "program": "${workspaceRoot}", - "env": {}, - "args": ["^TestStandardSpecSchedule$"], - "showLog": true - } - ] -} diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..77f29685 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/tangyanhan/cron + +go 1.13