timer
Timer module that supports one-time timer, periodic timer and Cron-compatible syntax.
timer
timer.cancel(timer) | Rearms a canceled or expired timer. |
timer.cron(cron, hook, varargs) | Creates a new timer object, which will elapse at every date described by
the cron parameter. |
timer.cron_nextevent(timer) | Converts a CRON string into the next date at which the corresponding event shall be triggered. |
timer.latencyExec(func, latency) | Calls a function within a maximum delay, while trying to avoid multiple calls. |
timer.nbofdaysinmonth(date) | Retrieves the number of days in the month including a given date. |
timer.new(expiry, hook, varargs) | Setups and returns a new timer object. |
timer.nextval(p, curr, period, minval) | Converts a CRON element, already extracted from the string, into a number representing the next occurrence. |
timer.once(delay, hook, varargs) | Creates a new timer object, which will elapse after delay seconds. |
timer.periodic(period, hook, varargs) | Creates a new timer object, which will elapse every period seconds. |
timer
timer.cancel(timer)
timer
:
as returned by #timer.new function.
timer.cron(cron, hook, varargs)
cron
parameter.
Cron entries are strings of 5 elements, separated by single spaces.
.---------------- minute (0 - 59)
| .------------- hour (0 - 23)
| | .---------- day of month (1 - 31)
| | | .------- month (1 - 12) OR jan,feb,mar,apr ...
| | | | .---- day of week (0 - 7) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
| | | | | .- optional random jitter
| | | | | |
* * * * * *
The corresponding event is triggered every minute at most, if and only if
every field matches the current date. Asterisks ("*"
) can be used as
placeholderst in unused fields. For instance, "30 8 1 * * *"
describes an
event which takes place at 8:30AM every first day of the month.
Beside numbers, some operators are allowed in fields:
As mentionned, the asterisk ('*'
) placeholder represents all possible
values for a field. For example, an asterisk in the hour time field
would be equivalent to 'every hour' (subject to matching other specified
fields).
The comma (','
) is a binary operator which specifies a list of possible
values, e.g. "1,3,4,7,8"
(there must be no space around commas)
The dash ('-'
) is a binary operator which specifies a range of values.
For instance, "1-6"
is equivalent to "1,2,3,4,5,6"
The slash ('/'
) is a binary operator, called "step", which allows to skip
a given number of values. For instance, "*/3"
in the hour field is
equivalent to "0,3,6,9,12,15,18,21"
.
For instance, "0 * * * *"
denotes an event at every hour (whenever the
number of minutes reaches 0), but "0 */6 * * *"
denotes only the hours
divisible by 6 (i.e. 0:00, 6:00, 12:00 and 18:00). Beware that the formal
meaning of the '/'
operator is "whenever the modulo is 0".
As a consequence, "*/61"
in the minutes slot will be triggered when the
number of minutes reaches 0, because 0/61==0.
Cron also accepts some aliases for common periodicities. "@hourly"
,
"@daily", "@weekly"
, "@monthly"and "@annually"
represent the
corresponding expected periodic events.
timer.cron()
supports an addition to the cron standard: if a sixth number
element is appended to the string, this number is taken as a random jitter,
in seconds. For instance, if a jitter of 120 is given, a random shifting
between 0 and 120 is chosen when the timer is created; every triggerring of
the event will be time-shifted by this amount of time.
The jitter doesn't change during the lifetime of a given timer:
if a minutely test "* * * * * 59"
is triggered at 12:03:28, it will be
triggered next at 12:04:28, 12:05:28 etc.: jitters don't modify the time
which elapses between the triggerings of a given timer.
Jitter allows to spread the triggering dates of resource-intensive
operations. For instance, if a communication to the M2M operating portal is
scheduled at midnight with "0 0 * * *"
on a large fleet of devices,
congestion might ensue on the communication networks and/or on the servers.
By adding a 60604 = 14400 jitter, communications will be uniformly
spread between 0:00AM and 04:00AM: "0 0 * * * 14400"
.
Cron specifications can also be entered as tables, with keys `minute, hour,
dayofmonth, dayofweek, jitter` respectively; missing keys are treated as
"*"
(except for jitter
, which is treated as missing); lists of values
are passed as tables; steps and ranges are not supported in any special ways,
but they can be passed as strings, e.g. { hour='9-12,14-17', minute=0 }
.
cron
:
specification of the cron dates, as a cron string or a table
(see #timer.new)
hook
:
optional function, run everytime the timer elapses
varargs
:
optional parameters for hook
a timer object
timer.cron_nextevent(timer)
nextevent
field.
timer
:
the CRON string
next occurrence's date, as an os.time()
number.
timer.latencyExec(func, latency)
n
seconds; if the task
is performed by calling function f
, then a call to timer.latencyexec(f, n)
will guarantee that. If f
hasn't been run after the n
seconds delay expired
(neither by this thread nor any other one), then the execution of f
is triggered.
However, if f
has already been triggered by another call to timer.latencyexec
before the delay expired, then it isn't triggered a second time.
When a function can treat data by batches and is idempotent (think for instance
flushing buffered data to a server), calling it with a delay through latencyexec
leaves a chance to group multiple invocations into a single one, thus presumably
saving resources.
func
:
function to run.
latency
:
number. Time to wait before running the function (in secondes).
timer.latencyExec examples
-- t1: `f` must have run within the next 10s
timer.latencyexec(f, 10)
-- t2: `f` must have run within the next 5s. This supercedes t1
timer.latencyexec(f, 5)
-- after 3s elapsed, `f` must run within the next 5s-3s=2s
sched.wait(3)
-- t3: `f` must have run within the next 1s. This supercedes the 2s left in t2
t3 = timer.latencyexec(f, 1)
-- `f` will run in the middle of this 2s delay, due to t3
sched.wait(2)
-- This has been scheduled after t3 elapsed, so `f` will be run again in 1s at most.
t4 = timer.latencyexec(f, 1)
timer.nbofdaysinmonth(date)
date
:
in os.time
format included in the considered month
number of days in the month: 28,29,30,31
timer.new(expiry, hook, varargs)
This function can be called directly instead of timer.once, timer.periodic and timer.cron.
The event will cause a signal([returned timer], 'run')
whenever its due
date(s) is/are reached. The date(s) is/are determined by the expiry
argument:
If expiry
is a positive number, causes a 1-shot event after that delay,
in seconds, has elapsed.
If expiry
is a negative number, causes a periodic event whose period is
the (positive) opposite of expiry
, in seconds.
If expiry
is a string or a table, causes events at the dates described
under the POSIX CRON format. Cf. timer.cron for a detailed description
of the CRON format.
assert
expiry
:
string or number specifying the timer's due date(s).
See explanations above.
hook
:
an optional function which will be called whenever the timer is due.
The hook is called in a new thread (thus blocking functions are allowed in it).
varargs
:
optional additional parameters, which will be passed to the hook
function when called.
nil
followed by an error message otherwise.
timer.nextval(p, curr, period, minval)
p
:
the string extract to convert
curr
:
value of current time (in the same units as p
)
period
:
period of p
's units, e.g. 60 for minutes, 24 for hours etc.
minval
:
minimal acceptable value for the result
time of next occurrence of the event, in the same units as p
.
timer.once(delay, hook, varargs)
delay
seconds.
delay
:
the duration before the timer elapses, in seconds
hook
:
optional function, run when the timer elapses
varargs
:
optional parameters for hook
timer.periodic(period, hook, varargs)
period
seconds.
period
:
the duration between two timer triggerrings, in seconds
hook
:
optional function, run everytime the timer elapses
varargs
:
optional parameters for hook