Up

Module Event

Events provide variants of run_at and run_after with the ability to abort or reschedule an event that hasn't yet happened. Once an event happens or is aborted, Async doesn't use any space for tracking it.

Signature

type ('a, 'h) t
val sexp_of_t : ('a -> Sexplib.Sexp.t) -> ('h -> Sexplib.Sexp.t) -> ('a, 'h) t -> Sexplib.Sexp.t
type t_unit = (unit, unit) t
val sexp_of_t_unit : t_unit -> Sexplib.Sexp.t
include Core_kernel.Std.Invariant.S2 with type ('a, 'b) t := ('a, 'b) t
type ('a, 'b) t
val invariant : 'a Invariant_intf.inv -> 'b Invariant_intf.inv -> ('a, 'b) t Invariant_intf.inv
val scheduled_at : (_, _) t -> Time.t
val status : ('a, 'h) t -> [
| `Aborted of 'a
| `Happened of 'h
| `Scheduled_at of Time.t
]

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 : Time.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 : Time.Span.t -> ('z -> 'h) -> 'z -> (_, 'h) t
val abort : ('a, 'h) t -> 'a -> [
| `Ok
| `Previously_aborted of 'a
| `Previously_happened of 'h
]

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).

val fired : ('a, 'h) t -> [
| `Aborted of 'a
| `Happened of 'h
] Clock_intf.Deferred.t
val reschedule_at : ('a, 'h) t -> Time.t -> [
| `Ok
| `Previously_aborted of 'a
| `Previously_happened of 'h
| `Too_late_to_reschedule
]

reschedule_at t and reschedule_after t change the time that t will fire, if possible, and if not, give a reason why. `Too_late_to_reschedule means that the Async job to fire t has been enqueued, but has not yet run.

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 -> Time.Span.t -> [
| `Ok
| `Previously_aborted of 'a
| `Previously_happened of 'h
| `Too_late_to_reschedule
]
val at : Time.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 one 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 : Time.Span.t -> (_, unit) t