PCL
pcl::Mutex Class Reference

Adaptive mutual exclusion lock variable. More...

#include <Mutex.h>

Public Member Functions

 Mutex (const Mutex &)=delete
 
 Mutex (int spinCount=4000, int spinCountDelta=1000, int spinCountMax=12000)
 
virtual ~Mutex ()
 
void Lock ()
 
void operator() (bool lock=true)
 
Mutexoperator= (const Mutex &)=delete
 
int SpinCount () const
 
bool TryLock ()
 
void Unlock ()
 

Detailed Description

The word mutex is an abbreviation for mutual exclusion. A mutex object provides access synchronization for threads. A mutex protects one or more objects or a code section, so that only one thread can access them at a given time.

To understand how mutual thread exclusion works and why is it needed, consider the following example code:

int data;
void functionOne()
{
data += 2;
data *= 2;
}
void functionTwo()
{
data -= 3;
data *= 3;
}

If functionOne() and functionTwo() are called in sequence, this happens:

data = 3;
functionOne(); // data is now = 10
functionTwo(); // data is now = 21

Now suppose that we define two threads that call the above functions:

class ThreadOne : public Thread
{
//...
void Run() override
{
functionOne();
}
};
class ThreadTwo : public Thread
{
//...
void Run() override
{
functionTwo();
}
};

If we start both threads in sequence:

data = 3;
ThreadOne one;
ThreadTwo two;
one.Start();
two.Start();

then the following might happen:

// ThreadOne calls functionOne:
data += 2; // data is now = 5
// ThreadTwo calls functionTwo. This causes ThreadOne to enter a wait state
// until ThreadTwo terminates:
data -= 3; // data is now = 2
data *= 3; // data is now = 6
// ThreadOne resumes execution:
data *= 2; // data is now = 12

Because both threads can access data in any order, and there is no guarantee as to when a given thread starts execution, the result may not be what we expect (we get 12 instead of 21). Adding synchronization with a Mutex object in functionOne() and functionTwo() solves the problem:

int data;
Mutex mutex;
void functionOne()
{
mutex.Lock();
data += 2;
data *= 2;
mutex.Unlock();
}
void functionTwo()
{
mutex.Lock();
data -= 3;
data *= 3;
mutex.Unlock();
}

A mutex can only be locked by a single thread at a time. After Lock() has been called from a thread T, other threads that call Lock() on the same mutex object block their execution until the thread T calls Unlock().

To attempt locking a mutex without blocking execution, the Mutex::TryLock() member function can be used. This can provide much higher performance than Mutex::Lock() when the calling threads don't depend on gaining exclusive access to the shared data being protected by the mutex object.

Mutex implements adaptive spinning, a technique that can improve performance by avoiding expensive semaphore wait operations under high levels of thread contention. See the documentation for the class constructor for more information.

Mutex has been implemented as a low-level PCL class that does not depend on the PixInsight core application. On Windows platforms, Mutex has been implemented as a wrapper to a critical section. On UNIX/Linux platforms, Mutex uses atomic integer operations implemented as inline assembly code and direct calls to the pthreads library.

See also
AutoLock, ReadWriteMutex

Definition at line 208 of file Mutex.h.

Constructor & Destructor Documentation

◆ Mutex() [1/2]

pcl::Mutex::Mutex ( int  spinCount = 4000,
int  spinCountDelta = 1000,
int  spinCountMax = 12000 
)
inline

Constructs a Mutex object.

