ffi/unsafe/atomic: improve docs

This commit is contained in:
Matthew Flatt 2014-07-29 14:11:06 +01:00
parent 3342d54c82
commit a32160c6f4

View File

@ -5,20 +5,54 @@
@defmodule[ffi/unsafe/atomic] @defmodule[ffi/unsafe/atomic]
@deftech{Atomic mode} evaluates a Racket expression without switching
among Racket threads and with limited support for events. An atomic
computation in this sense is @emph{not} atomic with respect to other
@tech[#:doc reference.scrbl]{places}, but only to other @tech[#:doc
reference.scrbl]{threads} within a place.
Atomic mode is unsafe, because the Racket scheduler is not able to
operate while execution is in atomic mode; the scheduler cannot switch
threads or poll certain kinds of events, which can lead to deadlock or
starvation of other threads. Beware that many operations can involve
such synchronization, such as writing to an output port.
@deftogether[( @deftogether[(
@defproc[(start-atomic) void?] @defproc[(start-atomic) void?]
@defproc[(end-atomic) void?] @defproc[(end-atomic) void?]
)]{ )]{
Disables and enables context switches and delivery of break exceptions Disables/re-enables context switches at the level of Racket threads,
at the level of Racket threads. Calls to @racket[start-atomic] and and also suspends/resumes delivery of break exceptions (independent of the
result of @racket[break-enabled]). Calls to @racket[start-atomic] and
@racket[end-atomic] can be nested. @racket[end-atomic] can be nested.
Using @racket[call-as-atomic] is somewhat safer, in that Note that pairing @racket[start-atomic] and @racket[end-atomic] with
@racket[call-as-atomic] correctly catches exceptions and re-raises @racket[dynamic-wind] is useful only when
them after exiting atomic mode. For simple uses where exceptions need
not be handled, however, @racket[start-atomic] and @racket[end-atomic] @itemlist[
are faster.}
@item{the current @tech[#:doc reference.scrbl]{exception handler} is
known to safely escape atomic mode, or else all possible
escapes are through known continuation jumps or aborts (because
breaks are disabled and no other exceptions are possible) that
escape safely; and}
@item{exception constructions, if any, avoid printing values in the
exception message, or else the @tech[#:doc
reference.scrbl]{error value conversion handler} is always used
and known to be safe for atomic mode.}
]
Using @racket[call-as-atomic] is somewhat safer than using
@racket[start-atomic] and @racket[end-atomic], because
@racket[call-as-atomic] catches exceptions and re-raises them after
exiting atomic mode, and it wraps any call to the error value
conversion handler with @racket[call-as-nonatomic]. The latter is safe
for a particular atomic region, however, only if the region can be
safely interrupted by a non-atomic exception construction.}
@deftogether[( @deftogether[(
@defproc[(start-breakable-atomic) void?] @defproc[(start-breakable-atomic) void?]
@ -26,36 +60,48 @@ are faster.}
)]{ )]{
Like @racket[start-atomic] and @racket[end-atomic], but the delivery Like @racket[start-atomic] and @racket[end-atomic], but the delivery
of break exceptions is not suspended. To ensure that a call to of break exceptions is not suspended.
@racket[start-atomic] is reliably paired with a call to
@racket[end-atomic], use @racket[dynamic-wind] pre- and post thunks or These functions are not significantly faster than
some other context where breaks are disabled. These variants are not @racket[start-atomic] and @racket[end-atomic], so they provide no
faster than plain @racket[start-atomic] and @racket[end-atomic].} benefit in a context where breaks are disabled.}
@defproc[(call-as-atomic [thunk (-> any)]) any]{ @defproc[(call-as-atomic [thunk (-> any)]) any]{
Calls @racket[thunk] in atomic mode. If @racket[thunk] raises an Calls @racket[thunk] in atomic mode, where @racket[call-as-nonatomic]
exception, the exception is caught and re-raised after exiting atomic can be used during the dynamic extent of the call to revert to
mode. non-atomic mode for a nested computation.
When @racket[call-as-atomic] is used in the dynamic extent of When @racket[call-as-atomic] is used in the dynamic extent of
@racket[call-as-atomic], then @racket[thunk] is simply called directly @racket[call-as-atomic], then @racket[thunk] is called directly as a
(as a non-tail call).} non-tail call.
If @racket[thunk] raises an exception, the exception is caught and
re-raised after exiting atomic mode. Any call to the current
@tech[#:doc reference.scrbl]{error value conversion handler} is
effectively wrapped with @racket[call-as-nonatomic].
Besides obvious paths to unknown expressions that may not be safe for
atomic mode, beware of printing an arbitrary value in any way other
than the error value conversion handler, because values can have
arbitrary printing procedures attached through
@racket[prop:custom-write].}
@defproc[(call-as-nonatomic [thunk (-> any)]) any]{ @defproc[(call-as-nonatomic [thunk (-> any)]) any]{
Within the dynamic extent of a @racket[call-as-atomic], calls Within the dynamic extent of a call to @racket[call-as-atomic], calls
@racket[thunk] in non-atomic mode. Beware that the current thread @racket[thunk] in non-atomic mode. Beware that the current thread may
may be suspended or terminated by other threads during the be suspended or terminated by other threads during the execution of
execution of @racket[thunk], in which case the call never returns. @racket[thunk].
When used not in the dynamic extent of @racket[call-as-atomic], When used not in the dynamic extent of a call to
@racket[call-as-nonatomic] raises @racket[exn:fail:contract].} @racket[call-as-atomic], @racket[call-as-nonatomic] raises
@racket[exn:fail:contract].}
@defproc[(in-atomic-mode?) boolean?]{ @defproc[(in-atomic-mode?) boolean?]{
Returns @racket[#t] if Racket context switches are disabled, Returns @racket[#t] when in @tech{atomic mode} (within the current
@racket[#f] otherwise.} @tech[#:doc reference.scrbl]{place}), @racket[#f] otherwise.}