Locks & Synchronization Manual

About

Locks are used to guard access to resources that are shared across multiple threads.

Note
Locks should be used to avoid crashes when seldom used resources are accessed and to protect single threaded operations. Locks should not be used to fix problems of ill-designed code.
Warning
Make sure that a thread releases a lock after it has finished or a deadlock will be the result.

Spinlock

maxon::Spinlock implements a mutex that will loop on a pause/idle instruction when it is already locked.

// This example shows how a global spin lock is used to protect the access to a global function.
static maxon::Result<void> UpdateWithSpinlock()
{
// example global instance; typically a member variable would be used
static maxon::Spinlock g_spinlock;
// lock
g_spinlock.Lock();
// handle shared resource
const maxon::Result<void> res = UpdateGlobalData();
// unlock
g_spinlock.Unlock();
return res;
}

ScopedLock

A maxon::ScopedLock acquires a lock when created and releases upon destruction (at the end of the scope).

Warning
ScopedLock should only be used for a short block of code. It will block all other threads.
// This example uses a maxon::ScopedLock to protect access to a global function.
static maxon::Result<void> UpdateWithScopedLock(maxon::Spinlock& lock)
{
// lock on creation
maxon::ScopedLock scopedLock(lock);
// handle shared resource
UpdateGlobalData() iferr_return;
return maxon::OK;
}

RWSpinlock

The maxon::RWSpinlock allows access for multiple readers and only one exclusive writer.

// This example shows how a global RW spin lock is used to protect the access to global functions.
// example global instance; typically a member variable would be used
static maxon::RWSpinlock g_rwSpinlock;
static maxon::Result<void> UpdateWithRWLock()
{
// write lock
g_rwSpinlock.WriteLock();
// handle shared resource
const maxon::Result<void> res = UpdateGlobalData();
// write unlock
g_rwSpinlock.WriteUnlock();
return res;
}
static maxon::Int ReadWithRWLock(maxon::Int i)
{
// read lock
g_rwSpinlock.ReadLock();
// access shared resource
const maxon::Int res = AccessData(i);
// read unlock
g_rwSpinlock.ReadUnlock();
return res;
}

ARWLock

The maxon::ARWLock is an asymmetric read write lock which prefers the readers.

// This example shows how a global asymmetric read write lock is used to protect the access to a global function.
// example global instance; typically a member variable would be used
static maxon::ARWLock* g_arwlock = nullptr;
static maxon::Result<void> Init()
{
return maxon::OK;
}
static void Clear()
{
DeleteObj(g_arwlock);
}
MAXON_INITIALIZATION(Init, Clear);
static maxon::Result<void> UpdateWithARWLock()
{
g_arwlock->WriteLock();
const maxon::Result<void> res = UpdateGlobalData();
g_arwlock->WriteUnlock();
return res;
}

Synchronized

The maxon::Synchronized template is used to guarantee safe access to a given variable. It allows access to the variable only after a lock has been acquired. For asymmetric read-write access there is also maxon::RWSynchronized based on maxon::ARWLock.

// This example shows an example class that uses
// maxon::Synchronized to lock access to a member variable.
class SimpleStringClass
{
public:
// return stored String
maxon::String GetString() const
{
return *_string.Read();
}
// add to stored String
maxon::Result<void> AddString(const maxon::String& string)
{
_string.Write()->Append(string) iferr_return;
return maxon::OK;
}
// adds multiple strings to the stored String
{
// get locked pointer to get write access
auto lockedPtr = _string.Write();
for (maxon::String& str : strings)
{
lockedPtr->Append(str) iferr_return;
}
// lock on _string is released when the function scope is left
return maxon::OK;
}
private:
};

Serializer

The maxon::Serializer guarantees mutually exclusive access to a shared resource. The given lambdas are enqueued and executed.

Note
maxon::Serializer is low level and does not support error handling.
// This example uses the given Serializer to manage access to a shared resource.
g_serializer.EnqueueAndWait([]()
{
iferr (UpdateGlobalData())
{
DiagnosticOutput("Error in UpdateGlobalData()");
}
});

Further Reading