Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Missing basic combinators guard-operation and nack-guard-operation #97

@LiberalArtist

Description

@LiberalArtist

It looks like (fibers operations) provides two basic operation combinators:

  1. choice-operation, corresponding to Concurrent ML's choose and Racket's choice-evt; and
  2. wrap-operation, like CML's wrap and Racket's wrap-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):

  1. guard, like Racket's guard-evt; and
  2. withNack, 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.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions