You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Reppy writes that “the first three combinators have analogs in many concurrent languages, but the withNack combinator is unique to CML.”
The guard combinator constructs an operation that encapsulates a thunk. Each time the guard-based operation is supplied to perform-operation (directly or indirectly), the thunk is called to produce a fresh operation that is used in place of the guard-based operation. (More precisely, the thunk is called at most once for each call to perform-operation: it may be skipped if some other ready operation is chosen before the guard-based operation is considered.)
The withNack combinator is similar, but the function it encapsulates takes an argument: perform-operation calls it with a “negative acknowledgment” operation which will become ready if and when the operation returned by the callback definitely will never be chosen (e.g. normally because perform-operation has committed to some other operation, but also if the fiber blocked on it has terminated or if control escapes in some other way).
It's hard to write a short motivating example, but I'd point to the paper “Kill-Safe Synchronization Abstractions”, which walks through the implementation of the racket/async-channel library. Quite apart from the kill-safety aspects, guard-evt is needed to implement an async-channel-put-evt with the same guarantee as channel-put-evt (a.k.a put-operation) that the put takes place if and only if the operation is chosen by perform-operation.
I don't think make-base-operation is sufficient to implement either the guard or the withNack combinator. A naïve implementation of guard-operation could use try and block functions that close over some shared mutable state, but there doesn't seem to be any way for an implementation to distinguish each call to perform-operation, which it needs to do in order to get a fresh underlying operation from the thunk for each call. More obviously, nack-guard-operation would need perform-operation to cooperate by signaling the “negative acknowledgment” operations.
The text was updated successfully, but these errors were encountered:
It looks like
(fibers operations)
provides two basic operation combinators:choice-operation
, corresponding to Concurrent ML'schoose
and Racket'schoice-evt
; andwrap-operation
, like CML'swrap
and Racket'swrap-evt
.But AFAICT, this library seems to be missing the other two basic combinators (of the four enumerated in Concurrent Programming in ML § 6.5):
guard
, like Racket'sguard-evt
; andwithNack
, a.k.a.nack-guard-evt
.Reppy writes that “the first three combinators have analogs in many concurrent languages, but the
withNack
combinator is unique to CML.”The
guard
combinator constructs an operation that encapsulates a thunk. Each time theguard
-based operation is supplied toperform-operation
(directly or indirectly), the thunk is called to produce a fresh operation that is used in place of theguard
-based operation. (More precisely, the thunk is called at most once for each call toperform-operation
: it may be skipped if some other ready operation is chosen before theguard
-based operation is considered.)The
withNack
combinator is similar, but the function it encapsulates takes an argument:perform-operation
calls it with a “negative acknowledgment” operation which will become ready if and when the operation returned by the callback definitely will never be chosen (e.g. normally becauseperform-operation
has committed to some other operation, but also if the fiber blocked on it has terminated or if control escapes in some other way).It's hard to write a short motivating example, but I'd point to the paper “Kill-Safe Synchronization Abstractions”, which walks through the implementation of the
racket/async-channel
library. Quite apart from the kill-safety aspects,guard-evt
is needed to implement anasync-channel-put-evt
with the same guarantee aschannel-put-evt
(a.k.aput-operation
) that the put takes place if and only if the operation is chosen byperform-operation
.I don't think
make-base-operation
is sufficient to implement either theguard
or thewithNack
combinator. A naïve implementation ofguard-operation
could use try and block functions that close over some shared mutable state, but there doesn't seem to be any way for an implementation to distinguish each call toperform-operation
, which it needs to do in order to get a fresh underlying operation from the thunk for each call. More obviously,nack-guard-operation
would needperform-operation
to cooperate by signaling the “negative acknowledgment” operations.The text was updated successfully, but these errors were encountered: