[ANN] Affect 0.0.0 – A streamlined and natural concurrency model for OCaml

A couple more points:

  1. Before someone asks if this shouldn’t be implemented over picos, I will defer to that discussion. The TL;DR is no and there is no plan to do so.

  2. In contrast to other concurrency libraries, using affect’s asynchronous functions does not rob you from using effects in your code. See the cookbook for more information.

  3. If you’d like to see the cancellation model in action this robust combined ping client/server program and its chaotic canceller shows how the cancellation model fares in pratice. Especially the lack of bureaucracy around it.

Finally I’d like to quickly describe how the cancellation model of asynchronous functions is implemented because I’m really happy in how the pieces ended up falling into place. In my head I expected it would involve making use of CML’s negative acknowledgements which allow an action to be notified if it’s not the chosen one in a choice[1] – except that makes no sense :–)

Instead when an asynchronous function invokes an action with Action.invoke, the scheduler adds a hidden primitive action as a choice to the action being performed. This added action synchronizes on the cancellation of the asynchronous function by raising Fun.Async.Cancelled. It is a private version (for efficiency reasons) of the Fun.Async.wait_cancellation action that you can use to synchronize on the cancellation of an asynchronous function.

So one way of thinking about the interaction between the cancellation of asynchronous functions and action invocations is that when you (or a direct style function) invokes an action with:

Action.invoke action

what you end up effectively invoking, unless you are in a masked cancellation scope, is:

Action.invoke (cancellable action)

with cancellable conceptually implemented for the running asynchronous function call f as:

let cancellable action = 
  let raise_cancelled () = raise Fun.Async.Cancelled in
  let if_cancelled = Fun.Async.wait_cancellation f () in 
  let raise_if_cancelled = Action.map raise_cancelled if_cancel in
  Action.choose [raise_if_cancelled; action]

The nice thing about is that this does not affect (yes) the implementation of actions at all which does its business unaware of the asynchronous function cancellation model[2]. Nor does it affect the scheduler (except for passing the hidden action). So all that is nicely contained to the model of asynchronous functions.

This scheme is what brings us our world of pervasive EINTR – except this time in a hopefully useful and reliable way :–) for structuring and cancelling your functional activites.

In passing note that this makes affect’s cancellation model pass the “cancellation support test” of the F# people mentioned in section §2.4 of this paper (PDF).


  1. This is not implemented in affect for now. It could be added later, it’s one additional combinator and entails only internal changes, but it’s not super clean in my opinion, it exists in OCaml’s upstream Event implementation but with a different signature from CML. ↩︎

  2. We did however add an optional argument to the private function that creates action invocations to distinguish this action so that it doesn’t show up in the tracing metadata ↩︎

9 Likes