doc work, especially threads and continuations reference

svn: r6786
This commit is contained in:
Matthew Flatt 2007-07-02 02:02:10 +00:00
parent 4bcf7111fb
commit 560eb67217
21 changed files with 1808 additions and 64 deletions

View File

@ -274,17 +274,20 @@
;; ----------------------------------------
(define/public (table-of-contents part ht)
(make-table #f (render-toc part #t)))
(make-table #f (render-toc part
(sub1 (length (collected-info-number
(part-collected-info part))))
#t)))
(define/public (local-table-of-contents part ht)
(table-of-contents part ht))
(define/private (render-toc part skip?)
(define/private (render-toc part base-len skip?)
(let ([number (collected-info-number (part-collected-info part))])
(let ([subs
(apply
append
(map (lambda (p) (render-toc p #f)) (part-parts part)))])
(map (lambda (p) (render-toc p base-len #f)) (part-parts part)))])
(if skip?
subs
(let ([l (cons
@ -292,7 +295,7 @@
(list
(make-paragraph
(list
(make-element 'hspace (list (make-string (* 2 (length number)) #\space)))
(make-element 'hspace (list (make-string (* 2 (- (length number) base-len)) #\space)))
(make-link-element (if (= 1 (length number))
"toptoclink"
"toclink")

View File

@ -378,6 +378,8 @@
(list (make-table style content))))
(list (make-table style content))))
(define max-proto-width 65)
(define (*defproc stx-ids prototypes arg-contractss result-contracts content-thunk)
(let ([spacer (hspace 1)]
[has-optional? (lambda (arg)
@ -437,7 +439,12 @@
(let ([req (reverse r-accum)])
(let loop ([a a][o-accum null])
(if (or (null? a)
(not (has-optional? (car a))))
(and (not (has-optional? (car a)))
;; A repeat after an optional argument is
;; effectively optional:
(not (memq (car a) '(...)))
(or (null? (cdr a))
(not (memq (cadr a) '(...))))))
(values req (reverse o-accum) a)
(loop (cdr a) (cons (car a) o-accum)))))
(loop (cdr a) (cons (car a) r-accum))))]
@ -457,11 +464,12 @@
flat-size
(prototype-size prototype + max))
(flow-element-width res))
. >= . 50)]
. >= . (- max-proto-width 7))]
[(end) (list (to-flow spacer)
(to-flow 'rarr)
(to-flow spacer)
(make-flow (list res)))])
(make-flow (list res)))]
[(opt-cnt) (length optional)])
(append
(list
(list (make-flow
@ -512,31 +520,44 @@
(arg->elem (car optional))
(arg->elem (car required))))
not-end)
(let loop ([args (cdr (append required optional))]
(let loop ([args (cdr (append required optional more-required))]
[req (sub1 (length required))])
(if (null? args)
null
(cons (list* (to-flow spacer)
(if (zero? req)
(to-flow (make-element #f (list spacer "[")))
(to-flow spacer))
(let ([a (arg->elem (car args))])
(to-flow
(cond
[(null? (cdr args))
(if (null? optional)
(make-element
#f
(list a (schemeparenfont ")")))
(make-element
#f
(list a "]" (schemeparenfont ")"))))]
[else a])))
(if (and (null? (cdr args))
(not result-next-line?))
end
not-end))
(loop (cdr args) (sub1 req)))))))))))))
(let ([dots-next? (or (and (pair? (cdr args))
(or (eq? (cadr args) '...)
(eq? (cadr args) '...+))))])
(cons (list* (to-flow spacer)
(if (zero? req)
(to-flow (make-element #f (list spacer "[")))
(to-flow spacer))
(let ([a (arg->elem (car args))]
[next (if dots-next?
(make-element #f (list (hspace 1)
(arg->elem (cadr args))))
"")])
(to-flow
(cond
[(null? ((if dots-next? cddr cdr) args))
(if (or (null? optional)
(not (null? more-required)))
(make-element
#f
(list a next (schemeparenfont ")")))
(make-element
#f
(list a next "]" (schemeparenfont ")"))))]
[(and (pair? more-required)
(= (- 1 req) (length optional)))
(make-element #f (list a next "]"))]
[(equal? next "") a]
[else
(make-element #f (list a next))])))
(if (and (null? ((if dots-next? cddr cdr) args))
(not result-next-line?))
end
not-end))
(loop ((if dots-next? cddr cdr) args) (sub1 req))))))))))))))
(if result-next-line?
(list (list (make-flow (make-table-if-necessary
"prototype"
@ -546,29 +567,52 @@
(map (lambda (v arg-contract)
(cond
[(pair? v)
(list
(list
(make-flow
(make-table-if-necessary
"argcontract"
(list
(let ([v (if (keyword? (car v))
(cdr v)
v)])
(append
(let* ([v (if (keyword? (car v))
(cdr v)
v)]
[arg-cont (arg-contract)]
[base-len (+ 5 (string-length (symbol->string (car v)))
(flow-element-width arg-cont))]
[def-len (if (has-optional? v)
(string-length (format "~a" (caddr v)))
0)]
[base-list
(list
(to-flow (hspace 2))
(to-flow (arg->elem v))
(to-flow spacer)
(to-flow ":")
(to-flow spacer)
(make-flow (list (arg-contract))))
(if (has-optional? v)
(list (to-flow spacer)
(to-flow "=")
(to-flow spacer)
(to-flow (to-element (caddr v))))
null))))))))]
(make-flow (list arg-cont)))])
(list
(list
(make-flow
(if (and (has-optional? v)
((+ base-len 3 def-len) . >= . max-proto-width))
(list
(make-table
"argcontract"
(list
base-list
(list
(to-flow spacer)
(to-flow spacer)
(to-flow spacer)
(to-flow "=")
(to-flow spacer)
(to-flow (to-element (caddr v)))))))
(make-table-if-necessary
"argcontract"
(list
(append
base-list
(if (and (has-optional? v)
((+ base-len 3 def-len) . < . max-proto-width))
(list (to-flow spacer)
(to-flow "=")
(to-flow spacer)
(to-flow (to-element (caddr v))))
null)))))))))]
[else null]))
(cdr prototype)
arg-contracts)))))
@ -815,6 +859,11 @@
(define (commandline . s)
(make-paragraph (list (hspace 2) (apply tt s))))
(define (elemtag t . body)
(make-target-element #f (decode-content body) t))
(define (elemref t . body)
(make-link-element #f (decode-content body) t))
(provide elemtag elemref)
(define (secref s)
(make-link-element #f null `(part ,s)))

View File

@ -0,0 +1,134 @@
#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]
@title[#:tag "mz:breakhandler"]{Breaks}
@index['("threads" "breaking")]{A} @deftech{break} is an asynchronous
exception, usually triggered through an external source controlled by
the user, or through the @scheme[break-thread] procedure. A break
exception can only occur in a thread while breaks are enabled. When a
break is detected and enabled, the @exnraise[exn:break] in the thread
sometime afterward; if breaking is disabled when @scheme[break-thread]
is called, the break is suspended until breaking is again enabled for
the thread. While a thread has a suspended break, additional breaks
are ignored.
Breaks are enabled through the @scheme[break-enabled] parameter-like
procedure, and through the @scheme[parameterize-break] form, which is
analogous to @scheme[parameterize]. The @scheme[break-enabled]
procedure does not represent a parameter to be used with
@scheme[parameterize], because changing the break-enabled state of a
thread requires an explicit check for breaks, and this check is
incompatible with the tail evaluation of a @scheme[parameterize]
expression's body.
Certain procedures, such as @scheme[semaphore-wait/enable-break],
enable breaks temporarily while performing a blocking action. If
breaks are enabled for a thread, and if a break is triggered for the
thread but not yet delivered as an @scheme[exn:break] exception, then
the break is guaranteed to be delivered before breaks can be disabled
in the thread. The timing of @scheme[exn:break] exceptions is not
guaranteed in any other way.
Before calling a @scheme[with-handlers] predicate or handler, an
exception handler, an error display handler, an error escape handler,
an error value conversion handler, or a @scheme[pre-thunk] or
@scheme[post-thunk] for a @scheme[dynamic-wind], the call is
@scheme[parameterize-break]ed to disable breaks. Furthermore, breaks
are disabled during the transitions among handlers related to
exceptions, during the transitions between @scheme[pre-thunk]s and
@scheme[post-thunk]s for @scheme[dynamic-wind], and during other
transitions for a continuation jump. For example, if breaks are
disabled when a continuation is invoked, and if breaks are also
disabled in the target continuation, then breaks will remain disabled
until from the time of the invocation until the target continuation
executes unless a relevant @scheme[dynamic-wind] @scheme[pre-thunk] or
@scheme[post-thunk] explicitly enables breaks.
If a break is triggered for a thread that is blocked on a nested
thread (see @scheme[call-in-nested-thread]), and if breaks are enabled
in the blocked thread, the break is implicitly handled by transferring
it to the nested thread.
When breaks are enabled, they can occur at any point within execution,
which makes certain implementation tasks subtle. For example, assuming
breaks are enabled when the following code is executed,
@schemeblock[
(with-handlers ([exn:break? (lambda (x) (void))])
(semaphore-wait s))
]
then it is @italic{not} the case that a @|void-const| result means the
semaphore was decremented or a break was received, exclusively. It is
possible that @italic{both} occur: the break may occur after the
semaphore is successfully decremented but before a @|void-const|
result is returned by @scheme[semaphore-wait]. A break exception will
never damage a semaphore, or any other built-in construct, but many
built-in procedures (including @scheme[semaphore-wait]) contain
internal sub-expressions that can be interrupted by a break.
In general, it is impossible using only @scheme[semaphore-wait] to
implement the guarantee that either the semaphore is decremented or an
exception is raised, but not both. Scheme therefore supplies
@scheme[semaphore-wait/enable-break] (see @secref["mz:semaphore"]),
which does permit the implementation of such an exclusive guarantee:
@schemeblock[
(parameterize ([break-enabled #f])
(with-handlers ([exn:break? (lambda (x) (void))])
(semaphore-wait/enable-break s)))
]
In the above expression, a break can occur at any point until breaks
are disabled, in which case a break exception is propagated to the
enclosing exception handler. Otherwise, the break can only occur
within @scheme[semaphore-wait/enable-break], which guarantees that if
a break exception is raised, the semaphore will not have been
decremented.
To allow similar implementation patterns over blocking port
operations, MzScheme provides @scheme[read-bytes-avail!/enable-break]
(see @secref["mz:read"]), @scheme[write-bytes-avail/enable-break] (see
@secref["mz:write"]), and other procedures.
@;------------------------------------------------------------------------
@defproc*[([(break-enabled) boolean?]
[(break-enabled [on? any/c]) void?])]{
Gets or sets the break enabled state of the current thread. If
@scheme[on?] is not supplied, the result is @scheme[#t] if breaks are
currently enabled, @scheme[#f] otherwise. If @scheme[on?] is supplied
as @scheme[#f], breaks are disabled, and if @scheme[on?] is a true
value, breaks are enabled.}
@defform[(parameterize-break boolean-expr body ...+)]{Evaluates
@scheme[boolean-expr] to determine whether breaks are initially
enabled in while evaluating the @scheme[body]s in sequence. The result
of the @scheme[parameter-break] expression is the result of the last
@scheme[expr].
Like @scheme[parameterize] (see @secref["mz:parameters"]), a fresh
@tech{thread cell} (see @secref["mz:threadcells"]) is allocated to
hold the break-enabled state of the continuation, and calls to
@scheme[break-enabled] within the continuation access or modify the
new cell. Unlike parameters, the break setting is not inherited by new
threads.}
@defproc[(current-break-parameterization) break-parameterization?]{
Analogous to @scheme[(current-parameterization)] (see
@secref["mz:parameters"]); it returns a break-parameterization
(effectively a thread cell) that holds the current continuation's
break-enable state.}
@defproc[(call-with-break-parameterization
[break-param break-parameterization?]
[thunk (-> any)])
any]{
Analogous to @scheme[(call-with-parameterization parameterization
thunk)] (see @secref["mz:parameters"]), calls @scheme[thunk] in a
continuation whose break-enabled state is in @scheme[break-param]. The
@scheme[thunk] is @italic{not} called in tail position with respect to
the @scheme[call-with-break-parameterization] call.}

View File

@ -0,0 +1,56 @@
#reader(lib "docreader.ss" "scribble")
@require[(lib "bnf.ss" "scribble")]
@require["mz.ss"]
@title[#:tag "mz:channel"]{Channels}
A @deftech{channel} both synchronizes a pair of threads and passes a
value from one to the other. Channels are synchronous; both the sender
and the receiver must block until the (atomic) transaction is
complete. Multiple senders and receivers can access a channel at once,
but a single sender and receiver is selected for each transaction.
Channel synchronization is @defterm{fair}: if a thread is blocked on a
channel and transaction opportunities for the channel occur infinitely
often, then the thread eventually participates in a transaction.
For buffered asynchronous channels, see @secref["mz:async-channel"].
@defproc[(make-channel) channel?]{
Creates and returns a new channel. The channel can be used with
@scheme[channel-get], with @scheme[channel-try-get], or as a
@tech{synchronizable event} (see @secref["mz:sync"]) to receive a value
through the channel. The channel can be used with @scheme[channel-put]
or through the result of @scheme[channel-put-evt] to send a value
through the channel.}
@defproc[(channel? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a channel created by
@scheme[make-channel], @scheme[#f] otherwise.}
@defproc[(channel-get [ch channel?]) any]{
Blocks until a sender is ready to provide a value through
@scheme[ch]. The result is the sent value.}
@defproc[(channel-try-get [ch channel?]) any]{
Receives and returns a value from @scheme[ch] if a sender is
immediately ready, otherwise returns @scheme[#f].}
@defproc[(channel-put [ch channel?][v any/c]) void?]{
Blocks until a receiver is ready to accept the value @scheme[v]
through @scheme[ch].}
@defproc[(channel-put-evt [ch channel?][v any/c]) evt?]{
Returns a fresh @tech{synchronizable event} for use with
@scheme[sync]. The event is ready when @scheme[(channel-put ch v)]
would not block, and the event's synchronization result is the event
itself.}

View File

@ -0,0 +1,21 @@
#reader(lib "docreader.ss" "scribble")
@require[(lib "bnf.ss" "scribble")]
@require["mz.ss"]
@title[#:tag "mz:concurrency" #:style 'toc]{Concurrency}
PLT Scheme supports multiple threads of control within a
program. Threads run concurrently, in the sense that one thread can
preempt another without its cooperation, but threads currently all run
on the same processor (i.e., the same underlying OS process and
thread).
@local-table-of-contents[]
@;------------------------------------------------------------------------
@include-section["threads.scrbl"]
@include-section["sync.scrbl"]
@include-section["thread-local.scrbl"]

View File

@ -0,0 +1,168 @@
#reader(lib "docreader.ss" "scribble")
@require[(lib "struct.ss" "scribble")]
@require["mz.ss"]
@define[(cont n) (make-element "schemevariable"
(list "C" (make-element 'subscript (list (format "~a" n)))))]
@title[#:tag "mz:contmarks"]{Continuation Marks}
See @secref["mz:mark-model"] and @secref["mz:prompt-model"] for
general information about continuation marks.
The list of continuation marks for a key @scheme[_k] and a continuation
@scheme[_C] that extends @cont[0] is defined as follows:
%
@itemize{
@item{If @scheme[_C] is an empty continuation, then the mark list is
@scheme[null].}
@item{If @scheme[_C]'s first frame contains a mark @scheme[_m] for @scheme[_k],
then the mark list for @scheme[_C] is @scheme[(cons @scheme[_m] _lst)],
where @scheme[_lst] is the mark list for @scheme[_k] in @cont[0].}
@item{If @scheme[_C]'s first frame does not contain a mark keyed by
@scheme[_k], then the mark list for @scheme[_C] is the mark list for
@cont[0].}
}
The @scheme[with-continuation-mark] form installs a mark on the first
frame of the current continuation (see @secref["mz:wcm"]). Procedures
such as @scheme[current-continuation-marks] allow inspection of marks.
Whenever Scheme creates an exception record for a primitive exception,
it fills the @scheme[continuation-marks] field with the value of
@scheme[(current-continuation-marks)], thus providing a snapshot of
the continuation marks at the time of the exception.
When a continuation procedure returned by
@scheme[call-with-current-continuation] or
@scheme[call-with-composable-continuation] is invoked, it restores the
captured continuation, and also restores the marks in the
continuation's frames to the marks that were present when
@scheme[call-with-current-continuation] or
@scheme[call-with-composable-continuation] was invoked.
@defproc[(continuation-marks [cont continuation?]
[prompt-tag prompt-tag? (default-continuation-prompt-tag)])
continuation-mark-set?]{
Returns an opaque value containing the set of continuation marks for
all keys in the continuation @scheme[cont] up to the prompt tagged by
@scheme[prompt-tag]. If @scheme[cont] is an escape continuation (see
@secref["mz:prompt-model"]), then the current continuation must extend
@scheme[cont], or the @exnraise[exn:fail:contract]. If @scheme[cont]
was not captured with respect to @scheme[prompt-tag] and does not
include a prompt for @scheme[prompt-tag], the
@exnraise[exn:fail:contract].}
@defproc[(current-continuation-marks [prompt-tag prompt-tag? (default-continuation-prompt-tag)])
continuation-mark-set?]{
Returns an opaque value containing the set of continuation marks for
all keys in the current continuation up to @scheme[prompt-tag]. In
other words, it produces the same value as
@schemeblock[
(call-with-current-continuation
(lambda (k)
(continuation-marks k prompt-tag))
prompt-tag)
]}
@defproc[(continuation-mark-set->list
[mark-set continuation-mark-set?]
[key-v any/c]
[prompt-tag prompt-tag? (default-continuation-prompt-tag)])
list?]{
Returns a newly-created list containing the marks for @scheme[key-v]
in @scheme[mark-set], which is a set of marks returned by
@scheme[current-continuation-marks]. The result list is truncated at
the first point, if any, where continuation frames were originally
separated by a prompt tagged with @scheme[prompt-tag]..}
@defproc[(continuation-mark-set->list*
[mark-set continuation-mark-set?]
[key-v any/c]
[none-v any/c #f]
[prompt-tag prompt-tag? (default-continuation-prompt-tag)])
(listof vector?)]{
Returns a newly-created list containing vectors of marks in
@scheme[mark-set] for the keys in @scheme[key-list], up to
@scheme[prompt-tag]. The length of each vector in the result list is
the same as the length of @scheme[key-list], and a value in a
particular vector position is the value for the corresponding key in
@scheme[key-list]. Values for multiple keys appear in a single vector
only when the marks are for the same continuation frame in
@scheme[mark-set]. The @scheme[none-v] argument is used for vector
elements to indicate the lack of a value.}
@defproc[(continuation-mark-set-first
[mark-set (or/c continuation-mark-set? false/c)]
[key-v any/c]
[prompt-tag prompt-tag? (default-continuation-prompt-tag)])
any]{
Returns the first element of the list that would be returned by
@scheme[(continuation-mark-set->list (or mark-set
(current-continuation-marks prompt-tag)) key-v prompt-tag)], or
@scheme[#f] if the result would be the empty list. Typically, this
result can be computed more quickly using
@scheme[continuation-mark-set-first].}
@defproc[(continuation-mark-set? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a mark set created by
@scheme[continuation-marks] or @scheme[current-continuation-marks],
@scheme[#f] otherwise.}
@defproc[(continuation-mark-set->context
[mark-set continuation-mark-set?])
list?]{
Returns a list representing a ``stack trace'' for @scheme[mark-set]'s
continuation. The list contains pairs, where the @scheme[car] of each
pair contains either @scheme[#f] or a symbol for a procedure name, and
the @scheme[cdr] of each pair contains either @scheme[#f] or a
@scheme[srcloc] value for the procedure's source location (see
@secref["mz:linecol"]); the @scheme[car] and @scheme[cdr] are never
both @scheme[#f].
The stack-trace list is the result of
@scheme[continuation-mark-set->list] with @scheme[mark-set] and
Scheme's private key for procedure-call marks. A stack trace is
extracted from an exception and displayed by the default error display
handler (see @secref["mz:exnsandflow"]) for exceptions other than
@scheme[exn:fail:user] (see @scheme[raise-user-error] in
@secref["mz:errorproc"]).}
@examples[
(define (extract-current-continuation-marks key)
(continuation-mark-set->list
(current-continuation-marks)
key))
(with-continuation-mark 'key 'mark
(extract-current-continuation-marks 'key))
(with-continuation-mark 'key1 'mark1
(with-continuation-mark 'key2 'mark2
(list
(extract-current-continuation-marks 'key1)
(extract-current-continuation-marks 'key2))))
(with-continuation-mark 'key 'mark1
(with-continuation-mark 'key 'mark2 (code:comment @t{replaces the previous mark})
(extract-current-continuation-marks 'key)))
(with-continuation-mark 'key 'mark1
(list (code:comment @t{continuation extended to evaluate the argument})
(with-continuation-mark 'key 'mark2
(extract-current-continuation-marks 'key))))
(let loop ([n 1000])
(if (zero? n)
(extract-current-continuation-marks 'key)
(with-continuation-mark 'key n
(loop (sub1 n)))))
]

View File

@ -0,0 +1,341 @@
#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]
@title[#:tag "mz:cont"]{Continuations}
See @secref["mz:cont-model"] and @secref["mz:prompt-model"] for
general information about continuations. PLT Scheme's support for
prompts and composable continuations most closely resembles Dorai
Sitaram's @scheme[\%] and @scheme[fcontrol] operator @cite[#:key
"cite:fcontrol" #:title "Handling Control" #:author "Dorai Sitaram"
#:location "Programming Language Design and Implementation" #:date
1993].
Scheme installs a @defterm{continuation barrier} around evaluation in
the following contexts, preventing full-continuation jumps across the
barrier:
@itemize{
@item{applying an exception handler, an error escape handler, or an
error display handler (see @secref["mz:exns"]);}
@item{applying a macro transformer (see @secref["mz:stxtrans"]),
evaluating a compile-time expression, or applying a module name
resolver (see @secref["mz:modnameresolver"]);}
@item{applying a custom-port procedure (see @secref["mz:customport"]), an
event guard procedure (see @secref["mz:sync"]), or a parameter guard
procedure (see @secref["mz:parameters"]);}
@item{applying a security-guard procedure (see
@secref["mz:securityguards"]);}
@item{applying a will procedure (see @secref["mz:willexecutor"]); or}
@item{evaluating or loading code from the stand-alone MzScheme
command line (see @secref["mz:running-sa"]).}
}
In addition, extensions of PLT Scheme may install barriers in
additional contexts. In particular, MrEd installs a continuation
barrier around most every callback. Finally,
@scheme[call-with-continuation-barrier] applies a thunk barrier
between the application and the current continuation.
@defproc[(call-with-continuation-prompt
[thunk (-> any)]
[prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)]
[handler (or/c procedure? false/c) #f])
any]{
Calls @scheme[thunk] with the current continuation extended by a
prompt. The prompt is tagged by @scheme[prompt-tag], which must be a
result from either @scheme[default-continuation-prompt-tag] (the
default) or @scheme[make-continuation-prompt-tag]. The result of
@scheme[thunk] is the result of the
@scheme[call-with-continuation-prompt] call.
The @scheme[handler] argument specifies a handler procedure to be
called in tail position with repsect to the
@scheme[call-with-continuation-prompt] call when the installed prompt
is the target of a @scheme[abort-current-continuation] call with
@scheme[prompt-tag]; the remaining arguments of
@scheme[abort-current-continuation] are supplied to the handler
procedure. If @scheme[handler] is @scheme[#f], the default handler
accepts a single @scheme[abort-thunk] argument and calls
@scheme[(call-with-continuation-prompt abort-thunk prompt-tag #f)];
that is, the default handler re-installs the prompt and continues with
a given thunk.}
@defproc[(abort-current-continuation
[prompt-tag any/c]
[v any/c] ...+)
any]{
Resets the current continuation to that of the nearest prompt tagged
by @scheme[prompt-tag] in the current continuation; if no such prompt exists,
the @exnraise[exn:fail:contract:continuation]. The @scheme[v]s are delivered
as arguments to the target prompt's handler procedure.
The protocol for @scheme[v]s supplied to an abort is specific to the
@scheme[prompt-tag]. When @scheme[abort-current-continuation] is used with
@scheme[(default-continuation-prompt-tag)], generally a single thunk
should be supplied that is suitable for use with the default prompt
handler. Similarly, when @scheme[call-with-continuation-prompt] is
used with @scheme[(default-continuation-prompt-tag)], the associated
handler should generally accept a single thunk argument.}
@defproc*[([(make-continuation-prompt-tag) continuation-prompt-tag?]
[(make-continuation-prompt-tag [sym symbol?]) continuation-prompt-tag?])]{
Creates a prompt tag that is not @scheme[equal?] to the result of any
other value (including prior or future results from
@scheme[make-continuation-prompt-tag]). The optional @scheme[sym]
argument, if supplied, is used when printing the prompt tag.}
@defproc[(default-continuation-prompt-tag) continuation-prompt-tag?]{
Returns a constant prompt tag for a which a prompt is installed at the
start of every thread's continuation; the handler for each thread's
initial prompt accepts any number of values and returns. The result of
@scheme[default-continuation-prompt-tag] is the default tag for more
any procedure that accepts a prompt tag.}
@defproc[(call-with-current-continuation
[proc (continuation? . -> . any)]
[prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
any]{
Captures the current continuation up to the nearest prompt tagged by
@scheme[prompt-tag]; if no such prompt exists, the
@exnraise[exn:fail:contract:continuation]. The truncated continuation
includes only continuation marks and @scheme[dynamic-wind] frames
installed since the prompt.
The capture continuation is delivered to @scheme[proc], which is
called in tail position with respect to the
@scheme[call-with-current-continuation] call.
If the continuation argument to @scheme[proc] is ever applied, then it
removes the portion of the current continuation up to the nearest
prompt tagged by @scheme[prompt-tag] (not including the prompt; if no
such prompt exists, the @exnraise[exn:fail:contract:continuation]), or
up to the nearest continuation frame (if any) shared by the current
and captured continuations---whichever is first. While removing
continuation frames, @scheme[dynamic-wind] @scheme[post-thunk]s are
executed. Finally, the (unshared portion of the) captured continuation
is appended to the remaining continuation, applying
@scheme[dynamic-wind] @scheme[pre-thunk]s.
The arguments supplied to an applied procedure become the result
values for the restored continuation. In particular, if multiple
arguments are supplied, then the continuation receives multiple
results.
If, at application time, a continuation barrier appears between the
current continuation and the prompt tagged with @scheme[prompt-tag],
and if the same barrier is not part of the captured continuation, then
the @exnraise[exn:fail:contract:continuation].
A continuation can be invoked from the thread (see
@secref["mz:threads"]) other than the one where it was captured.}
@defproc[(call/cc
[proc (continuation? . -> . any)]
[prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
any]{
The @scheme[call/cc] binding is an alias for @scheme[call-with-current-continuation].
}
@defproc[(call-with-composable-continuation
[proc (continuation? . -> . any)]
[prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
any]{
Similar to @scheme[call-with-current-continuation], but applying
the resulting continuation procedure does not remove any portion of
the current continuation. Instead, application always extends the
current continuation with the captured continuation (without
installing any prompts other than those be captured in the
continuation). When @scheme[call-with-composable-continuation] is
called, if a continuation barrier appears in the continuation before
the closest prompt tagged by @scheme[prompt-tag], the
@exnraise[exn:fail:contract:continuation].}
@defproc[(call-with-escape-continuation
[proc (continuation? . -> . any)]
[prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
any]{
Like @scheme[call-with-current-continuation], but @scheme[proc] is not
called in tail position, and the continuation procedure supplied to
@scheme[proc] can only be called during the dynamic extent of the
@scheme[call-with-escape-continuation] call. A continuation barrier,
however, never prevents the application of the continuation.
Due to the limited applicability of its continuation,
@scheme[call-with-escape-continuation] can be implemented more efficiently
than @scheme[call-with-current-continuation].
A continuation obtained from @scheme[call-with-escape-continuation] is
actually a kind of prompt. Escape continuations are provided mainly
for backward compatibility, since they pre-date general prompts in
MzScheme, and because @scheme[call/ec] is often an easy replacement
for @scheme[call/cc] to improve performance.}
@defproc[(call/ec
[proc (continuation? . -> . any)]
[prompt-tag continuation-prompt-tag? (default-continuation-prompt-tag)])
any]{
The @scheme[call/ec] binding is an alias for @scheme[call-with-escape-continuation].
}
@defform[(let/cc k body ...+)]{
Equivalent to @scheme[(call/cc (lambda (k) body ...))].
}
@defform[(let/ec k body ...+)]{
Equivalent to @scheme[(call/ec (lambda (k) body ...))].
}
@defproc[(call-with-continuation-barrier [thunk (-> any)]) any]{
Applies @scheme[thunk] with a barrier between the application and the
current continuation. The results of @scheme[thunk] are the results of
the @scheme[call-with-continuation-barrier] call.}
@defproc[(continuation-prompt-available?
[prompt-tag continuation-prompt-tag?]
[cont continuation? (call/cc values)])
any]{
Returns @scheme[#t] if @scheme[cont], which must be a continuation,
includes a prompt tagged by @scheme[prompt-tag], @scheme[#f]
otherwise.
}
@defproc[(continuation? [v any/c]) boolean?]{ Return @scheme[#t] if
@scheme[v] is a continuation as produced by
@scheme[call-with-current-continuation],
@scheme[call-with-composable-continuation], or
@scheme[call-with-escape-continuation], @scheme[#f] otherwise.}
@defproc[(continuation-prompt-tag? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a continuation prompt tag as produced by
@scheme[default-continuation-prompt-tag] or @scheme[make-continuation-prompt-tag].}
@defproc[(dynamic-wind [pre-thunk (-> any)]
[value-thunk (-> any)]
[post-thunk (-> any)])
any]{
Applies its three thunk arguments in order. The value of a
@scheme[dynamic-wind] expression is the value returned by
@scheme[value-thunk]. The @scheme[pre-thunk] procedure is invoked
before calling @scheme[value-thunk] and @scheme[post-thunk] is invoked
after @scheme[value-thunk] returns. The special properties of
@scheme[dynamic-wind] are manifest when control jumps into or out of
the @scheme[value-thunk] application (either due to a prompt abort or
a continuation invocation): every time control jumps into the
@scheme[value-thunk] application, @scheme[pre-thunk] is invoked, and
every time control jumps out of @scheme[value-thunk],
@scheme[post-thunk] is invoked. (No special handling is performed for
jumps into or out of the @scheme[pre-thunk] and @scheme[post-thunk]
applications.)
When @scheme[dynamic-wind] calls @scheme[pre-thunk] for normal
evaluation of @scheme[value-thunk], the continuation of the
@scheme[pre-thunk] application calls @scheme[value-thunk] (with
@scheme[dynamic-wind]'s special jump handling) and then
@scheme[post-thunk]. Similarly, the continuation of the
@scheme[post-thunk] application returns the value of the preceding
@scheme[value-thunk] application to the continuation of the entire
@scheme[dynamic-wind] application.
When @scheme[pre-thunk] is called due to a continuation jump, the
continuation of @scheme[pre-thunk]
@itemize{
@item{jumps to a more deeply nested @scheme[pre-thunk], if any, or jumps
to the destination continuation; then}
@item{continues with the context of the @scheme[pre-thunk]'s
@scheme[dynamic-wind] call.}
}
Normally, the second part of this continuation is never reached, due
to a jump in the first part. However, the second part is relevant
because it enables jumps to escape continuations that are contained in
the context of the @scheme[dynamic-wind] call. Furthermore, it means
that the continuation marks (see @secref["mz:contmarks"]) and
parameterization (see @secref["mz:parameters"]) for @scheme[pre-thunk]
correspond to those of the @scheme[dynamic-wind] call that installed
@scheme[pre-thunk]. The @scheme[pre-thunk] call, however, is
@scheme[parameterize-break]ed to disable breaks (see also
@secref["mz:breakhandler"]).
Similarly, when @scheme[post-thunk] is called due to a continuation
jump, the continuation of @scheme[post-thunk] jumps to a less deeply
nested @scheme[post-thunk], if any, or jumps to a @scheme[pre-thunk]
protecting the destination, if any, or jumps to the destination
continuation, then continues from the @scheme[post-thunk]'s
@scheme[dynamic-wind] application. As for @scheme[pre-thunk], the
parameterization of the original @scheme[dynamic-wind] call is
restored for the call, and the call is @scheme[parameterize-break]ed
to disable breaks.
In both cases, the target for a jump is recomputed after each
@scheme[pre-thunk] or @scheme[post-thunk] completes. When a
prompt-delimited continuation (see @secref["mz:prompts"]) is captured
in a @scheme[post-thunk], it might be delimited and instantiated in
such a way that the target of a jump turns out to be different when
the continuation is applied than when the continuation was
captured. There may even be no appropriate target, if a relevant
prompt or escape continuation is not in the continuation after the
restore; in that case, the first step in a @scheme[pre-thunk] or
@scheme[post-thunk]'s continuation can raise an exception.
@examples[
(let ([v (let/ec out
(dynamic-wind
(lambda () (display "in "))
(lambda ()
(display "pre ")
(display (call/cc out))
#f)
(lambda () (display "out "))))])
(when v (v "post ")))
(let/ec k0
(let/ec k1
(dynamic-wind
void
(lambda () (k0 'cancel))
(lambda () (k1 'cancel-canceled)))))
(let* ([x (make-parameter 0)]
[l null]
[add (lambda (a b)
(set! l (append l (list (cons a b)))))])
(let ([k (parameterize ([x 5])
(dynamic-wind
(lambda () (add 1 (x)))
(lambda () (parameterize ([x 6])
(let ([k+e (let/cc k (cons k void))])
(add 2 (x))
((cdr k+e))
(car k+e))))
(lambda () (add 3 (x)))))])
(parameterize ([x 7])
(let/cc esc
(k (cons void esc)))))
l)
]}

View File

@ -0,0 +1,11 @@
#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]
@title[#:tag "mz:control" #:style 'toc]{Control Flow}
@local-table-of-contents[]
@include-section["exns.scrbl"]
@include-section["cont.scrbl"]
@include-section["cont-marks.scrbl"]
@include-section["breaks.scrbl"]

View File

@ -5,7 +5,7 @@
@title[#:tag "mz:custodians"]{Custodians}
See @secref["mz:custodian-model"] for basic information on the PLT
Scheme custodian parameter model.
Scheme custodian model.
@defproc[(make-custodian [cust custodian? (current-custodian)]) custodian?]{

View File

@ -0,0 +1,370 @@
#reader(lib "docreader.ss" "scribble")
@require[(lib "struct.ss" "scribble")]
@require["mz.ss"]
@define[(ResultItself x)
(make-element #f (list "The "
(tech "synchronization result")
" of " x " is " x " itself"))]
@title[#:tag "mz:sync"]{Events}
@index['("select")]{@index['("poll")]{A}} @deftech{synchronizable
event} (or just @defterm{event} for short) works with the
@scheme[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 an point in time, an event is either @defterm{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 @scheme[semaphore-wait]. For most kinds of
events, however (such as a port), synchronizing does not modify the
event's state.
The following act as events in stand-alone MzScheme. An extension or
embedding application can extend the set of primitive events --- in
particular, an eventspace in MrEd is an event --- and new structure
types can generate events (see @secref["mz:evt-structs"]).
@itemize{
@item{@scheme[semaphore] --- a semaphore is ready when
@scheme[semaphore-wait] (see @secref["mz:semaphore"]) would not
block. @ResultItself{semaphore}.}
@item{@scheme[semaphore-peek] --- a semaphore-peek event returned by
@scheme[semaphore-peek-evt] applied to @scheme[semaphore] (see
@secref["mz:semaphore"]) is ready exactly when @scheme[semaphore] is
ready. @ResultItself{semaphore-peek}.}
@item{@scheme[channel] --- a channel returned by @scheme[make-channel]
is ready when @scheme[channel-get] would not block (see
@secref["mz:channel"]). The channel's result as an event is the same as
the @scheme[channel-get] result.}
@item{@scheme[channel-put] --- an event returned by
@scheme[channel-put-evt] applied to @scheme[channel] is ready when
@scheme[channel-put] would not block on @scheme[channel] (see
@secref["mz:channel"]). @ResultItself{channel-put}.}
@item{@scheme[input-port] --- an input port is ready as an event when
@scheme[read-byte] would not block. @ResultItself{input-port}.}
@item{@scheme[output-port] --- an output port is ready when
@scheme[write-bytes-avail] would not block (see @secref["mz:write"]) or
when the port contains buffered characters and
@scheme[write-bytes-avail*] can flush part of the buffer (although
@scheme[write-bytes-avail] might block). @ResultItself{output-port}.}
@item{@scheme[progress] --- an event produced by
@scheme[port-progress-evt] applied to @scheme[input-port] is ready after
any subsequent read from @scheme[input-port]. @ResultItself{progress}.}
@item{@scheme[tcp-listener] --- a TCP listener is ready when
@scheme[tcp-accept] (see @secref["mz:tcp"]) would not block.
@ResultItself{listener}.}
@item{@scheme[thd] --- a thread is ready when @scheme[thread-wait]
(see @secref["mz:threadsync"]) would not block. @ResultItself{thread}.}
@item{@scheme[thread-dead] --- an event returned by
@scheme[thread-dead-evt] (see @secref["mz:threadsync"]) applied to
@scheme[thd] is ready when @scheme[thd] has terminated.
@ResultItself{thread-dead}.}
@item{@scheme[thread-resume] --- an event returned by
@scheme[thread-resume-evt] (see @secref["mz:threadsync"]) applied to
@scheme[thd] is ready when @scheme[thd] subsequently resumes
execution (if it was not already running). The event's result is
@scheme[thd].}
@item{@scheme[thread-suspend] --- an event returned by
@scheme[thread-suspend-evt] (see @secref["mz:threadsync"]) applied to
@scheme[thd] is ready when @scheme[thd] subsequently suspends
execution (if it was not already suspended). The event's result is
@scheme[thd].}
@item{@scheme[alarm] --- an event returned by @scheme[alarm-evt] is
ready after a particular date and time. @ResultItself{alarm}.}
@item{@scheme[subprocess] --- a subprocess is ready when
@scheme[subprocess-wait] (see @secref["mz:subprocess"]) would not block.
@ResultItself{subprocess}.}
@item{@scheme[will-executor] --- a will executor is ready when
@scheme[will-execute] (see @secref["mz:willexecutor"]) would not block.
@ResultItself{will-executor}.}
@item{@scheme[udp] --- an event returned by @scheme[udp-send-evt] or
@scheme[udp-receive!-evt] (see @secref["mz:udp"]) is ready when a send or
receive on the original socket would block, respectively. @ResultItself{udp}.}
@item{@scheme[choice] --- an event returned by @scheme[choice-evt] (see
below) is ready when one or more of the @scheme[evt]s supplied to
@scheme[chocie-evt] are ready. If the choice event is chosen, one of
its ready @scheme[evt]s is chosen pseudo-randomly, and the result is the
chosen @scheme[evt]'s result.}
@item{@scheme[wrap] --- an event returned by @scheme[wrap-evt]
applied to @scheme[evt] and @scheme[proc] is ready when @scheme[evt] is
ready. The event's result is obtained by a call to @scheme[proc] (with
breaks disabled) on the result of @scheme[evt].}
@item{@scheme[handle] --- an event returned by @scheme[handle-evt]
applied to @scheme[evt] and @scheme[proc] is ready when @scheme[evt] is
ready. The event's result is obtained by a tail call to @scheme[proc] on
the result of @scheme[evt].}
@item{@elemtag["mz:guard-evt"]{@scheme[guard]} --- an event returned by @scheme[guard-evt] applied
to @scheme[thunk] generates a new event every time that @scheme[guard] is
used with @scheme[sync] (or whenever it is part of a choice event
used with @scheme[sync], etc.); the generated event is the result of
calling @scheme[thunk] when the synchronization begins; if @scheme[thunk]
returns a non-event, then @scheme[thunk]'s result is replaced with an
event that is ready and whose result is @scheme[guard].}
@item{@elemtag["mz:nack-guard-evt"]{@scheme[nack-guard]} --- an event
returned by @scheme[nack-guard-evt] applied to @scheme[proc]
generates a new event every time that @scheme[nack-guard] is used
with @scheme[sync] (or whenever it is part of a choice event used
with @scheme[sync], etc.); the generated event is the result of
calling @scheme[proc] with a NACK (``negative acknowledgment'') event
when the synchronization begins; if @scheme[proc] returns a
non-event, then @scheme[proc]'s result is replaced with an event that
is ready and whose result is @scheme[nack-guard].
If the event from @scheme[proc] is not ultimately chosen as the
unblocked event, then the NACK event supplied to @scheme[proc]
becomes ready with a @|void-const| value. This NACK event becomes ready
when the event is abandoned because some other event is chosen,
because the synchronizing thread is dead, or because control escaped
from the call to @scheme[sync] (even if @scheme[nack-guard]'s @scheme[proc]
has not yet returned a value). If the event returned by @scheme[proc] is
chosen, then the NACK event never becomes ready.}
@item{@elemtag["mz:poll-guard-evt"]{@scheme[poll-guard]} --- an event
returned by @scheme[poll-guard-evt] applied to @scheme[proc]
generates a new event every time that @scheme[poll-guard] is used
with @scheme[sync] (or whenever it is part of a choice event used
with @scheme[sync], etc.); the generated event is the result of
calling @scheme[proc] with a boolean: @scheme[#t] if the event will
be used for a poll, @scheme[#f] for a blocking synchronization.
If @scheme[#t] is supplied to @scheme[proc], if breaks are disabled, if
the polling thread is not terminated, and if polling the resulting
event produces a result, the event will certainly be chosen for its
result.}
@item{@scheme[struct] --- a structure whose type has the
@scheme[prop:evt] property identifies/generates an event through
the property; see @secref["mz:evt-structs"] for further information.}
@item{@scheme[always-evt] --- a constant event that is always
ready. @ResultItself{@scheme[always-evt]}.}
@item{@scheme[never-evt] --- a constant event that is never ready.}
@item{@elemtag["mz:system-idle-evt"]{@scheme[idle]} --- an event
produced by @scheme[system-idle-evt] is ready when, if this event
were replaced by @scheme[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
event's result is @|void-const|.}
}
@;------------------------------------------------------------------------
@defproc[(sync [evt evt?] ...+) any]{
Blocks as long as none of the @tech{synchronizable events}
@scheme[evt]s are ready, as defined above.
When at least one @scheme[evt] is ready, its @tech{synchronization
result} (often @scheme[evt] itself) is returned. If multiple
@scheme[evt]s are ready, one of the @scheme[evt]s is chosen
pseudo-randomly for the result; the
@scheme[current-evt-pseudo-random-generator] parameter sets the
random-number generator that controls this choice.}
@defproc[(sync/timeout [timeout-secs (or/c nonnegative-number? false/c)]
[evt evt?] ...+)
any]{
Like @scheme[sync], but returns @scheme[#f] if @scheme[timeout-secs]
is not @scheme[#f] and if @scheme[timeout-secs] seconds pass without a
successful synchronization.
If @scheme[timeout-secs] is @scheme[0], each @scheme[evt] is checked
at least once, so a @scheme[timeout-secs] value of @scheme[0] can be
used for polling.
See also @scheme[alarm-evt] for an alternative timeout mechanism.}
@defproc[(sync/enable-break [evt evt?] ...+) any]{
Like @scheme[sync], but breaking is enabled (see
@secref["mz:breakhandler"]) while waiting on the @scheme[evt]s. If
breaking is disabled when @scheme[sync/enable-break] is called, then
either all @scheme[evt]s remain unchosen or the @scheme[exn:break]
exception is raised, but not both.}
@defproc[(sync/timeout/enable-break [timeout-secs (or/c nonnegative-number? false/c)]
[evt evt?] ...+)
any]{
Like @scheme[sync/enable-break], but with a timeout in seconds (or
@scheme[#f]), as for @scheme[sync/timeout].}
@defproc[(choice-evt [evt evt?] ...) evt?]{
Creates and returns a single event that combines the
@scheme[evt]s. Supplying the result to @scheme[sync] is the same as
supplying each @scheme[evt] to the same call.}
@defproc[(wrap-evt [evt (and/c evt? (not/c handle-evt?))]
[wrap (any/c . -> . any)])
evt?]{
Creates an event that is in a ready when @scheme[evt] is ready, but
whose result is determined by applying @scheme[wrap] to the result of
@scheme[evt]. The call to @scheme[wrap] is
@scheme[parameterize-break]ed to disable breaks initially. The
@scheme[evt] cannot be an event created by @scheme[handle-evt] or any
combination of @scheme[choice-evt] involving an event from
@scheme[handle-evt].}
@defproc[(handle-evt [evt (and/c evt? (not/c handle-evt?))]
[handle (any/c . -> . any)])
evt?]{
Like @scheme[wrap], except that @scheme[handle] is called in tail
position with respect to the synchronization request, and without
breaks explicitly disabled.}
@defproc[(guard-evt [generator (-> evt?)]) evt?]{
Creates a value that behaves as an event, but that is actually an
event generator. For details, see @elemref["mz:guard-evt"]{the
overview}.}
@defproc[(nack-guard-evt [generator (evt? . -> . evt?)]) evt?]{
Creates a value that behaves as an event, but that is actually an
event generator; the generator procedure receives an event that
becomes ready with a @|void-const| value if the generated event was
not ultimately chosen. For details, see
@elemref["mz:nack-guard-evt"]{the overview}.}
@defproc[(poll-guard-evt [generator (boolean? . -> . evt?)]) evt?]{
Creates a value that behaves as an event, but that is actually an
event generator; the generator procedure receives a boolean indicating
whether the event is used for polling. For details, see
@elemref["mz:poll-guard-evt"]{the overview}.}
@defthing[always-evt evt?]{A constant event that is always ready, with
itself as its result.}
@defthing[never-evt evt?]{A constant event that is never ready.}
@defproc[(system-idle-evt) evt?]{Returns an event that is ready when
the system is otherwise idle; see @elemref["mz:system-idle-evt"]{the
overview} for more information. The result of the
@scheme[system-idle-evt] procedure is always the same event.}
@defproc[(alarm-evt [msecs nonnegative-number?]) evt]{
Returns a synchronizable event that is not ready when
@scheme[(current-inexact-milliseconds)] would return a value that is
less than @scheme[msecs], and it is ready when
@scheme[(current-inexact-milliseconds)] would return a value that is
more than @scheme[msecs].}
@defproc[(evt? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a @tech{synchronizable event},
@scheme[#f] otherwise.}
@defproc[(handle-evt? [evt evt?]) boolean?]{
Returns @scheme[#t] if @scheme[evt] was created by @scheme[handle-evt]
or by @scheme[choice-evt] applied to another event for which
@scheme[handle-evt?] produces @scheme[#t]. Such events are illegal as
an argument to @scheme[handle-evt] or @scheme[wrap-evt], because they
cannot be wrapped further. For any other event, @scheme[handle-evt?]
produces @scheme[#f], and the event is a legal argument to
@scheme[handle-evt] or @scheme[wrap-evt] for further wrapping.}
@;------------------------------------------------------------------------
@defthing[prop:evt struct-type-property?]{
A @tech{structure type property} that identifies structure types whose
instances can serve as synchronizable events. The property value can
be any of the following:
@itemize{
@item{An event @scheme[evt]: In this case, using the structure as an
event is equivalent to using @scheme[evt].}
@item{A procedure @scheme[proc] of one argument: In this case, the
structure is similar to an event generated
by @scheme[guard-evt], except that the would-be guard
procedure @scheme[proc] receives the structure as an argument, instead
of no arguments.}
@item{An exact, non-negative integer between @scheme[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.}
}
Instances of a structure type with the @scheme[prop:input-port] or
@scheme[prop:output-port] property (see @secref["mz:portstructs"]) are
also synchronizable by virtue of being a port. If the structure type
has more than one of @scheme[prop:evt], @scheme[prop:input-port], and
@scheme[prop:output-port], then the @scheme[prop:evt] value (if any)
takes precedence for determing the instance's behavior as an event,
and the @scheme[prop:input-port] property takes precedence over
@scheme[prop:output-port] for synchronization.
@examples[
(define-struct wt ([base #:immutable] 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)
]}

View File

@ -48,7 +48,7 @@ expression that evaluation simplifies no further, such as the number
@scheme[2].
@;------------------------------------------------------------------------
@section{Sub-expression Evaluation and Continuations}
@section[#:tag "mz:cont-model"]{Sub-expression Evaluation and Continuations}
Some simplifications require more than one step. For example:
@ -581,7 +581,7 @@ re-declared, each re-declaration of the module is immediately
@tech{instantiate}d in the same @tech{phase}s.
@;------------------------------------------------------------------------
@section{Continuation Frames and Marks}
@section[#:tag "mz:mark-model"]{Continuation Frames and Marks}
Every continuation @scheme[_C] can be partitioned into
@deftech{continuation frames} @frame[1], @frame[2], ..., @frame["n"]
@ -600,7 +600,7 @@ for a ``stack trace'' to be used when an exception is raised, or
to implement dynamic scope.
@;------------------------------------------------------------------------
@section{Prompts and Delimited Continuations}
@section[#:tag "mz:prompt-model"]{Prompts, Delimited Continuations, and Barriers}
A @deftech{prompt} is a special kind of continuation frame that is
annotated with a specific @deftech{prompt-tag} (essentially a
@ -615,6 +615,22 @@ a particular tag, or replace the continuation to the nearest enclosing
prompt with another one. When a delimited continuation is captured,
the marks associated with the relevant frames are also captured.
A @deftech{continuation barrier} is another kind of continuation frame
that prohibits certain replacements of the current continuation with
another. Specifically, while an abort is allowed to remove a portion
of the continuation containing a prompt, the continuation can be
replaced by another only when the replacement also includes the
continuation barrier. Certain operations install barriers
automatically; in particular, when an exception handler is called, a
continuation barrier prohibits the continuation of the handler from
capturing the continuation past the exception point.
A @deftech{escape continuation} is essentially a derived concept. It
combines a prompt for escape purposes with a continuation for
mark-gathering purposes. as the name implies, escape continuations are
used only to abort to the point of capture, which means that
escape-continuation aborts can cross continuation barriers.
@;------------------------------------------------------------------------
@section[#:tag "mz:thread-model"]{Threads}

View File

@ -0,0 +1,156 @@
#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]
@title[#:tag "mz:parameters"]{Parameters}
See @secref["mz:parameter-model"] for basic information on the
parameter model. Parameters correspond to @defterm{preserved thread
fluids} in \first{Scsh} @cite[#:key "cite:thread-fluids" #:title
"Processes vs. User-Level Threads in Scsh" #:author "Martin Gasbichler
and Michael Sperber" #:date 2002 #:location "Scheme Workshop"].
To parameterize code in a thread- and continuation-friendly manner,
use @scheme[parameterize]. The @scheme[parameterize] form introduces a
fresh @tech{thread cell} for the dynamic extent of its body
expressions.
When a new thread is created, the @tech{parameterization} for the new
thread's initial continuation is the @tech{parameterization} of the
creator thread. Since each parameter's @tech{thread cell} is
@tech{preserved}, the new thread ``inherits'' the parameter values of
its creating thread. When a continuation is moved from one thread to
another, settings introduced with @scheme[parameterize] effectively
move with the continuation.
In contrast, direct assignment to a parameter (by calling the
parameter procedure with a value) changes the value in a thread cell,
and therefore changes the setting only for the current
thread. Consequently, as far as the memory manager is concerned, the
value originally associated with a parameter through
@scheme[parameterize] remains reachable as long the continuation is
reachable, even if the parameter is mutated.
@defproc[(make-parameter [v any/c]
[guard (or/c (any/c . -> . any) false/c) #f])
parameter?]{
Returns a new parameter procedure. The value of the parameter is
initialized to @scheme[v] in all threads. If @scheme[guard] is
supplied, it is used as the parameter's guard procedure. A guard
procedure takes one argument. Whenever the parameter procedure is
applied to an argument, the argument is passed on to the guard
procedure. The result returned by the guard procedure is used as the
new parameter value. A guard procedure can raise an exception to
reject a change to the parameter's value. The @scheme[guard] is not
applied to the initial @scheme[v].}
@defform[(parameterize ((parameter-expr value-expr) ...)
body ...+)]{
The result of a @scheme[parameterize] expression is the result of the
last @scheme[body]. The @scheme[parameter-expr]s determine the
parameters to set, and the @scheme[value-expr]s determine the
corresponding values to install while evaluating the
@scheme[body-expr]s. All of the @scheme[parameter-expr]s are evaluated
first (and checked with @scheme[parameter?]), then all
@scheme[value-expr]s are evaluated, and then the parameters are bound
in the continuation to preserved thread cells that contain the values
of the @scheme[value-expr]s. The last @scheme[body-expr] is in tail
position with respect to the entire @scheme[parameterize] form.
Outside the dynamic extent of a @scheme[parameterize] expression,
parameters remain bound to other thread cells. Effectively, therefore,
old parameters settings are restored as control exits the
@scheme[parameterize] expression.
If a continuation is captured during the evaluation of
@scheme[parameterize], invoking the continuation effectively
re-introduces the @tech{parameterization}, since a parameterization is
associated to a continuation via a continuation mark (see
@secref["mz:contmarks"]) using a private key.}
@examples[
(parameterize ([exit-handler (lambda (x) 'no-exit)])
(exit))
(define p1 (make-parameter 1))
(define p2 (make-parameter 2))
(parameterize ([p1 3]
[p2 (p1)])
(cons (p1) (p2)))
(let ([k (let/cc out
(parameterize ([p1 2])
(p1 3)
(cons (let/cc k
(out k))
(p1))))])
(if (procedure? k)
(k (p1))
k))
(define ch (make-channel))
(parameterize ([p1 0])
(thread (lambda ()
(channel-put ch (cons (p1) (p2))))))
(channel-get ch)
(define k-ch (make-channel))
(define (send-k)
(parameterize ([p1 0])
(thread (lambda ()
(let/ec esc
(channel-put ch
((let/cc k
(channel-put k-ch k)
(esc)))))))))
(send-k)
(thread (lambda () ((channel-get k-ch)
(let ([v (p1)])
(lambda () v)))))
(channel-get ch)
(send-k)
(thread (lambda () ((channel-get k-ch) p1)))
(channel-get ch)
]
@defform[(parameterize* ((parameter-expr value-expr) ...)
body ...+)]{
Analogous to @scheme[let*] compared to @scheme[let], @scheme[parameterize*]
is the same as a nested series of single-parameter @scheme[parameterize]
forms.}
@defproc[(make-derived-parameter [v any/c]
[guard (any/c . -> . any)])
parameter?]{
Returns a parameter procedure that sets or retrieves the same value as
@scheme[parameter], but with the additional guard @scheme[guard]
applied (before any guard associated with @scheme[parameter]).}
@defproc[(parameter? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a parameter procedure,
@scheme[#f] otherwise.}
@defproc[(parameter-procedure=? [a parameter?][b parameter?]) boolean?]{
Returns @scheme[#t] if the parameter procedures @scheme[a] and
@scheme[b] always modify the same parameter with the same guards,
@scheme[#f] otherwise.}
@defproc[(current-parameterization) parameterization?]{Returns the
current continuation's @tech{parameterization}.}
@defproc[(call-with-parameterization [parameterization parameterization?]
[thunk (-> any)])
any]{
Calls @scheme[thunk] (via a tail call) with @scheme[parameterization]
as the current @tech{parameterization}.}
@defproc[(parameterization? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a @tech{parameterization}
returned by @scheme[current-parameterization], @scheme[#f] otherwise.}

View File

@ -146,3 +146,98 @@ obtains its result frmo @scheme[plain-proc].
@defstruct[arity-at-least ([value nonnegative-exact-integer?])]{
This structure type is used for the result of @scheme[procedure-arity].}
@defthing[prop:procedure struct-type-property?]{
A @tech{structure type property} to indentify structure types whose
instances can be applied as procedures. In particular, when
@scheme[procedure?] is applied to the instance, the result will be
@scheme[#t], and when an instance is used in the function position of
an application expression, a procedure is extracted from the instance
and used to complete the procedure call.
If the @scheme[prop:procedure] property value is an integer, it
designates a field within the structure that should contain a
procedure. The integer must be between @scheme[0] (inclusive) and the
number of non-automatic fields in the structure type (exclusive, not
counting supertype fields). The designated field must also be
specified as immutable, so that after an instance of the structure is
created, its procedure cannot be changed. (Otherwise, the arity and
name of the instance could change, and such mutations are generally
not allowed for procedures.) When the instance is used as the
procedure in an application expression, the value of the designated
field in the instance is used to complete the procedure call. (This
procedure can be another structure that acts as a procedure; the
immutability of procedure fields disallows cycles in the procedure
graph, so that the procedure call will eventually continue with a
non-structure procedure.) That procedure receives all of the arguments
from the application expression. The procedure's name (see
@secref["mz:infernames"]) and arity (see @secref["mz:arity"]) are also
used for the name and arity of the structure. If the value in the
designated field is not a procedure, then the instance behaves like
@scheme[(case-lambda)] (i.e., a procedure which does not accept any
number of arguments).
Providing an integer @scheme[proc-spec] argument to
@scheme[make-struct-type] is the same as both supplying the value with
the @scheme[prop:procedure] property and designating the field as
immutable (so that a property binding or immutable designation is
redundant and disallowed).
@examples[
(define-struct annotated-proc ([base #:immutable] note)
#:property prop:procedure (struct-field-index base))
(define plus1 (make-annotated-proc
(lambda (x) (+ x 1))
"adds 1 to its argument"))
(procedure? plus1)
(annotated-proc? plus1)
(plus1 10)
(annotated-proc-note plus1)
]
When the @scheme[prop:procedure] value is a procedure, it should
accept at least one argument. When an instance of the structure is
used in an application expression, the property-value procedure is
called with the instance as the first argument. The remaining
arguments to the property-value procedure are the arguments from the
application expression. Thus, if the application expression contained
five arguments, the property-value procedure is called with six
arguments. The name of the instance (see @secref["mz:infernames"]) is
unaffected by the property-value procedure, but the instance's arity
is determined by subtracting one from every possible argument count of
the property-value procedure. If the property-value procedure cannot
accept at least one argument, then the instance behaves like
@scheme[(case-lambda)].
Providing a procedure @scheme[proc-spec] argument to
@scheme[make-struct-type] is the same as supplying the value with the
@scheme[prop:procedure] property (so that a specific property binding
is disallowed).
@examples[
(define-struct fish (weight color)
#:property
prop:procedure
(lambda (f n) (set-fish-weight! f (+ n (fish-weight f)))))
(define wanda (make-fish 12 'red))
(fish? wanda)
(procedure? wanda)
(fish-weight wanda)
(for-each wanda '(1 2 3))
(fish-weight wanda)
]
If a structure type generates procedure instances, then subtypes of
the type also generate procedure instances. The instances behave the
same as instances of the original type. When a @scheme[prop:procedure]
property or non-@scheme[#f] @scheme[proc-spec] is supplied to
@scheme[make-struct-type] with a supertype that already behaves as a
procedure, the @exnraise[exn:fail:contract].}
@defproc[(procedure-struct-type? [type struct-type?]) boolean?]{
Returns @scheme[#t] if instances of the structure type represented by
@scheme[type] are procedures (according to @scheme[procedure?]),
@scheme[#f] otherwise.}

View File

@ -29,8 +29,8 @@ language.
@;------------------------------------------------------------------------
@include-section["regexps.scrbl"]
@include-section["exns.scrbl"]
@include-section["threads.scrbl"]
@include-section["control.scrbl"]
@include-section["concurrency.scrbl"]
@include-section["custodians.scrbl"]
@;------------------------------------------------------------------------

View File

@ -0,0 +1,88 @@
#reader(lib "docreader.ss" "scribble")
@require[(lib "bnf.ss" "scribble")]
@require["mz.ss"]
@title[#:tag "mz:semaphore"]{Semaphores}
A @deftech{semaphore} has an internal counter; when this counter is
zero, the semaphore can block a thread's execution (through
@scheme[semaphore-wait]) until another thread increments the counter
(using @scheme[semaphore-post]). The maximum value for a semaphore's
internal counter is platform-specific, but always at least
@scheme[10000].
A semaphore's counter is updated in a single-threaded manner, so that
semaphores can be used for reliable synchronization. Semaphore waiting
is @defterm{fair}: if a thread is blocked on a semaphore and the
semaphore's internal value is non-zero infinitely often, then the
thread is eventually unblocked.
In addition to its use with semaphore-specific procedures, semaphores
can be used as events; see @secref["mz:sync"].
@defproc[(make-semaphore [init non-negative-exact-integer? 0]) semaphore?]{
Creates and returns a new semaphore with the counter initially set to
@scheme[init]. If @scheme[init-k] is larger than a semaphore's maximum
internal counter value, the @exnraise[exn:fail].}
@defproc[(semaphore? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a semaphore created by
@scheme[make-semaphore], @scheme[#f] otherwise.}
@defproc[(semaphore-post [sema semaphore?]) void?]{Increments the
semaphore's internal counter and returns @|void-const|. If the
semaphore's internal counter has already reached its maximum value,
the @exnraise[exn:fail].}
@defproc[(semaphore-wait [sema semaphore?]) void?]{Blocks until the
internal counter for semaphore @scheme[sema] is non-zero. When the
counter is non-zero, it is decremented and @scheme[semaphore-wait]
returns @|void-const|.}
@defproc[(semaphore-try-wait? [sema semaphore?]) boolean?]{Like
@scheme[semaphore-wait], but @scheme[semaphore-try-wait?] never blocks
execution. If @scheme[sema]'s internal counter is zero,
@scheme[semaphore-try-wait?] returns @scheme[#f] immediately without
decrementing the counter. If @scheme[sema]'s counter is positive, it
is decremented and @scheme[#t] is returned.}
@defproc[(semaphore-wait/enable-break [sema semaphore?]) void?]{Like
@scheme[semaphore-wait], but breaking is enabled (see
@secref["mz:breakhandler"]) while waiting on @scheme[sema]. If
breaking is disabled when @scheme[semaphore-wait/enable-break] is
called, then either the semaphore's counter is decremented or the
@scheme[exn:break] exception is raised, but not both.}
@defproc[(semaphore-peek-evt [sema semaphore?]) evt?]{Creates and
returns a new synchronizable event (for use with @scheme[sync], for
example) that is ready when @scheme[sema] is ready, but synchronizing
the event does not decrement @scheme[sema]'s internal count.}
@defproc[(call-with-semaphore [sema semaphore?]
[proc procedure?]
[try-fail-thunk (or/c (-> any) false/c) #f]
[arg any/c] ...) any]{
Waits on @scheme[sema] using @scheme[semaphore-wait], calls
@scheme[proc] with all @scheme[arg]s, and then posts to
@scheme[sema]. A continuation barrier blocks full continuation jumps
into or out of @scheme[proc] (see @secref["mz:continuations"]), but
escape jumps are allowed, and @scheme[sema] is posted on escape. If
@scheme[try-fail-thunk] is provided and is not @scheme[#f], then
@scheme[semaphore-try-wait?] is called on @scheme[sema] instead of
@scheme[semaphore-wait], and @scheme[try-fail-thunk] is called if the
wait fails.}
@defproc[(call-with-semaphore/enable-break [sema semaphore?]
[proc procedure?]
[try-fail-thunk (or/c (-> any) false/c) #f]
[arg any/c] ...) any]{
Like @scheme[call-with-semaphore], except that
@scheme[semaphore-wait/enable-break] is used with @scheme[sema] in
non-try mode. When @scheme[try-fail-thunk] is provided and not
@scheme[#f], then breaks are enabled around the use of
@scheme[semaphore-try-wait?] on @scheme[sema].}

View File

@ -190,12 +190,12 @@ The result of @scheme[make-struct-type] is five values:
]}
@defproc[(make-struct-field-accessor [accessor-proc struct-accessot-procedure?]
[field-pos-k exact-nonnegative-integer?]
[field-pos exact-nonnegative-integer?]
[field-name symbol?])
procedure?]{
Returns a field accessor that is equivalent to @scheme[(lambda (s)
(accessor-proc s field-pos-k))]. The @scheme[accessor-proc] must be
(accessor-proc s field-pos))]. The @scheme[accessor-proc] must be
an @tech{accessor} returned by @scheme[make-struct-type]. The name of the
resulting procedure for debugging purposes is derived from
@scheme[field-name] and the name of @scheme[accessor-proc]'s
@ -204,12 +204,12 @@ structure type.
For examples, see @scheme[make-struct-type].}
@defproc[(make-struct-field-mutator [mutator-proc struct-mutator-procedure?]
[field-pos-k exact-nonnegative-integer?]
[field-pos exact-nonnegative-integer?]
[field-name symbol?])
procedure?]{
Returns a field mutator that is equivalent to @scheme[(lambda (s v)
(mutator-proc s field-pos-k v))]. The @scheme[mutator-proc] must be
(mutator-proc s field-pos v))]. The @scheme[mutator-proc] must be
a @tech{mutator} returned by @scheme[make-struct-type]. The name of the
resulting procedure for debugging purposes is derived from
@scheme[field-name] and the name of @scheme[mutator-proc]'s
@ -221,7 +221,7 @@ For examples, see @scheme[make-struct-type].}
@;------------------------------------------------------------------------
@section[#:tag "mz:structprops"]{Structure Type Properties}
A @index['("structure type properties")]{@defterm{structure type
A @index['("structure type properties")]{@deftech{structure type
property}} allows per-type information to be associated with a
structure type (as opposed to per-instance information associated
with a structure value). A property value is associated with a

View File

@ -0,0 +1,27 @@
#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]
@title[#:tag "mz:all-sync" #:style 'toc]{Synchronization}
Scheme's synchronization toolbox spans three layers:
@itemize{
@item{@tech{synchronizable events} --- a general framework for
synchronization;}
@item{@tech{channels} --- a primitive that can be used, in principle,
to build most other kinds of synchronizable events (except the ones
that compose events); and}
@item{@tech{semaphores} --- a simple and especially cheap primitive
for synchronization.}
}
@local-table-of-contents[]
@include-section["evts.scrbl"]
@include-section["channels.scrbl"]
@include-section["semaphores.scrbl"]

View File

@ -643,11 +643,21 @@ corresponding value from @scheme[expr] in the same way as for
@section[#:tag "mz:wcm"]{Continuation Marks: @scheme[with-continuation-mark]}
@defform[(with-continuation-mark key-expr val-expr result-expr)]{
Evaluates @scheme[key-expr] and @scheme[val-expr] in order to obtain a key and
value, respectively. The key and value are attached as a mark to the
current continuation frame (see @secref["mz:contmarks"]), and then
@scheme[result-expr] is evaluated in tail position.
}
The @scheme[key-expr], @scheme[mark-expr], and @scheme[result-expr]
expressions are evaluated in order. After @scheme[key-expr] is
evaluated to obtain a key and @scheme[mark-expr] is evaluated to
obtain a mark, the key is mapped to the mark in the current
continuation's initial frame. If the frame already has a mark for the
key, it is replaced. Finally, the @scheme[result-expr] is evaluated;
the continuation for evaluating @scheme[result-expr] is the
continuation of the @scheme[with-continuation-mark] expression (so the
result of the @scheme[resultbody-expr] is the result of the
@scheme[with-continuation-mark] expression, and @scheme[result-expr]
is in tail position for the @scheme[with-continuation-mark]
expression).
@moreref["mz:contmarks"]{continuation marks}}
@;------------------------------------------------------------------------
@section{Syntax Quoting: @scheme[quote-syntax]}

View File

@ -0,0 +1,93 @@
#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]
@title[#:tag "mz:threadcells"]{Thread Cells}
A @deftech{thread cell} contains a thread-specific value; that is, it
contains a specific value for each thread, but it may contain
different values for different threads. A thread cell is created with
a default value that is used for all existing threads. When the cell's
content is changed with @scheme[thread-cell-set!], the cell's value
changes only for the current thread. Similarly,
@scheme[thread-cell-ref] obtains the value of the cell that is
specific to the current thread.
A thread cell's value can be @deftech{preserved}, which means that
when a new thread is created, the cell's initial value for the new
thread is the same as the creating thread's current value. If a thread
cell is non-preserved, then the cell's initial value for a newly
created thread is the default value (which was supplied when the cell
was created).
Within the current thread, the current values of all preserved threads
cells can be captured through
@scheme[current-preserved-thread-cell-values]. The captured set of
values can be imperatively installed into the current thread through
another call to @scheme[current-preserved-thread-cell-values]. The
capturing and restoring threads can be different.
@defproc[(make-thread-cell [v any/c][preserved? any/c #f]) thread-cell?]{
Creates and returns a new thread cell. Initially, @scheme[v] is the
cell's value for all threads. If @scheme[preserved?] is true, then the
cell's initial value for a newly created threads is the creating
thread's value for the cell, otherwise the cell's value is initially
@scheme[v] in all future threads.}
@defproc[(thread-cell? [v any/c]) boolean?]{
Returns @scheme[#t] if @scheme[v] is a thread cell created by
@scheme[make-thread-cell], @scheme[#f] otherwise.}
@defproc[(thread-cell-ref [cell thread-cell?]) any]{Returns the
current value of @scheme[cell] for the current thread.}
@defproc[(thread-cell-set! [cell thread-cell?][v any/c]) any]{Sets the
value in @scheme[cell] to @scheme[v] for the current thread.}
@examples[
(define cnp (make-thread-cell '(nerve) #f))
(define cp (make-thread-cell '(cancer) #t))
(thread-cell-ref cnp)
(thread-cell-ref cp)
(thread-cell-set! cnp '(nerve nerve))
(thread-cell-set! cp '(cancer cancer))
(thread-cell-ref cnp)
(thread-cell-ref cp)
(define ch (make-channel))
(thread (lambda ()
(channel-put ch (thread-cell-ref cnp))
(channel-put ch (thread-cell-ref cp))
(channel-get ch) ; to wait
(channel-put ch (thread-cell-ref cp))))
(channel-get ch)
(channel-get ch)
(thread-cell-set! cp '(cancer cancer cancer))
(thread-cell-ref cp)
(channel-put ch 'ok)
(channel-get ch)
]
@defproc*[([(current-preserved-thread-cell-values) any]
[(current-preserved-thread-cell-values [thread-cell-vals any/c]) void?])]{
When called with no arguments, this procedure produces a
@scheme[thread-cell-vals] that represents the current values (in the
current thread) for all preserved thread cells.
When called with a @scheme[thread-cell-vals] generated by a previous
call to @scheme[current-preserved-thread-cell-values], the values of
all preserved thread cells (in the current thread) are set to the
values captured in @scheme[thread-cell-vals]; if a preserved thread
cell was created after @scheme[thread-cell-vals] was generated, then
the thread cell's value for the current thread reverts to its initial
value.}

View File

@ -0,0 +1,13 @@
#reader(lib "docreader.ss" "scribble")
@require["mz.ss"]
@title[#:tag "mz:thread-local-storage" #:style 'toc]{Thread-Local Storage}
@tech{Thread cells} provides primitive support for thread-local
storage. @tech{Parameters} combine thread cells and continuation marks
to support thread-specific, continuation-specific binding.
@local-table-of-contents[]
@include-section["thread-cells.scrbl"]
@include-section["parameters.scrbl"]

View File

@ -4,8 +4,8 @@
@title[#:tag "mz:threads"]{Threads}
See @secref["mz:thread-model"] and @secref["mz:parameter-model"] for
basic information on the PLT Scheme thread and parameter model.
See @secref["mz: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
@ -61,6 +61,34 @@ 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 "mz:threadkill"]{Suspending, Resuming, and Killing Threads}
@ -130,3 +158,68 @@ 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["mz: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 "mz: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["mz:breakhandler"]).}
@defproc[(thread-dead-evt [thd thread?]) evt?]{
Returns a @tech{synchronizable event} (see @secref["mz: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["mz:gc-model"]).}
@defproc[(thread-resume-evt [thd thread?]) evt?]{
Returns a @tech{synchronizable event} (see @secref["mz: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["mz:gc-model"]).}
@defproc[(thread-suspend-evt [thd thread?]) evt?]{
Returns a @tech{synchronizable event} (see @secref["mz: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].}