-
Notifications
You must be signed in to change notification settings - Fork 38
Missing basic combinators guard-operation
and nack-guard-operation
#97
Description
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 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.