module Rtime:Timelines forsig..end
React.
Rtime manages time stamp events, delayed events and delayed signals along timelines. The client chooses the concrete timeline by providing an absolute notion of time. Running the timeline at the appropriate pace is left to the client.
See examples of use.
Version 0.9.0 - daniel.buenzl i@erratique.ch
typetime =float
typeduration =float
type t
val create : ?earlier:(t -> unit) -> (unit -> time) -> tcreate earlier now is a timeline whose absolute current time is
defined by calling the function now. earlier is called with
the timeline as an argument whenever a new deadline is scheduled
before all the others on the line; this can be used to unblock a
sleeping thread.val now : t -> timenow l is the current time on the timeline.val wakeup : t -> duration optionwakeup l is the duration until the next deadline on the timeline
(if any). If duration is negative, the timeline is late.val progress : ?exec:bool -> t -> unitprogress exec l immediatly removes the next deadline from the
timeline and if exec is true (default) executes it.
Warning. Deadline executions usually perform React
update cycles.
On a time stamp event occurence we distinguish :
The schedule and the occurence time may not coincide.
val stamp : ?stop:'a React.event ->
(time -> time -> 'b) -> t -> time -> 'b React.eventstamp stop occ l t is an event such that :
t on l.occ t t' with t' the occurence time.stop occurs
(existing deadlines are removed) or if t is earlier than now l
when stamp gets executed.val stamps : ?stop:'a React.event ->
?start:time ->
(time -> time -> 'b * time) -> t -> 'b React.eventstamps stop start occ l is an event such that :
start (defaults to now l).t, occ start t returns the stamp for the
current occurence and the time of the next schedule. If the latter
is earlier or equal to t no new occurence is scheduled.stop occurs
(existing deadlines are removed) or if start is earlier than
now l when stamps gets exectued.val delay_e : ?stop:'a React.event ->
t -> duration -> 'b React.event -> 'b React.eventdelay_e stop l d e is an event such that :
e delayed by d units of time on l.stop occurs (existing deadlines
are removed).val delay_s : ?eq:('a -> 'a -> bool) ->
?stop:'b React.event ->
t -> duration -> 'a -> 'a React.signal -> 'a React.signaldelay_s eq stop l d i s is :
S.hold ?eq i (S.delay stop l d (S.changes s))
The following function returns an event with occurences stamped by their
occurence time and scheduled on l at
start, start + p, start + 2p, ...
start + (max - 1)p. Occurences known to be late at schedule time
are dropped. If max is None the number of occurences is unbounded.
let periodic ?max ?stop ?start l p = match max with
| None ->
let occ start t = t, (start +. ceil ((t -. start) /. p) *. p) in
Rtime.stamps ?stop ?start occ l
| Some max ->
if max <= 0 then React.E.never else
let occ max start t =
let i = ceil ((t -. start) /. p) in
if i >= max then t, t (* stop *) else t, (start +. i *. p)
in
Rtime.stamps ?stop ?start (occ (float max)) l
The following function returns a signal that will vary linearly
from 0.0 to 1.0 during the time interval from start to
start + d on l. The signal will update at most freq times per
l units. Updates known to be late at schedule time are dropped
and the last update value is guaranteed to be 1.
let ninterval ?stop ?start freq l d =
let max = floor (freq *. d) in
let p = 1. /. freq in
let np = 1. /. max in
let first = p +. match start with None -> Rtime.now l | Some s -> s in
let occ first t =
let i = ceil ((t -. first) /. p) in
if i >= max then 1., t (* stop *) else (i *. np), (first +. i *. p)
in
React.S.hold 0. (Rtime.stamps ?stop ~start:first occ l)
We show how to run a UNIX timeline in a dedicated thread.
First, React's update cycles must be executed in a critical
section. The thread dedicated to the timeline will trigger update
cycles upon deadline execution. Hence we need to execute them in
mutual exclusion from update cycles triggered by other
threads. The mutex function applies a function to its argument
in a critical section. The other threads will have to use this
function to perform update cycles; to acheive this automatically
they can use e_create and s_create to create their primitives.
let mutex =
let m = Mutex.create () in
fun f v ->
try Mutex.lock m; let r = f v in Mutex.unlock m; r with
| e -> (Mutex.unlock m; raise e)
let e_create () =
let e, send = React.E.create () in
e, mutex send
let s_create v =
let s, set = React.S.create v in
s, mutex set
Next, we need a mechanism to sleep the thread for a specific
amount of time (sleep) and a mean to unblock the thread
from another thread in case an earlier event occurrence gets created
(earlier). Unix's interval timers and condition variables are used to
achieve this.
let sleep, earlier =
let m = Mutex.create () in
let proceed = Condition.create () in
let sleeping = ref false in
let set_timer d =
let s = { Unix.it_interval = 0.; it_value = d } in
ignore (Unix.setitimer Unix.ITIMER_REAL s)
in
let sleep d = (* with d = 0. unbounded sleep. *)
if d < 0. then invalid_arg "negative delay";
Mutex.lock m;
sleeping := true;
set_timer d;
while !sleeping do Condition.wait proceed m done;
Mutex.unlock m
in
let earlier _ =
Mutex.lock m;
sleeping := false;
set_timer 0.;
Condition.signal proceed;
Mutex.unlock m;
in
let timer _ = sleeping := false; Condition.signal proceed;
in
Sys.set_signal Sys.sigalrm (Sys.Signal_handle timer);
sleep, earlier
We can now define a UNIX timeline l and a run function to
run it. Note the use of the mutex function to progress the timeline.
let l = Rtime.create ~earlier Unix.gettimeofday
let run l =
try
while true do match Rtime.wakeup l with
| None -> sleep 0. (* unbounded sleep. *)
| Some d when d > 0. -> sleep d
| Some _ -> mutex Rtime.progress l
done;
assert (false);
with e -> e
let run_utime () = Thread.create run l