ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
No Matches
SmartLock Class Reference


This class extends class ThreadLock, adding functionality to register 'acquirers' of type ThreadLock. Only with the second acquirer added, the lock is activated using method ThreadLock::SetSafeness . The goal is to not use a mutex, when such use is not needed. In occasions with very high frequency of acquisition, this can provide a performance benefit.

The following rules apply:

  • An instance of this type must not be acquired before an acquirer is registered.
  • The acquirers have to be in recursive mode.
  • If acquirers are locked in a nested fashion, then they have to be added in the same order they are locked and removed in reverse order
  • An acquirer must not be added twice. (This is not a technical restriction, but a chosen design. While a second addition is ignored, in debug versions of the code, an ALib Error Report is written (by default this triggers an assertion).

Using nulled acquirers:
Sometimes it is useful to add a nullptr as an acquirer. A sample for this is found and explained static field StdOutputStreams. If the first acquirer is nullptr, the second should be added in a thread-safe way. This means, the code invoking AddAcquirer needs to care for itself, that this object is not acquired during this process. E.g. it can be done in the bootstrap section of a process, when no parallel threads were started. For further acquirers, such care does not need to be taken. While an acquirer must not be attached twice, 'anonymous' (nullptr) acquirers may. For each anonymous invocation of AddAcquirer, a corresponding call RemoveAcquirer is needed, to get back to Safeness::Unsafe.

Definition at line 48 of file smartlock.hpp.

#include <smartlock.hpp>

Inheritance diagram for SmartLock:
Collaboration diagram for SmartLock:

Public Static Field Index:

static ALIB_API SmartLock StdOutputStreams

Public Method Index:

 SmartLock ()
void Acquire (const NCString &dbgFile, int dbgLine, const NCString &dbgFunc)
ALIB_API int AddAcquirer (ThreadLock *newAcquirer)
ALIB_API int CntAcquirers ()
ALIB_API void Release ()
ALIB_API int RemoveAcquirer (ThreadLock *acquirer)
- Public Method Index: inherited from ThreadLock
ALIB_API ThreadLock (lang::Safeness safeness=lang::Safeness::Safe)
ALIB_API ~ThreadLock ()
ALIB_API void Acquire (const NCString &dbgFile, int dbgLine, const NCString &dbgFunc)
int CountAcquirements () const
ThreadGetOwner () const
lang::Safeness GetSafeness () const
bool IsOwnedByCurrentThread () const
ALIB_API void Release ()
ALIB_API void SetSafeness (lang::Safeness safeness)
bool WillRelease () const

Additional Inherited Members

- Public Field Index: inherited from ThreadLock
NCString DbgOwnerFile =nullptr
NCString DbgOwnerFunc =nullptr
int DbgOwnerLine
uint16_t DbgRecursionWarningThreshold =10
integer DbgWarningAfterWaitTimeInMillis =2000L

Field Details:

◆ acquirers

std::vector<ThreadLock*> acquirers

The list of acquirers.

Definition at line 58 of file smartlock.hpp.

◆ lock

ThreadLockNR lock

A lock for acquirer management.

Definition at line 55 of file smartlock.hpp.

◆ StdOutputStreams

SmartLock StdOutputStreams

This is a static singleton of this class that allows to lock an application's standard output streams.

In multi-threaded processes, to protect the output streams from concurrent access, this smart lock might be used by any entity that writes data to the streams. Before it can be used (acquired and released), it is needed to register with the object using SmartLock::AddAcquirer . This has to be done once per thread that aims to write to the stream. Then, prior to writing, this object has to be acquired and after writing released.

Because often, the standard output stream and standard error stream are identical, ALib provides one single lock for both, to protect also against interwoven standard output and error information.

If the 'entity' that is registering is not of type ThreadLock it is allowed to provide nullptr in the parameter of method AddAcquirer. In this case, the process of adding and removing acquirers is not performed in a thread safe way. Therefore it is advised to register so called anonymous (nullptr) acquirers only at bootstrap time, when no parallel threads were started, yet.

If an application is deemed to always write to the standard output streams from within multiple threads, an alternative to registering each writing entity, is to invoke AddAcquirer just two times in a row with nullptr at the start of a process and then never do this again (and never de-register). This way, no thread needs to register/de-register but threads may still Acquire and Release the lock without being registered. In other words, once a smart lock is enabled, subsequent registrations are just used to count and identify the de-registration.

The advantage of the SmartLock is that if only one 'entity' registered, no system mutexes will be used with Acquire and Release, hence there is a performance gain. Such gain is not noticeable for the 'slow' terminal console output, but it is for fast, buffered output streams.
Logging module ALox , which is built on ALib , will register whenever a Logger is used that writes to the standard output stream. Hence, applications that in parallel use, e.g. 'std::cout', should register at bootstrap and acquire this instance prior to writing. This way, log output and other application output is not mixed, but separated in different Lines.

Definition at line 174 of file smartlock.hpp.

Constructor(s) / Destructor Details::

◆ SmartLock()

SmartLock ( )

Constructs a SmartLock. Parent ThreadLock is initialized to Unsafe mode.

Definition at line 68 of file smartlock.hpp.

Method Details:

◆ Acquire()

void Acquire ( const NCString & dbgFile,
int dbgLine,
const NCString & dbgFunc )

Overwriting ThreadLock::Acquire. With debug builds, asserts that at least one acquirer is set.

In debug-compilations of the library, this method accepts three parameters, providing information about the caller. In the release version these parameters do not exist. Therefore use macro ALIB_CALLER_PRUNED to provide the parameters:
   sample.Acquire( ALIB_CALLER_PRUNED );
dbgFileCaller information. Available only with debug builds.
dbgLineCaller information. Available only with debug builds.
dbgFuncCaller information. Available only with debug builds.

Definition at line 87 of file smartlock.hpp.

Here is the call graph for this function:

◆ AddAcquirer()

int AddAcquirer ( ThreadLock * newAcquirer)

Adds an acquirer. With the second acquirer added, this lock will be set into safe mode.

newAcquirerThe acquirer to add.
The new number of acquirers set.

Definition at line 29 of file smartlock.cpp.

Here is the call graph for this function:

◆ CntAcquirers()

int CntAcquirers ( )

Returns the number of acquirers. This is for debug and statistics purposes.

The number of acquirers set.

Definition at line 23 of file smartlock.cpp.

◆ Release()

void Release ( )


Releases ownership of this object. If Acquire was called multiple times before, the same number of calls to this method have to be performed to release ownership.

Definition at line 163 of file threadlock.cpp.

◆ RemoveAcquirer()

int RemoveAcquirer ( ThreadLock * acquirer)

Removes an acquirer. If the amount of acquirers after removal equals 1, then this lock will be set to unsafe mode.

acquirerThe acquirer to remove.
The new number of acquirers set.

Definition at line 92 of file smartlock.cpp.

Here is the call graph for this function:

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