Module Private.Fiber_context

Every fiber has an associated context.

type t
val make_root : unit -> t

Make a new root context for a new domain.

val destroy : t -> unit

destroy t removes t from its cancellation context.

val tid : t -> Trace.id

Cancellation

The Cancel module describes the user's view of cancellation.

Internally, when the user calls a primitive operation that needs to block the fiber, the Suspend callback effect is performed. This suspends the fiber and calls callback from the scheduler's context, passing it the suspended fiber's context. If the operation can be cancelled, the callback should use set_cancel_fn to register a cancellation function.

There are two possible outcomes for the operation: it may complete normally, or it may be cancelled. If it is cancelled then the registered cancellation function is called. This function will always be called from the fiber's own domain, but care must be taken if the operation could be completed by another domain at the same time.

Consider the case of Stream.take, which can be fulfilled by a Stream.add from another domain. We want to ensure that either the item is removed from the stream and returned to the waiting fiber, or that the operation is cancelled and the item is not removed from the stream.

Therefore, cancelling and completing both need to update an atomic value (with Atomic.compare_and_set) so that only one can succeed. The case where Stream.take succeeds before cancellation:

  1. A fiber calls Suspend and is suspended. The callback sets a cancel function and registers a waiter on the stream.
  2. When another domain has an item, it marks the atomic as finished (making the take uncancellable) and begins resuming the fiber with the new item.
  3. If the taking fiber is cancelled after this, the cancellation must be ignored and the operation will complete successfully. Future operations will fail immediately, however.

The case of cancellation winning the race:

  1. A fiber calls Suspend and is suspended. The callback sets a cancel function and registers a waiter on the stream.
  2. The taking fiber is cancelled. Its cancellation function is called, which updates the atomic and starts removing the waiter.
  3. If another domain tries to provide an item to the waiter as this is happening, it will try to update the atomic too and fail. The item will be given to the next waiter instead.

Note: A fiber will only have a cancel function set while it is suspended.

val cancellation_context : t -> Cancel.t

cancellation_context t is t's current cancellation context.

val set_cancel_fn : t -> (exn -> unit) -> unit

set_cancel_fn t fn sets fn as the fiber's cancel function.

If t's cancellation context is cancelled, the function is called. It should attempt to make the current operation finish quickly, either with a successful result or by raising the given exception.

Just before being called, the fiber's cancel function is replaced with ignore so that fn cannot be called twice.

On success, the cancel function is cleared automatically when Suspend.enter returns, but for single-domain operations you may like to call clear_cancel_fn manually to remove it earlier.

fn will be called from t's domain (from the fiber that called cancel).

fn must not switch fibers. If it did, this could happen:

  1. Another suspended fiber in the same cancellation context resumes before its cancel function is called.
  2. It enters a protected block and starts a new operation.
  3. fn returns.
  4. We cancel the protected operation.
val clear_cancel_fn : t -> unit

clear_cancel_fn t is set_cancel_fn t ignore.

This must only be called from the fiber's own domain.

For single-domain operations, it can be useful to call this manually as soon as the operation succeeds (i.e. when the fiber is added to the run-queue) to prevent the cancel function from being called.

For operations where another domain may resume the fiber, your cancel function will need to cope with being called after the operation has succeeded. In that case you should not call clear_cancel_fn. The backend will do it automatically just before resuming your fiber.

val get_error : t -> exn option

get_error t is Cancel.get_error (cancellation_context t)