diff --git a/collects/scribble/base-render.ss b/collects/scribble/base-render.ss index 66cd2756ed..d47de35db6 100644 --- a/collects/scribble/base-render.ss +++ b/collects/scribble/base-render.ss @@ -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") diff --git a/collects/scribble/manual.ss b/collects/scribble/manual.ss index 521add3b62..a1fa24838d 100644 --- a/collects/scribble/manual.ss +++ b/collects/scribble/manual.ss @@ -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))) diff --git a/collects/scribblings/reference/breaks.scrbl b/collects/scribblings/reference/breaks.scrbl new file mode 100644 index 0000000000..6eb2da104a --- /dev/null +++ b/collects/scribblings/reference/breaks.scrbl @@ -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.} diff --git a/collects/scribblings/reference/channels.scrbl b/collects/scribblings/reference/channels.scrbl new file mode 100644 index 0000000000..86a5cd5e8b --- /dev/null +++ b/collects/scribblings/reference/channels.scrbl @@ -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.} diff --git a/collects/scribblings/reference/concurrency.scrbl b/collects/scribblings/reference/concurrency.scrbl new file mode 100644 index 0000000000..990f4f2555 --- /dev/null +++ b/collects/scribblings/reference/concurrency.scrbl @@ -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"] + + diff --git a/collects/scribblings/reference/cont-marks.scrbl b/collects/scribblings/reference/cont-marks.scrbl new file mode 100644 index 0000000000..62dca3c851 --- /dev/null +++ b/collects/scribblings/reference/cont-marks.scrbl @@ -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))))) +] diff --git a/collects/scribblings/reference/cont.scrbl b/collects/scribblings/reference/cont.scrbl new file mode 100644 index 0000000000..fb19a9ba46 --- /dev/null +++ b/collects/scribblings/reference/cont.scrbl @@ -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) +]} diff --git a/collects/scribblings/reference/control.scrbl b/collects/scribblings/reference/control.scrbl new file mode 100644 index 0000000000..7876e9f3c8 --- /dev/null +++ b/collects/scribblings/reference/control.scrbl @@ -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"] diff --git a/collects/scribblings/reference/custodians.scrbl b/collects/scribblings/reference/custodians.scrbl index aaba12717e..88c088cb66 100644 --- a/collects/scribblings/reference/custodians.scrbl +++ b/collects/scribblings/reference/custodians.scrbl @@ -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?]{ diff --git a/collects/scribblings/reference/evts.scrbl b/collects/scribblings/reference/evts.scrbl new file mode 100644 index 0000000000..10c714b02b --- /dev/null +++ b/collects/scribblings/reference/evts.scrbl @@ -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) +]} diff --git a/collects/scribblings/reference/model.scrbl b/collects/scribblings/reference/model.scrbl index 873caa2935..3402146dac 100644 --- a/collects/scribblings/reference/model.scrbl +++ b/collects/scribblings/reference/model.scrbl @@ -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} diff --git a/collects/scribblings/reference/parameters.scrbl b/collects/scribblings/reference/parameters.scrbl new file mode 100644 index 0000000000..8a58d6a9b5 --- /dev/null +++ b/collects/scribblings/reference/parameters.scrbl @@ -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.} diff --git a/collects/scribblings/reference/procedures.scrbl b/collects/scribblings/reference/procedures.scrbl index 9bdded131d..da45e757b0 100644 --- a/collects/scribblings/reference/procedures.scrbl +++ b/collects/scribblings/reference/procedures.scrbl @@ -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.} + diff --git a/collects/scribblings/reference/reference.scrbl b/collects/scribblings/reference/reference.scrbl index f3e690cbdd..8577f08893 100644 --- a/collects/scribblings/reference/reference.scrbl +++ b/collects/scribblings/reference/reference.scrbl @@ -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"] @;------------------------------------------------------------------------ diff --git a/collects/scribblings/reference/semaphores.scrbl b/collects/scribblings/reference/semaphores.scrbl new file mode 100644 index 0000000000..96c94a1135 --- /dev/null +++ b/collects/scribblings/reference/semaphores.scrbl @@ -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].} diff --git a/collects/scribblings/reference/struct.scrbl b/collects/scribblings/reference/struct.scrbl index 6649ceeff0..0cf73c222c 100644 --- a/collects/scribblings/reference/struct.scrbl +++ b/collects/scribblings/reference/struct.scrbl @@ -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 diff --git a/collects/scribblings/reference/sync.scrbl b/collects/scribblings/reference/sync.scrbl new file mode 100644 index 0000000000..ad90ca95f0 --- /dev/null +++ b/collects/scribblings/reference/sync.scrbl @@ -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"] diff --git a/collects/scribblings/reference/syntax.scrbl b/collects/scribblings/reference/syntax.scrbl index 920cc8427d..572fd3e060 100644 --- a/collects/scribblings/reference/syntax.scrbl +++ b/collects/scribblings/reference/syntax.scrbl @@ -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]} diff --git a/collects/scribblings/reference/thread-cells.scrbl b/collects/scribblings/reference/thread-cells.scrbl new file mode 100644 index 0000000000..b1e6c15c90 --- /dev/null +++ b/collects/scribblings/reference/thread-cells.scrbl @@ -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.} + diff --git a/collects/scribblings/reference/thread-local.scrbl b/collects/scribblings/reference/thread-local.scrbl new file mode 100644 index 0000000000..5b0e18d92f --- /dev/null +++ b/collects/scribblings/reference/thread-local.scrbl @@ -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"] diff --git a/collects/scribblings/reference/threads.scrbl b/collects/scribblings/reference/threads.scrbl index 7f18b9fd63..7bdabb7cb3 100644 --- a/collects/scribblings/reference/threads.scrbl +++ b/collects/scribblings/reference/threads.scrbl @@ -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].}