The
x::lockpool
template implements a thread-safe locking mechanism that uses
an event file descriptor for signaling.
The template takes several parameters, with the type of the lock
identifier being the only required parameter:
#include <x/lockpool.H> typedef x::lockpool<std::string> mylockpool_t; mylockpool_t mylockpool(mylockpool_t::create());
This example defines mylockpool_t
as a reference
to a reference-counted object that implements
locking based on text strings. That is, a lock is identified by a
simple text string:
x::eventfd evfd(x::eventfd::create()); mylockpool_t::lockentry myLock(mylockpool->addLockSet("filelock", evfd)); while (!myLock->locked()) { evfd->event(); }
Acquiring a lock involves invoking the lock pool object's
addLockSet
() method, that takes a lock identifier
and an event file descriptor.
addLockSet
() returns
an opaque lockentry
, which is a
reference.
addLockSet
() does not wait until the requested lock
has been acquired, it always returns immediately.
Use the lockentry
's locked
()
method to check if the lock has been acquired. In this example, if another
thread already holds a lock named “filelock”,
locked
() returns false
until the
existing lock gets released.
The lock pool may simultaneously hold other locks with
different names; locked
() checks only that the
given lock has been acquired. If not, the thread may then wait
for an event to be registered on the given event file descriptor.
The lock, once acquired, exists as long as the
lockentry
instance remains in scope.
The lock gets released
when this lockentry
reference goes out of
scope.
When the lock gets released, any thread that's waiting — for the
same lock identifier — acquires it.
In the given example, only one lock gets acquired
at a time. Multiple locks must be acquired one at a time. This is
allowed. A single thread may hold more than one
lockentry
,
however the application is responsible for avoiding deadlocks
between multiple threads that are attempting to acquire locks held by
each other.
x::lockpool
cannot detect deadlocks.
It is possible to define a
x::lockpool
that is capable of acquiring multiple
locks simultaneously, where the first parameter to
addLockSet
() is a container of some sorts, that may
hold multiple lock identifiers.
The resulting lockentry
's
locked
() method returns false
as long as any lock identifier is already locked.
Only when all lock identifiers in the lock set are not lock,
the lock identifiers, in the lock set, get acquired simultaneously.
The x::multilockpool
template provides a convenient way to define lock pools where multiple
lock identifiers get acquired at once.
Note that it's still possible to end up in a deadlock situation, where
conflicting threads attempt to acquire lock sets that contain lock
identifiers that are already held by each other.
However, with the ability to acquire multiple locks at the same time,
the need for a single thread to acquire multiple lock sets diminishes, and
it should be possible to use logic with each individual application thread
needing to acquire only one lockentry
at a time,
which eliminates any possibility for deadlocking.
The fourth, optional, parameter to the lockpool
defaults to true
.
A waiting lock gets acquired at the earliest possible
opportunity, as soon as a blocking lock gets removed from the lock pool.
This — depending on the comparison function, and whether or not
the lock pool implements multiple lock acquisition — may result in
older waiting locks
getting continuously starved by newer locks jumping ahead of
them; as well as a lengthy scan of a long list of waiting locks, searching
for any lock that can be acquired at this time.
Explicitly setting the starve
flag
to false
sets a strict first-come/first-serve
lock acquisition order. If the oldest waiting lock cannot be acquired
because it's blocked, any newer locks are not checked. This results in
faster performance and no starvation, however it means that a newer
lock gets placed on the waiting list
even when it is not blocked by another existing
lock, because it must wait its turn behind an older waiting lock.
See the
reference documentation for
x::lockpool
for more information.