FairSharedMutex Class Reference

A shared mutex that starves neither readers nor writers. More...

#include <RobotAPI/libraries/armem/server/wm/FairSharedMutex.h>

Public Member Functions

 FairSharedMutex ()=default
 
 FairSharedMutex (const FairSharedMutex &)=delete
 
void lock ()
 
void lock_shared ()
 
FairSharedMutex & operator= (const FairSharedMutex &)=delete
 
bool try_lock ()
 
bool try_lock_shared ()
 
void unlock ()
 
void unlock_shared ()
 

Static Public Member Functions

static const char * activeBehaviourName ()
 Name of the behaviour currently selected, for benchmark output.
 
static bool readerPreferringForBenchmarks ()
 
static void setReaderPreferringForBenchmarks (bool enabled)
 Benchmarks and regression tests only.
 

Detailed Description

A shared mutex that starves neither readers nor writers.

Neither of the obvious choices works for a core segment:

  • std::shared_mutex is a reader-preferring pthread_rwlock on glibc. An arriving reader takes the lock even while a writer is queued, so a steady stream of queries holds commits off indefinitely. Measured on a core segment with 2 commit and 8 query threads: 1.3 commits/s, and a writer that waited the entire 1.5 s run.
  • PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP just moves the problem to the other side. Same benchmark, writers saturated: 17 queries/s.

So a writer announces itself and closes a gate before queueing for the lock. Readers check that announcement – one relaxed atomic load, so the uncontended fast path stays the plain std::shared_mutex one that 8ec383f5a was after – and only queue on the gate when a writer is actually waiting. The writer holds the gate shut until it owns the lock, so the only readers that can get ahead of it are the ones already past the load when it announced itself: a bounded batch, after which it runs. Readers cannot be starved either, since the writer opens the gate as soon as it holds the lock rather than keeping it for the critical section.

Locks are not recursive, in either mode. armem never nests segment locks – it cannot, since taking the exclusive lock while holding the shared one (doLocked + commitLocking) already deadlocks with std::shared_mutex.

Satisfies the SharedMutex named requirement, so std::shared_lock and std::unique_lock work unchanged.

Constructor & Destructor Documentation

◆ FairSharedMutex() [1/2]

FairSharedMutex ( )
default

◆ FairSharedMutex() [2/2]

FairSharedMutex ( const FairSharedMutex & )
delete

Member Function Documentation

◆ activeBehaviourName()

static const char * activeBehaviourName ( )
inlinestatic

Name of the behaviour currently selected, for benchmark output.

◆ lock()

void lock ( )
inline

◆ lock_shared()

void lock_shared ( )
inline

◆ operator=()

FairSharedMutex & operator= ( const FairSharedMutex & )
delete

◆ readerPreferringForBenchmarks()

static bool readerPreferringForBenchmarks ( )
inlinestatic

◆ setReaderPreferringForBenchmarks()

static void setReaderPreferringForBenchmarks ( bool enabled)
inlinestatic

Benchmarks and regression tests only.

Skips the gate, reducing every FairSharedMutex in the process to the bare reader-preferring std::shared_mutex the core segment used before – the configuration that starves writers. It exists so a benchmark can report old and new side by side in one binary, instead of the mutex type having to be edited and the tree rebuilt.

Set it before starting the threads that will use the lock. Nothing outside a benchmark should ever call this.

◆ try_lock()

bool try_lock ( )
inline

◆ try_lock_shared()

bool try_lock_shared ( )
inline

◆ unlock()

void unlock ( )
inline

◆ unlock_shared()

void unlock_shared ( )
inline

The documentation for this class was generated from the following file: