[[tags: egg]]
== simple-timer
Simple, cancel-able, efficient timers.
Currently only a low level interface is provided.
TBD: Support [[https://srfi.schemers.org/srfi-120/srfi-120.html|srfi-120]] too.
== Rationale
The srfi-120 API incurs just too much overhead for many use cases to
settle upon. (The issue: srfi120's task identifiers as returned by
{{timer-schedule!}} are defined to be readable object, which adds
undue overhead for cancellation.)
Every other egg implements it's own idea of timers, which makes for a
hell to mix them. This should be(come) a low level enough interface
to support most needs while having all timers in one place.
Another issue frequently coming up with CHICKEN is the false deadlock
detection when signal handlers are used to unlock the situation. The
common work around to load yet another thread looping for some time
puts load at the core's timeout queue. Simply using this egg should
install one such timer once and for all. (TBD: make sure this works
over forks too. Should this cancel timeouts?)
The timers here are assumed mostly timeouts or regular background jobs
and hence rarely run. They should not be sensitive to precise
timings. (It is the job of this eggs timers to reduce the load on the
timeout queue in chickens core and optimize for minimal overhead for
timers canceled within less than a {{timer-period}}.)
Timeouts fire only if they are not canceled before at least a full
{{timer-period}} passed. A {{timer-period}} defaults to one
second. Timers are run in batches rounded to {{timer-epsilon}} of
it's scheduled time every {{timer-period}}.
== Requirements
Requires [[llrb-tree]], [[pigeon-hole]].
Notes:
* Could use any other priority queue conforming to the srfi-69 API
instead of llrb-tree
* pigeon-hole is merely for historical reasons
== API
(timer-period . new) -> PERIOD
Without argument queries the current period. With argument chances
the period at which timers are fired.
(timer-epsilon . new) -> PERIOD
Without argument queries the current epsilon. With argument chances
the epsilon for grouping timers by due time.
(timer-condition? obj) -> boolean
Predicate to test for timer conditions.
(make-timer-condition)
Creates a timer condition.
(register-timer-task! time job) -> TASK
Registers {{JOB}} to be run after {{TIME}} has passed. Returns a
reference to the task. The reference is opaque by definition - unlike
srfi-120 {{timer-schedule!}}'s task identifiers. In fact it is a
pair.
The job is typically a thunk to be executed. This thunk MUST NOT
raise exceptions, MAY NOT block and SHOULD return ASAP. So except for
simple, fast operations it should schedule the actual operation,
e.g. by starting a fresh thread, and return.
If a thread is given as {{JOB}}, it will receive a {{timer-condition?}}
via {{thread-signal!}}.
Undocumented: if a {{pigeon-hole}} is given as {{JOB}} a
{{timer-condition?}} is unconditionally queued.
(cancel-timer-task! TASK) -> boolean
Cancels the TASK (must be a reference obtained from
{{register-timer-task!}}). (In fact it atomically sets the cdr of the
reference to {{#f}} and returns the old value.)
Returns {{#f}} if the {{TASK}} was already canceled or fired.
== Examples
(handle-exceptions
ex
(cond
((timer-condition? ex) #t)
(else ex))
(register-timer-task! 1 (current-thread))
(thread-sleep! 3)
'timer-exceptions-should-have-occurred-before)
=> #t
Cancelation: {{set! b #t}} is never invoked.
(or (cancel-timer-task!
(register-timer-task! 0.03 (lambda () (set! b #t))))
(error "too late to cancel timer"))
Best practice using thread signals:
(handle-exceptions
ex
(cond
((timer-condition? ex)
;; clean up after timeout here
#t)
(else ex))
...
(let* ((task (register-timer-task! 1 (current-thread)) )
(result (do-some-work-within-time-limit)))
(cancel-timer-task! task)
...
result)
Best practice using messages (try to avoid needless {{dynamic-wind}}s
in this context):
(let* ((chan (gochan 0))
(task (register-timer-task! 1 (lambda () (go (gochan-send chan (make-timer-condition))))) )
(result (do-some-work-with chan)))
(cancel-timer-task! task)
result)
== About this egg
=== Source
Latest version:
[[http://github.com/0-8-15/simple-timer]]
=== Version History
0.1: Initial version.
=== Authors
Jörg F. Wittenberger
=== License
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the Software),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED ASIS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.