val sexp_of_t : ('a -> Ppx_sexp_conv_lib.Sexp.t) -> ('h -> Ppx_sexp_conv_lib.Sexp.t) -> ('a, 'h) t -> Ppx_sexp_conv_lib.Sexp.t
type t_unit = (unit, unit) t
val sexp_of_t_unit : t_unit -> Ppx_sexp_conv_lib.Sexp.t
include Core_kernel.Invariant.S2 with type ('a, 'b) t := ('a, 'b) t
val invariant : ('a -> unit) -> ('b -> unit) -> ('a, 'b) t -> unit
val scheduled_at : (_, _) t -> Core_kernel.Int63.t
module Status : sig ... end
If status
returns Scheduled_at time
, it is possible that time < Time.now ()
if Async's scheduler hasn't yet gotten the chance to update its clock, e.g., due to user jobs running.
val run_at : Core_kernel.Int63.t -> ('z -> 'h) -> 'z -> (_, 'h) t
Let t = run_at time f z
. At time
, this runs f z
and transitions status t
to Happened h
, where h
is result of f z
.
More precisely, at time
, provided abort t a
has not previously been called, this will call f z
, with the guarantee that status t = Scheduled_at time
. If f z
returns h
and did not call abort t a
, then status t
becomes Happened
h
. If f z
calls abort t a
, then the result of f
is ignored, and status t
is Aborted a
.
If f z
raises, then status t
does not transition and remains Scheduled_at
time
, and the exception is sent to the monitor in effect when run_at
was called.
val run_after : Core_kernel.Int63.t -> ('z -> 'h) -> 'z -> (_, 'h) t
module Abort_result = Time_source.Event.Abort_result
val abort : ('a, 'h) t -> 'a -> ('a, 'h) Abort_result.t
abort t
changes status t
to Aborted
and returns Ok
, unless t
previously happened or was previously aborted.
val abort_exn : ('a, 'h) t -> 'a -> unit
abort_exn t a
returns unit
if abort t a = `Ok
, and otherwise raises.
val abort_if_possible : ('a, _) t -> 'a -> unit
abort_if_possible t a = ignore (abort t a)
.
module Fired = Time_source.Event.Fired
module Reschedule_result = Time_source.Event.Reschedule_result
val reschedule_at : ('a, 'h) t -> Core_kernel.Int63.t -> ('a, 'h) Reschedule_result.t
reschedule_at t
and reschedule_after t
change the time that t
will fire, if possible, and if not, give a reason why. Like run_at
, if the requested time is in the past, the event will be scheduled to run immediately. If reschedule_at t
time = Ok
, then subsequently scheduled_at t = time
.
val reschedule_after : ('a, 'h) t -> Core_kernel.Int63.t -> ('a, 'h) Reschedule_result.t
val at : Core_kernel.Int63.t -> (_, unit) t
at time
is run_at time ignore ()
. after time
is run_after time ignore ()
.
You should generally prefer to use the run_*
functions, which allow you to synchronously update state via a user-supplied function when the event transitions to Happened
. That is, there is an important difference between:
let t = run_at time f ()
and:
let t = at time in
fired t
>>> function
| Happened () -> f ()
| Aborted () -> ()
With run_at
, if status t = Happened
, one knows that f
has run. With at
and fired
, one does not know whether f
has yet run; it may still be scheduled to run. Thus, with at
and fired
, it is easy to introduce a race. For example, consider these two code snippets:
let t = Event.after (sec 2.) in
upon (Event.fired t) (function
| Aborted () -> ()
| Happened () -> printf "Timer fired");
upon deferred_event (fun () ->
match Event.abort t () with
| Ok -> printf "Event occurred"
| Previously_aborted () -> assert false
| Previously_happened () -> printf "Event occurred after timer fired");
let t = Event.run_after (sec 2.) printf "Timer fired" in
upon deferred_event (fun () ->
match Event.abort t () with
| Ok -> printf "Event occurred"
| Previously_aborted () -> assert false
| Previously_happened () -> printf "Event occurred after timer fired");
In both snippets, if Event.abort
returns Ok
, "Timer fired" is never printed. However, the first snippet might print "Event occurred after timer fired" and then "Timer fired". This confused ordering cannot happen with Event.run_after
.
val after : Core_kernel.Int63.t -> (_, unit) t