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.
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.
abort t changes status t to `Aborted and returns `Ok, unless t
previously happened or was previously aborted.
abort_exn t a returns unit if abort t a = `Ok, and otherwise raises.
abort_if_possible t a = ignore (abort t a).
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.
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.