#lang scribble/doc
@(require scribble/struct
          "mz.rkt"
          (for-label racket/async-channel
                     (only-in ffi/unsafe/schedule unsafe-poller)))

@(define evt-eval (make-base-eval))

@title[#:tag "sync"]{Events}

@section-index["select"]
@section-index["poll"]

A @deftech{synchronizable event} (or just @defterm{event} for short)
works with the @racket[sync] procedure to coordinate synchronization
among threads. Certain kinds of objects double as events, including
ports and threads. Other kinds of objects exist only for their use as
events.

At any point in time, an event is either @deftech{ready for
synchronization}, or it is not; depending on the kind of event and how
it is used by other threads, an event can switch from not ready to
ready (or back), at any time.  If a thread synchronizes on an event
when it is ready, then the event produces a particular
@deftech{synchronization result}.

Synchronizing an event may affect the state of the event. For example,
when synchronizing a semaphore, then the semaphore's internal count is
decremented, just as with @racket[semaphore-wait]. For most kinds of
events, however (such as a port), synchronizing does not modify the
event's state.

Racket values that act as @tech{synchronizable events} include
@tech{asynchronous channels},
@tech{channels},
@tech{custodian box}es,
@tech{log receivers},
@tech{place channels},
@tech{ports},
@tech{semaphores},
@tech{subprocess}es,
@tech{TCP listeners},
@tech{threads}, and
@tech{will executors}.
Libraries can define new synchronizable events, especially
though @racket[prop:evt].

@;------------------------------------------------------------------------

@defproc[(evt? [v any/c]) boolean?]{

Returns @racket[#t] if @racket[v] is a @tech{synchronizable event},
@racket[#f] otherwise.

@examples[#:eval evt-eval
  (evt? never-evt)
  (evt? (make-channel))
  (evt? 5)
]}


@defproc[(sync [evt evt?] ...) any]{

Blocks as long as none of the @tech{synchronizable events}
@racket[evt]s are ready, as defined above.

When at least one @racket[evt] is ready, its @tech{synchronization
result} (often @racket[evt] itself) is returned.  If multiple
@racket[evt]s are ready, one of the @racket[evt]s is chosen
pseudo-randomly for the result; the
@racket[current-evt-pseudo-random-generator] parameter sets the
random-number generator that controls this choice.

@examples[#:eval evt-eval
  (define ch (make-channel))
  (thread (λ () (displayln (sync ch))))
  (channel-put ch 'hellooooo)
]

@history[#:changed "6.1.0.3" @elem{Allow 0 arguments instead of 1 or more.}]}


@defproc[(sync/timeout [timeout (or/c #f (and/c real? (not/c negative?)) (-> any))]
                       [evt evt?] ...)
          any]{

Like @racket[sync] if @racket[timeout] is @racket[#f]. If
@racket[timeout] is a real number, then the result is @racket[#f]
if @racket[timeout] seconds pass without a
successful synchronization. If @racket[timeout] is a procedure, then
it is called in tail position if polling the @racket[evt]s discovers
no ready events.

A zero value for @racket[timeout] is equivalent to @racket[(lambda ()
#f)]. In either case, each @racket[evt] is checked at least once
before returning @racket[#f] or calling @racket[timeout].

See also @racket[alarm-evt] for an alternative timeout mechanism.

@examples[#:eval evt-eval
  (code:comment "times out before waking up")
  (sync/timeout
   0.5
   (thread (λ () (sleep 1) (displayln "woke up!"))))
  (sync/timeout
   (λ () (displayln "no ready events"))
   never-evt)
]

@history[#:changed "6.1.0.3" @elem{Allow 1 argument instead of 2 or more.}]}


@defproc[(sync/enable-break [evt evt?] ...) any]{

Like @racket[sync], but breaking is enabled (see
@secref["breakhandler"]) while waiting on the @racket[evt]s. If
breaking is disabled when @racket[sync/enable-break] is called, then
either all @racket[evt]s remain unchosen or the @racket[exn:break]
exception is raised, but not both.}


@defproc[(sync/timeout/enable-break [timeout (or/c #f (and/c real? (not/c negative?)) (-> any))]
                                    [evt evt?] ...)
         any]{

Like @racket[sync/enable-break], but with a timeout as for @racket[sync/timeout].}


@defproc[(choice-evt [evt evt?] ...) evt?]{

Creates and returns a single event that combines the
@racket[evt]s. Supplying the result to @racket[sync] is the same as
supplying each @racket[evt] to the same call.

That is, an event returned by @racket[choice-evt] is @tech{ready for
synchronization} when one or more of the @racket[_evt]s supplied to
@racket[choice-evt] are @tech{ready for synchronization}. If the
choice event is chosen, one of its ready @racket[_evt]s is chosen
pseudo-randomly, and the @tech{synchronization result} is the chosen
@racket[_evt]'s @tech{synchronization result}.

@examples[#:eval evt-eval
  (define ch1 (make-channel))
  (define ch2 (make-channel))
  (define either-channel (choice-evt ch1 ch2))
  (thread (λ () (displayln (sync either-channel))))
  (channel-put
   (if (> (random) 0.5) ch1 ch2)
   'tuturuu)
]}


@defproc[(wrap-evt [evt evt?]
                   [wrap (any/c ... . -> . any)]) 
         evt?]{

Creates an event that is @tech{ready for synchronization} when
@racket[evt] is @tech{ready for synchronization}, but whose
@tech{synchronization result} is determined by applying @racket[wrap]
to the @tech{synchronization result} of @racket[evt]. The number
of arguments accepted by @racket[wrap] must match the number of values
for the synchronization result of @racket[evt].

The call to @racket[wrap] is
@racket[parameterize-break]ed to disable breaks initially.

@examples[#:eval evt-eval
  (define ch (make-channel))
  (define evt (wrap-evt ch (λ (v) (format "you've got mail: ~a" v))))
  (thread (λ () (displayln (sync evt))))
  (channel-put ch "Dear Alice ...")
]}


@defproc[(handle-evt [evt evt?]
                     [handle (any/c ... . -> . any)]) 
         handle-evt?]{

Like @racket[wrap-evt], except that @racket[handle] is called in @tech{tail
position} with respect to the synchronization request---and without
breaks explicitly disabled---when it is not wrapped by @racket[wrap-evt],
@racket[chaperone-evt], or another @racket[handle-evt].

@examples[#:eval evt-eval
  (define msg-ch (make-channel))
  (define exit-ch (make-channel))
  (thread
   (λ ()
     (let loop ([val 0])
       (printf "val = ~a~n" val)
       (sync (handle-evt
              msg-ch
              (λ (val) (loop val)))
             (handle-evt
              exit-ch
              (λ (val) (displayln val)))))))
  (channel-put msg-ch 5)
  (channel-put msg-ch 7)
  (channel-put exit-ch 'done)
]}


@defproc[(guard-evt [maker (-> (or/c evt? any/c))]) evt?]{

Creates a value that behaves as an event, but that is actually an
event maker.

An event @racket[_guard] returned by @racket[guard-evt] generates an
event when @racket[_guard] is used with @racket[sync]
(or whenever it is part of a choice event used with @racket[sync],
etc.), where the generated event is the result of calling
@racket[maker]. The @racket[maker] procedure may be called by
@racket[sync] at most once for a given call to @racket[sync], but
@racket[maker] may not be called if a ready event is chosen before
@racket[_guard] is even considered.

If @racket[maker] returns a non-event, then @racket[maker]'s
result is replaced with an event that is @tech{ready for
synchronization} and whose @tech{synchronization result} is
@racket[_guard].}


@defproc[(nack-guard-evt [maker (evt? . -> . (or/c evt? any/c))]) evt?]{

Like @racket[guard-evt], but when @racket[maker] is called, it is
given a NACK (``negative acknowledgment'') event. After starting the
call to @racket[maker], if the event from @racket[maker] is not
ultimately chosen as the ready event, then the NACK event supplied to
@racket[maker] becomes @tech{ready for synchronization} with a
@|void-const| value.

The NACK event becomes @tech{ready for synchronization} when the event
is abandoned when either some other event is chosen, the synchronizing
thread is dead, or control escapes from the call to @racket[sync]
(even if @racket[_nack-guard]'s @racket[maker] has not yet returned a
value). If the event returned by @racket[maker] is chosen, then the
NACK event never becomes @tech{ready for synchronization}.}


@defproc[(poll-guard-evt [maker (boolean? . -> . (or/c evt? any/c))]) evt?]{

Like @racket[guard-evt], but when @racket[maker] is called, it is
provided a boolean value that indicates whether the event will be used
for a poll, @racket[#t], or for a blocking synchronization,
@racket[#f].

If @racket[#t] is supplied to @racket[maker], if breaks are
disabled, if the polling thread is not terminated, and if polling the
resulting event produces a @tech{synchronization result}, then the event
will certainly be chosen for its result.}


@defproc[(replace-evt [evt evt?] [maker (any/c ... . -> . (or/c evt? any/c))]) evt?]{

Like @racket[guard-evt], but @racket[maker] is called only after
@racket[evt] becomes @tech{ready for synchronization}, and the
@tech{synchronization result} of @racket[evt] is passed to @racket[maker].

The attempt to synchronize on @racket[evt] proceeds concurrently as
the attempt to synchronize on the result @racket[_guard] from
@racket[replace-evt]; despite that concurrency, if @racket[maker] is
called, it is called in the thread that is synchronizing on
@racket[_guard]. Synchronization can succeed for both @racket[evt] and
another synchronized with @racket[_guard] at the same time; the
single-choice guarantee of synchronization applies only to the result
of @racket[maker] and other events synchronized with @racket[_guard].

If @racket[maker] returns a non-event, then @racket[maker]'s
result is replaced with an event that is @tech{ready for
synchronization} and whose @tech{synchronization result} is
@racket[_guard].

@history[#:added "6.1.0.3"]}


@defthing[always-evt evt?]{A constant event that is always @tech{ready
for synchronization}, with itself as its @tech{synchronization result}.

@examples[#:eval evt-eval
  (sync always-evt)
]}


@defthing[never-evt evt?]{A constant event that is never @tech{ready
for synchronization}.

@examples[#:eval evt-eval
  (sync/timeout 0.1 never-evt)
]}


@defproc[(system-idle-evt) evt?]{

Returns an event that is @tech{ready for synchronization} when the
system is otherwise idle: if the result event were replaced by
@racket[never-evt], no thread in the system would be available to run.
In other words, all threads must be suspended or blocked on events
with timeouts that have not yet expired. The system-idle event's
@tech{synchronization result} is @|void-const|. The result of the
@racket[system-idle-evt] procedure is always the same event.

@examples[#:eval evt-eval
  (define th (thread (λ () (let loop () (loop)))))
  (sync/timeout 0.1 (system-idle-evt))
  (kill-thread th)
  (eval:alts (sync (system-idle-evt)) (void))
]}


@defproc[(alarm-evt [msecs real?]) evt?]{

Returns a @tech{synchronizable event} that is not @tech{ready for synchronization} when
@racket[(current-inexact-milliseconds)] would return a value that is
less than @racket[msecs], and it is @tech{ready for synchronization} when
@racket[(current-inexact-milliseconds)] would return a value that is
more than @racket[msecs]. @ResultItself{alarm event}.

@examples[#:eval evt-eval
  (define alarm (alarm-evt (+ (current-inexact-milliseconds) 100)))
  (sync alarm)
]}


@defproc[(handle-evt? [evt evt?]) boolean?]{

Returns @racket[#t] if @racket[evt] was created by @racket[handle-evt]
or by @racket[choice-evt] applied to another event for which
@racket[handle-evt?] produces @racket[#t]. For any other event,
@racket[handle-evt?]  produces @racket[#f].

@examples[#:eval evt-eval
  (handle-evt? never-evt)
  (handle-evt? (handle-evt always-evt values))
]}

@;------------------------------------------------------------------------
@defthing[prop:evt struct-type-property?]{

A @tech{structure type property} that identifies structure types whose
 instances can serve as @tech{synchronizable events}. The property value can
 be any of the following:

@itemize[
 
 @item{An event @racket[_evt]: In this case, using the structure as an
 event is equivalent to using @racket[_evt].}

 @item{A procedure @racket[_proc] of one argument: In this case, the
 structure is similar to an event generated
 by @racket[guard-evt], except that the would-be guard
 procedure @racket[_proc] receives the structure as an argument, instead
 of no arguments; also, a non-event result from @racket[_proc]
 is replaced with an event that is already @tech{ready for synchronization}
 and whose @tech{synchronization result} is the structure.}

 @item{An exact, non-negative integer between @racket[0] (inclusive)
 and the number of non-automatic fields in the structure type
 (exclusive, not counting supertype fields): The integer identifies a
 field in the structure, and the field must be designated as
 immutable. If the field contains an object or an event-generating
 procedure of one argument, the event or procedure is used as
 above. Otherwise, the structure acts as an event that is never
 ready.}

]

@margin-note{For working with foreign libraries, a @racket[prop:evt]
             value can also be a result of @racket[unsafe-poller],
             although that possibility is omitted from the safe
             contract of @racket[prop:evt].}

Instances of a structure type with the @racket[prop:input-port] or
@racket[prop:output-port] property are also @tech{synchronizable events} by virtue
of being a port. If the structure type has more than one of
@racket[prop:evt], @racket[prop:input-port], and
@racket[prop:output-port], then the @racket[prop:evt] value (if any)
takes precedence for determining the instance's behavior as an event,
and the @racket[prop:input-port] property takes precedence over
@racket[prop:output-port] for synchronization.

@examples[
(define-struct wt (base val)
               #:property prop:evt (struct-field-index base))

(define sema (make-semaphore))
(sync/timeout 0 (make-wt sema #f))
(semaphore-post sema)
(sync/timeout 0 (make-wt sema #f))
(semaphore-post sema)
(sync/timeout 0 (make-wt (lambda (self) (wt-val self)) sema))
(semaphore-post sema)
(define my-wt (make-wt (lambda (self) (wrap-evt
                                       (wt-val self)
                                       (lambda (x) self)))
                       sema))
(sync/timeout 0 my-wt)
(sync/timeout 0 my-wt)
]}


@defparam[current-evt-pseudo-random-generator generator pseudo-random-generator?]{

A @tech{parameter} that determines the pseudo-random number generator used by
@racket[sync] for events created by @racket[choice-evt].}

@close-eval[evt-eval]

