I’ve finally had some time to work on one of my favorite and longest ongoing hobby projects, libfork, a C++20 library for for strict fork-join parallelism.

I built libfork when I realized that with the introduction of coroutines in C++20 it was possible to implement continuation-stealing without the use of macros or inline assembly. This is a feature that I have wanted for a long time and I am very happy with the results:

auto fib(int n) -> pool_task<int> { 

  if (n < 2) {
    co_return n;
  }

  auto a = co_await fib(n - 1).fork(); // Spawn a child task.
  auto b = co_await fib(n - 2);        // Execute inline.

  co_await lf::join();                 // Wait for children.

  co_return *a + b;                    // Use * to dereference a future.
}

Furthermore libfork has acted as a place for me to explore lock-free and wait-free programming, discover the delights of the C11 memory model and try to build a production grade CI/CD pipeline.

This release includes:

  • Decoupling scheduling from task graph creation.
  • Custom schedulers supported.
  • Tasks support allocators.
  • Exceptions support removed.
  • Void tasks no longer require a future.
  • Benchmarks!!
  • Many more tests.

Planned for the (near) future:

  • More examples.
  • No per-task coroutine frame allocations.
  • A scheduler that sleeps workers when there is no work based on this paper.