Cron like digital signage scheduling

My first attempt to create a digital signage player in Google Chrome used a very simple scheduling strategy: define a list of content items to be played, along with the duration for each item, and play each item sequentially. However, this solution is not very flexible as it does not allow different programs during a day. Using a Cron-based scheduler is much more flexible.

Sequential scheduler

My first sequential scheduler used a very simple scheduling table with just two pieces of information: the item to be played, and the duration. The scheduler simply plays each item, keeping the playhead on an item for the duration assigned to that item. When it reaches the end, the playhead is put at the top:

Table 1: Sequential player

item duration playhead
Item A 1 min <-
Item B 3 min ..<-
Item C 2 min ….<-

Using a scheduler such as this, does not allow you to have different programs for different times of the day, unless you define a different schedule table for each time of day, AND program the player to use different schedule tables throught the day (which would only introduce complexity to the player).

Cron-based scheduler

Cron is a time-based job scheduler originally designed for Unix systems, which uses a very simple syntax for defining the periodicity of a job. For example, to make JobA run every minute, you would enter the following into the cron table (crontab):

1 * * * * JobA

Cron uses six fields for defining a schedule:

min hour day of month month day of week job
1 * * * * JobA

Each field can have an asterisk, that matches any possible value for that field; a range, e.g., 1–4, that match all values in the interval, a range and increment, e.g., 10–30/5 (every 5 values from 10 to 30), and lists of values, ranges, and ranges/increment (these are the main ones, at least.)

Cron-jobs and sequential playing

In a digital signage player, although a sequential scheduling is very limited, it is easy to understand and specify, and it maps well to our mental model of a player.

Using cron, although one can specify multiple jobs, they are independently scheduled by cron. This makes it hard to define a set of jobs that will play sequentially. It possible, but very time consuming and error-prone. For example to schedule three jobs corresponding to the items in Table 1, if we make a list of which item is being played on the first few minutes:

Start of Min Item
0 A
1 B
2 B
3 B
4 C
5 C
6 A
7 B
8 B
9 B
10 C
11 C

We can see that each item plays in 6 min intervals, so a crontab like the following would play the items in sequence:

min hour day of month month day of week job
0–59/6 * * * * ItemA
1–59/6 * * * * ItemB
4–59/6 * * * * ItemC

However, changing the duration of any item, means changing the whole cron table!

Cron-like scheduling

A better solution would be to combine both the sequential scheduler with a cron-scheduler.

Ideally, I should be able to schedule a set of items for a given period of time, using the same cron definition for all items, but make sure they will play sequentially.

To do this, we need to change how jobs are executed, i.e., we need to make our own cron scheduler.

This scheduler keeps a list of jobs to run, defined in the traditional cron job definition, with two differences. First, we add a duration field to the job specification which indicates for how long the job should run; second, we allow only one job to be executed at a time. The basic process to determine which job should run next is:

  1. Evaluate each job definition and determine the period of time from now to when the job should be executed (calculate the job timeout),
  2. Go through the list and pick the first job which has already timed out (it’s timeout is lower than a pre-defined threshold),
  3. Remove that job from its position in the list an put it at the end of list,
  4. Run that job,
  5. Wait for the duration specified in the running job
  6. Repeat

If no job has timed out, then just wait for a period of time equal to the minimum job timeout in the list and then restart the process.

With this scheduler, it’s easy to define a set of jobs that will run in sequence:

min hour day of month month day of week job duration
* * * * * ItemA 1 min
* * * * * ItemB 3 min
* * * * * ItemC 2 min

This would be equivalent to our first sequential scheduler example. Notice that, we specified all jobs to run every minute, but this will actually not be the case since our jobs are now executed one at a time. So, when itemA finishes, itemB will already have timed out one minute ago, but it does not matter. Because we execute the first timed out job of the list, and we move executed jobs to the end, we guarantee that all jobs have a chance to run.

However, the nice part is we now can easily define different jobs to run at different times of the day:

min hour day of month month day of week job duration
* 9–12 * * * ItemA 1 min
* 9–12 * * * ItemB 3 min
* 9–12 * * * ItemC 2 min
* 13–17 * * * ItemD 3 min
* 13–17 * * * ItemE 2 min
* 18–20 * * * ItemF 1 min
* 18–20 * * * ItemG 3 min
* 18–20 * * * ItemH 2 min

This defines three content programming for three periods of the day:

period jobs
9h to 12h ItemA, ItemB, ItemC
13h to 17h ItemD, ItemE
18h to 20h ItemG, ItemH

In each period, the jobs run in repeating sequence.

Implementing in Javascript

The cron-like scheduler is easy to implement in Javascript if you have a cron interpreter like cron.js by Stefan Liebenberg. cron.js is full cron scheduler, but we only need two functions from it: parsing cron job definitions, and telling us the timeout for each job. The implementation of the process is fairly easy so I’ll skip the code…

comments powered by Disqus