racket/collects/scribblings/reference/threads.scrbl
Matthew Flatt 39cedb62ed v3.99.0.2
svn: r7706
2007-11-13 12:40:00 +00:00

227 lines
10 KiB
Racket

#lang scribble/doc
@require["mz.ss"]
@title[#:tag "threads"]{Threads}
@declare-exporting[(lib "scheme/thread")]
See @secref["thread-model"] for basic information on the PLT Scheme
thread model.
When a thread is created, it is placed into the management of the
@tech{current custodian} and added to the current thread group (see
@secref["threadgroups"]). A thread can have any number of custodian
managers added through @scheme[thread-resume].
A thread that has not terminated can be garbage collected (see
@secref["gc-model"]) if it is unreachable and suspended, or if it
is unreachable and blocked on a set of unreachable events through
@scheme[semaphore-wait] or @scheme[semaphore-wait/enable-break],
@scheme[channel-put] or @scheme[channel-get], @scheme[sync] or
@scheme[sync/enable-break], or @scheme[thread-wait].
@margin-note{In MrEd, a handler thread for an eventspace is blocked on
an internal semaphore when its event queue is empty. Thus, the handler
thread is collectible when the eventspace is unreachable and contains
no visible windows or running timers.}
All constant-time procedures and operations provided by MzScheme are
thread-safe because they are @defterm{atomic}. For example,
@scheme[set!] assigns to a variable as an atomic action with respect
to all threads, so that no thread can see a ``half-assigned''
variable. Similarly, @scheme[vector-set!] assigns to a vector
atomically. The @scheme[hash-table-put!] procedure is not atomic, but
the table is protected by a lock; see @secref["hashtables"] for more
information. Port operations are generally not atomic, but they are
thread-safe in the sense that a byte consumed by one thread from an
input port will not be returned also to another thread, and procedures
like @scheme[port-commit-peeked] and @scheme[write-bytes-avail] offer
specific concurrency guarantees.
@;------------------------------------------------------------------------
@section{Creating Threads}
@defproc[(thread [thunk (-> any)]) thread?]{
Calls @scheme[thunk] with no arguments in a new thread of control. The
@scheme[thread] procedure returns immediately with a @deftech{thread
descriptor} value. When the invocation of @scheme[thunk] returns, the
thread created to invoke @scheme[thunk] terminates.
}
@defproc[(thread? [v any/c]) thread?]{Returns @scheme[#t] if
@scheme[v] is a @tech{thread descriptor}, @scheme[#f] otherwise.}
@defproc[(current-thread) thread?]{Returns the @tech{thread
descriptor} for the currently executing thread.}
@defproc[(thread/suspend-to-kill [thunk (-> any)]) thread]{
Like @scheme[thread], except that ``killing'' the thread through
@scheme[kill-thread] or @scheme[custodian-shutdown-all] merely
suspends the thread instead of terminating it. }
@defproc[(call-in-nested-thread [thunk (->any)]
[cust custodian? (current-custodian)])
any]{
Creates a nested thread managed by @scheme[cust] to execute
@scheme[thunk]. (The nested thread's current custodian is inherited
from the creating thread, independent of the @scheme[cust] argument.)
The current thread blocks until @scheme[thunk] returns, and the result
of the @scheme[call-in-nested-thread] call is the result returned by
@scheme[thunk].
The nested thread's exception handler is initialized to a procedure
that jumps to the beginning of the thread and transfers the exception
to the original thread. The handler thus terminates the nested thread
and re-raises the exception in the original thread.
If the thread created by @scheme[call-in-nested-thread] dies before
@scheme[thunk] returns, the @exnraise[exn:fail] in the original
thread. If the original thread is killed before @scheme[thunk]
returns, a break is queued for the nested thread.
If a break is queued for the original thread (with
@scheme[break-thread]) while the nested thread is running, the break
is redirected to the nested thread. If a break is already queued on
the original thread when the nested thread is created, the break is
moved to the nested thread. If a break remains queued on the nested
thread when it completes, the break is moved to the original thread.}
@;------------------------------------------------------------------------
@section[#:tag "threadkill"]{Suspending, Resuming, and Killing Threads}
@defproc[(thread-suspend [thd thread?]) void?]{
Immediately suspends the execution of @scheme[thd] if it is
running. If the thread has terminated or is already suspended,
@scheme[thread-suspend] has no effect. The thread remains suspended
(i.e., it does not execute) until it is resumed with
@scheme[thread-resume]. If the @tech{current custodian} does not
manage @scheme[thd] (and none of its subordinates manages
@scheme[thd]), the @exnraise[exn:fail:contract], and the thread is not
suspended.}
@defproc[(thread-resume [thd thread?][benefactor (or/c thread? custodian? false/c) #f]) void?]{
Resumes the execution of @scheme[thd] if it is suspended and has at
least one custodian (possibly added through @scheme[benefactor], as
described below). If the thread has terminated, or if the thread is
already running and @scheme[benefactor] is not supplied, or if the
thread has no custodian and @scheme[benefactor] is not supplied, then
@scheme[thread-resume] has no effect. Otherwise, if
@scheme[benefactor] is supplied, it triggers up to three
additional actions:
@itemize{
@item{If @scheme[benefactor] is a thread, whenever it is resumed
from a suspended state in the future, then @scheme[thd] is also
resumed. (Resuming @scheme[thd] may trigger the resumption of other
threads that were previously attached to @scheme[thd] through
@scheme[thread-resume].)}
@item{New custodians may be added to @scheme[thd]'s set of
managers. If @scheme[benefactor] is a thread, then all of the
thread's custodians are added to @scheme[thd]. Otherwise,
@scheme[benefactor] is a custodian, and it is added to @scheme[thd]
(unless the custodian is already shut down). If @scheme[thd]
becomes managed by both a custodian and one or more of its
subordinates, the redundant subordinates are removed from
@scheme[thd]. If @scheme[thd] is suspended and a custodian is
added, then @scheme[thd] is resumed only after the addition.}
@item{If @scheme[benefactor] is a thread, whenever it receives a
new managing custodian in the future, then @scheme[thd] also
receives the custodian. (Adding custodians to @scheme[thd] may
trigger adding the custodians to other threads that were previously
attached to @scheme[thd] through @scheme[thread-resume].)}
}}
@defproc[(kill-thread [thd thread?]) void?]{
Terminates the specified thread immediately, or suspends the thread if
@scheme[thd] was created with
@scheme[thread/suspend-to-kill]. Terminating the main thread exits the
application. If @scheme[thd] has already terminated,
@scheme[kill-thread] does nothing. If the @tech{current custodian}
does not manage @scheme[thd] (and none of its subordinates manages
@scheme[thd]), the @exnraise[exn:fail:contract], and the thread is not
killed or suspended.
Unless otherwise noted, procedures provided by MzScheme (and MrEd) are
kill-safe and suspend-safe; that is, killing or suspending a thread
never interferes with the application of procedures in other
threads. For example, if a thread is killed while extracting a
character from an input port, the character is either completely
consumed or not consumed, and other threads can safely use the port.}
@defproc[(break-thread [thd thread?]) void?]{
@index['("threads" "breaking")]{Registers} a break with the specified
thread. If breaking is disabled in @scheme[thd], the break will be
ignored until breaks are re-enabled (see @secref["breakhandler"]).}
@defproc[(sleep [secs nonnegative-number? 0]) void?]{
Causes the current thread to sleep until at least @scheme[secs]
seconds have passed after it starts sleeping. A zero value for
@scheme[secs] simply acts as a hint to allow other threads to
execute. The value of @scheme[secs] can be non-integral to request a
sleep duration to any precision; the precision of the actual sleep
time is unspecified.}
@defproc[(thread-running? [thd thread?]) any]{
@index['("threads" "run state")]{Returns} @scheme[#t] if @scheme[thd]
has not terminated and is not suspended, @scheme[#f] otherwise.}
@defproc[(thread-dead? [thd thread?]) any]{
Returns @scheme[#t] if @scheme[thd] has terminated, @scheme[#f]
otherwise.}
@;------------------------------------------------------------------------
@section[#:tag "threadsync"]{Synchronizing Thread State}
@defproc[(thread-wait [thd thread?]) void?]{
Blocks execution of the current thread until @scheme[thd] has
terminated. Note that @scheme[(thread-wait (current-thread))]
deadlocks the current thread, but a break can end the deadlock (if
breaking is enabled; see @secref["breakhandler"]).}
@defproc[(thread-dead-evt [thd thread?]) evt?]{
Returns a @tech{synchronizable event} (see @secref["sync"]) that is
ready if and only if @scheme[thd] has terminated. Unlike using
@scheme[thd] directly, however, a reference to the event does not
prevent @scheme[thd] from being garbage collected (see
@secref["gc-model"]).}
@defproc[(thread-resume-evt [thd thread?]) evt?]{
Returns a @tech{synchronizable event} (see @secref["sync"]) that
becomes ready when @scheme[thd] is running. (If @scheme[thd] has
terminated, the event never becomes ready.) If @scheme[thd] runs and
is then suspended after a call to @scheme[thread-resume-evt], the
result event remains ready; after each suspend of @scheme[thd] a fresh
event is generated to be returned by @scheme[thread-resume-evt]. The
result of the event is @scheme[thd], but if @scheme[thd] is never
resumed, then reference to the event does not prevent @scheme[thd]
from being garbage collected (see @secref["gc-model"]).}
@defproc[(thread-suspend-evt [thd thread?]) evt?]{
Returns a @tech{synchronizable event} (see @secref["sync"]) that
becomes ready when @scheme[thd] is suspended. (If @scheme[thd] has
terminated, the event will never unblock.) If @scheme[thd] is
suspended and then resumes after a call to
@scheme[thread-suspend-evt], the result event remains ready; after
each resume of @scheme[thd] created a fresh event to be returned by
@scheme[thread-suspend-evt].}