Parameters
spinCountMaximum number of spinning loops to do before performing a mutex semaphore wait operation when a thread attempts to lock this mutex and it has already been locked by another thread. The expensive wait operation can be avoided if this object becomes unlocked during the spinning loops. The spin count must be ≥ 0. When this parameter is zero, a regular non-spinning mutex is constructed. The default value is 4000.
spinCountDeltaWhen spinCount > 0, this is an increment applied to the maximum number of spinning loops when the spinning process fails and an expensive mutex lock operation is performed because not enough loops were executed. This parameter allows for an adaptive spin lock mutex operation by increasing the number of loops when necessary. The default value is 1000.
spinCountMaxWhen both spinCount and spinCountDelta are greater than zero, this is the upper bound of spinning loops allowed by adaptively increasing them when the loops are insufficient to avoid an expensive mutex lock operation. The default value is 12000.
Note
The spinCountDelta and spinCountMax parameters are only used on Linux and macOS. Adaptive mutex spinning is currently unavailable on Windows.

Definition at line 242 of file Mutex.h.

◆ ~Mutex()

virtual pcl::Mutex::~Mutex ( )
inlinevirtual

Virtual destructor.

Warning
Destroying a locked Mutex object invokes undefined behavior. Always make sure that a mutex has been unlocked before destroying it.

Definition at line 260 of file Mutex.h.

◆ Mutex() [2/2]

pcl::Mutex::Mutex ( const Mutex )
delete

Copy constructor. This constructor is disabled because mutexes are unique objects.

Member Function Documentation

◆ Lock()

void pcl::Mutex::Lock ( )
inline

Locks this Mutex object.

When a mutex has been locked in a thread T, other threads cannot lock it until the thread T unlocks it. When a thread attempts to lock a Mutex object that has been previously locked, it blocks its execution until the Mutex object is unlocked.

If the mutex has already been locked by another thread, this routine performs a number of spin loops before doing an (expensive) wait operation on a semaphore associated with this mutex object. If this mutex becomes unlocked during the spinning loops, the wait operation can be avoided to lock the mutex in the calling thread. This can greatly improve efficiency of multithreaded code under high levels of contention (e.g. several running threads that depend on frequent concurrent accesses to shared data). For fine control and performance tuning, the maximum number of spinning loops can be specified in the class constructor, as well as an increment for adaptive spinning and the maximum spinning loops allowed by increasing them adaptively.

Definition at line 301 of file Mutex.h.

◆ operator()()

void pcl::Mutex::operator() ( bool  lock = true)
inline

Function call operator. This is a convenience operator that performs the lock and unlock operations in an alternative, perhaps more elegant way.

Parameters
lockWhether the mutex should be locked (when true) or unlocked (when lock is false).

For example, the following code snippet:

Mutex mutex;
//...
mutex( true );
// some code to be protected
mutex( false );

is equivalent to:

Mutex mutex;
//...
mutex.Lock();
// some code to be protected
mutex.Unlock();

Definition at line 374 of file Mutex.h.

◆ operator=()

Mutex& pcl::Mutex::operator= ( const Mutex )
delete

Copy assignment. This operator is disabled because mutexes are unique objects.

◆ SpinCount()

int pcl::Mutex::SpinCount ( ) const
inline

Returns the current spin count of this Mutex object.

The spin count is a read-only property that can only be set upon object construction. It can grow adaptively after successive mutex lock operations. For information on mutex spin counts, refer to the class constructor.

Definition at line 408 of file Mutex.h.

◆ TryLock()

bool pcl::Mutex::TryLock ( )
inline

Attempts locking this Mutex object. Returns true iff this mutex has been successfully locked because no other thread had already acquired it.

Unlike Lock(), this function does not block execution of the calling thread if this mutex cannot be locked.

Definition at line 389 of file Mutex.h.

Referenced by pcl::AbstractImage::RunThreads().

◆ Unlock()

void pcl::Mutex::Unlock ( )
inline

Unlocks this Mutex object.

See the Lock() documentation for more information.

Definition at line 337 of file Mutex.h.

Referenced by pcl::AbstractImage::RunThreads().


The documentation for this class was generated from the following file:
pcl::Mutex::Mutex
Mutex(int spinCount=4000, int spinCountDelta=1000, int spinCountMax=12000)
Definition: Mutex.h:242