babylon

[简体中文]

Epoch

Principle

In typical lock-free structure implementations, there are generally two aspects that need to be addressed:

Lock-free algorithm garbage collection is less related to data structures and application scenarios and is generally considered as a separate topic. For example, the article Performance of memory reclamation for lockless synchronization provides a review of some typical lock-free reclamation methods. It mentions several classic reclamation schemes: QSBR (Quiescent-state-based reclamation), EBR (Epoch-based reclamation), and HPBR (Hazard-pointer-based reclamation).

Epoch is a modified implementation of the EBR mechanism, which focuses on reducing the overhead caused by mechanisms through the separation of access, elimination, and reclamation actions. The main differences are:

Usage Example

#include "babylon/concurrent/epoch.h"

using ::babylon::Epoch;

// Define an Epoch instance
Epoch epoch;

// Use Epoch to open a thread-local critical section before accessing the shared structure
{
  std::lock_guard<Epoch> lock {epoch};
  ... // Elements of the shared structure can be accessed within the critical section
} // After the critical section ends, the obtained element pointer can no longer be used

// In addition to thread-local mode, an Accessor can also be actively created to track the critical section
auto accessor = epoch.create_accessor();
{
  std::lock_guard<Accessor> lock {accessor};
  ... // Elements of the shared structure can be accessed within the critical section
} // After the critical section ends, the obtained element pointer can no longer be used

{
  accessor.lock();
  Task task {::std::move(accessor), ...} // The critical section can be transferred by moving the accessor
  ... // It can be transferred to an asynchronous thread, etc. The critical section ends after accessor.unlock()
} // If transferred, the critical section will not end here

// Elimination operation
... // Operate on the shared structure, removing some elements
// Advance the epoch and return the minimum epoch at which the previously removed elements can be reclaimed
auto lowest_epoch = epoch.tick();

// The lowest_epoch is associated with the eliminated elements, which can be synchronously or asynchronously checked for actual reclamation
auto low_water_mark = epoch.low_water_mark();
// When low_water_mark exceeds lowest_epoch, the elements can be safely reclaimed
if (lowest_epoch <= low_water_mark) {
  ... // Elements corresponding to lowest_epoch can be reclaimed
}