babylon

[简体中文]

Futex

Principle

The standard coroutine mechanism’s co_await is essentially aligned with the std::future parallel synchronization model. However, more complex synchronization models, such as std::mutex or std::condition_variable, can be unified through a mechanism similar to futex(2).

futex

The implementation uses a std::mutex for each futex instance to manage value checks and to chain together waiting callbacks atomically. The DepositBox is used to ensure the uniqueness of cancellation and wake-up actions.

Usage Example

#include "babylon/coroutine/task.h"
#include "babylon/coroutine/futex.h"

using ::babylon::coroutine::Task;
using ::babylon::coroutine::Futex;

using Cancellation = Futex::Cancellation;

// Futex is initialized with an internal value of 0
Futex futex;

// Read and write futex internal value
futex.value() = ...;
// Atomically read and write futex internal value
futex.atomic_value().xxxx(...);

Task<...> some_coroutine(...) {
  ...
  // Atomically check if the internal value is equal to expected_value.
  // If true, suspend the coroutine, otherwise continue execution.
  co_await futex.wait(expected_value).on_suspend(
    // Use a callback function to receive the cancellation handle after the coroutine is suspended.
    // Note that if the coroutine is not suspended, the callback won't be called.
    [&](Cancellation cancel) {
      // Typically, the cancel handle is registered to a timer mechanism, and after a specified time, cancel() is invoked to trigger cancellation.
      // From the moment the callback is executed, cancel is usable. You can even invoke cancel() directly within the callback, though it's generally unnecessary.
      on_timer(cancel, 100ms);
    }
  );
  // Several scenarios could lead to execution reaching this point:
  // 1. The expected_value was not met.
  // 2. After suspension, futex.wake_one or futex.wake_all was called.
  // 3. After suspension, cancel() was invoked.
  ...
}