Skip to content

Cron:Entries() may return nil when called concurrently #97

@mrwonko

Description

@mrwonko

This is a theoretical issue I have not yet encountered in practice (but think could actually happen) that I came across while thinking about the source code, in particular these bits:

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil // A
		x := <-c.snapshot // B
		return x
	}
	return c.entrySnapshot()
}
		case <-c.snapshot: // C
			c.snapshot <- c.entrySnapshot() // D

Let's say Goroutine 1 is executing the run() loop containing C and D. Now Goroutine 2 executes Entries() until A, where it blocks. Goroutine 1 executes C, but before it gets a chance to execute D the scheduler decides to preempt it in favour of Goroutine 3, which executes Entries() until A, where it puts nil into the snapshot channel, leading Goroutine 2 to receive nil in B!

Since entrySnapshot() is a non-trivial function I feel like there's a good chance the Goroutine might get preempted during its execution.

The issue lies in using the same channel for communication in both directions. You could instead have a channel of channels over which you send a result channel created in Entries().

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions