#lang scribble/doc @(require "mz.ss") @title[#:tag "breakhandler"]{Breaks} @section-index["threads" "breaking"] A @deftech{break} is an asynchronous exception, usually triggered through an external source controlled by the user, or through the @racket[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 @racket[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 @racket[break-enabled] parameter-like procedure, and through the @racket[parameterize-break] form, which is analogous to @racket[parameterize]. The @racket[break-enabled] procedure does not represent a parameter to be used with @racket[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 @racket[parameterize] expression's body. Certain procedures, such as @racket[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 @racket[exn:break] exception, then the break is guaranteed to be delivered before breaks can be disabled in the thread. The timing of @racket[exn:break] exceptions is not guaranteed in any other way. Before calling a @racket[with-handlers] predicate or handler, an exception handler, an error display handler, an error escape handler, an error value conversion handler, or a @racket[pre-thunk] or @racket[post-thunk] for a @racket[dynamic-wind], the call is @racket[parameterize-break]ed to disable breaks. Furthermore, breaks are disabled during the transitions among handlers related to exceptions, during the transitions between @racket[pre-thunk]s and @racket[post-thunk]s for @racket[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 from the time of the invocation until the target continuation executes unless a relevant @racket[dynamic-wind] @racket[pre-thunk] or @racket[post-thunk] explicitly enables breaks. If a break is triggered for a thread that is blocked on a nested thread (see @racket[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, @racketblock[ (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 @racket[semaphore-wait]. A break exception will never damage a semaphore, or any other built-in construct, but many built-in procedures (including @racket[semaphore-wait]) contain internal sub-expressions that can be interrupted by a break. In general, it is impossible using only @racket[semaphore-wait] to implement the guarantee that either the semaphore is decremented or an exception is raised, but not both. Racket therefore supplies @racket[semaphore-wait/enable-break] (see @secref["semaphore"]), which does permit the implementation of such an exclusive guarantee: @racketblock[ (parameterize-break #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 @racket[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, Racket provides @racket[read-bytes-avail!/enable-break], @racket[write-bytes-avail/enable-break], 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 @racket[on?] is not supplied, the result is @racket[#t] if breaks are currently enabled, @racket[#f] otherwise. If @racket[on?] is supplied as @racket[#f], breaks are disabled, and if @racket[on?] is a true value, breaks are enabled.} @defform[(parameterize-break boolean-expr body ...+)]{Evaluates @racket[boolean-expr] to determine whether breaks are initially enabled while evaluating the @racket[body]s in sequence. The result of the @racket[parameter-break] expression is the result of the last @racket[expr]. Like @racket[parameterize] (see @secref["parameters"]), a fresh @tech{thread cell} (see @secref["threadcells"]) is allocated to hold the break-enabled state of the continuation, and calls to @racket[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 @racket[(current-parameterization)] (see @secref["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 @racket[(call-with-parameterization parameterization thunk)] (see @secref["parameters"]), calls @racket[thunk] in a continuation whose break-enabled state is in @racket[break-param]. The @racket[thunk] is @italic{not} called in tail position with respect to the @racket[call-with-break-parameterization] call.}