6.21.4 Mutexes and Condition Variables

A mutex is a thread synchronization object, it can be used by threads
to control access to a shared resource. A mutex can be locked to
indicate a resource is in use, and other threads can then block on the
mutex to wait for the resource (or can just test and do something else
if not available). “Mutex” is short for “mutual exclusion”.

There are two types of mutexes in Guile, “standard” and
“recursive”. They’re created by make-mutex and
make-recursive-mutex respectively, the operation functions are
then common to both.

Note that for both types of mutex there’s no protection against a
“deadly embrace”. For instance if one thread has locked mutex A and
is waiting on mutex B, but another thread owns B and is waiting on A,
then an endless wait will occur (in the current implementation).
Acquiring requisite mutexes in a fixed order (like always A before B)
in all threads is one way to avoid such problems.

Scheme Procedure: make-mutex . flags

C Function: scm_make_mutex ()

C Function: scm_make_mutex_with_flags (SCM flags)

Return a new mutex. It is initially unlocked. If flags is
specified, it must be a list of symbols specifying configuration flags
for the newly-created mutex. The supported flags are:

unchecked-unlock

Unless this flag is present, a call to ‘unlock-mutex’ on the returned
mutex when it is already unlocked will cause an error to be signalled.

allow-external-unlock

Allow the returned mutex to be unlocked by the calling thread even if
it was originally locked by a different thread.

recursive

The returned mutex will be recursive.

Scheme Procedure: mutex? obj

C Function: scm_mutex_p (obj)

Return #t iff obj is a mutex; otherwise, return
#f.

Scheme Procedure: make-recursive-mutex

C Function: scm_make_recursive_mutex ()

Create a new recursive mutex. It is initially unlocked. Calling this
function is equivalent to calling ‘make-mutex’ and specifying the
recursive flag.

Scheme Procedure: lock-mutex mutex [timeout [owner]]

C Function: scm_lock_mutex (mutex)

C Function: scm_lock_mutex_timed (mutex, timeout, owner)

Lock mutex. If the mutex is already locked, then block and
return only when mutex has been acquired.

When timeout is given, it specifies a point in time where the
waiting should be aborted. It can be either an integer as returned
by current-time or a pair as returned by gettimeofday.
When the waiting is aborted, #f is returned.

When owner is given, it specifies an owner for mutex other
than the calling thread. owner may also be #f,
indicating that the mutex should be locked but left unowned.

For standard mutexes (make-mutex), and error is signalled if
the thread has itself already locked mutex.

For a recursive mutex (make-recursive-mutex), if the thread has
itself already locked mutex, then a further lock-mutex
call increments the lock count. An additional unlock-mutex
will be required to finally release.

If mutex was locked by a thread that exited before unlocking it,
the next attempt to lock mutex will succeed, but
abandoned-mutex-error will be signalled.

When a system async (see section System asyncs) is activated for a thread
blocked in lock-mutex, the wait is interrupted and the async is
executed. When the async returns, the wait resumes.

C Function: voidscm_dynwind_lock_mutex (SCM mutex)

Arrange for mutex to be locked whenever the current dynwind
context is entered and to be unlocked when it is exited.

Scheme Procedure: try-mutex mx

C Function: scm_try_mutex (mx)

Try to lock mutex as per lock-mutex. If mutex can
be acquired immediately then this is done and the return is #t.
If mutex is locked by some other thread then nothing is done and
the return is #f.

Scheme Procedure: unlock-mutex mutex [condvar [timeout]]

C Function: scm_unlock_mutex (mutex)

C Function: scm_unlock_mutex_timed (mutex, condvar, timeout)

Unlock mutex. An error is signalled if mutex is not locked
and was not created with the unchecked-unlock flag set, or if
mutex is locked by a thread other than the calling thread and was
not created with the allow-external-unlock flag set.

If condvar is given, it specifies a condition variable upon
which the calling thread will wait to be signalled before returning.
(This behavior is very similar to that of
wait-condition-variable, except that the mutex is left in an
unlocked state when the function returns.)

When timeout is also given, it specifies a point in time where
the waiting should be aborted. It can be either an integer as
returned by current-time or a pair as returned by
gettimeofday. When the waiting is aborted, #f is
returned. Otherwise the function returns #t.

Scheme Procedure: mutex-owner mutex

C Function: scm_mutex_owner (mutex)

Return the current owner of mutex, in the form of a thread or
#f (indicating no owner). Note that a mutex may be unowned but
still locked.

Scheme Procedure: mutex-level mutex

C Function: scm_mutex_level (mutex)

Return the current lock level of mutex. If mutex is
currently unlocked, this value will be 0; otherwise, it will be the
number of times mutex has been recursively locked by its current
owner.

Wait until condvar has been signalled. While waiting,
mutex is atomically unlocked (as with unlock-mutex) and
is locked again when this function returns. When time is given,
it specifies a point in time where the waiting should be aborted. It
can be either a integer as returned by current-time or a pair
as returned by gettimeofday. When the waiting is aborted,
#f is returned. When the condition variable has in fact been
signalled, #t is returned. The mutex is re-locked in any case
before wait-condition-variable returns.

When a system async is activated for a thread that is blocked in a
call to wait-condition-variable, the waiting is interrupted,
the mutex is locked, and the async is executed. When the async
returns, the mutex is unlocked again and the waiting is resumed. When
the thread block while re-acquiring the mutex, execution of asyncs is
blocked.

Scheme Procedure: signal-condition-variable condvar

C Function: scm_signal_condition_variable (condvar)

Wake up one thread that is waiting for condvar.

Scheme Procedure: broadcast-condition-variable condvar

C Function: scm_broadcast_condition_variable (condvar)

Wake up all threads that are waiting for condvar.

The following are higher level operations on mutexes. These are
available from

(use-modules (ice-9 threads))

macro: with-mutex mutex [body…]

Lock mutex, evaluate the body forms, then unlock
mutex. The return value is the return from the last body
form.

The lock, body and unlock form the branches of a dynamic-wind
(see section Dynamic Wind), so mutex is automatically unlocked if an
error or new continuation exits body, and is re-locked if
body is re-entered by a captured continuation.

macro: monitor body…

Evaluate the body forms, with a mutex locked so only one thread
can execute that code at any one time. The return value is the return
from the last body form.

Each monitor form has its own private mutex and the locking and
evaluation is as per with-mutex above. A standard mutex
(make-mutex) is used, which means body must not
recursively re-enter the monitor form.

The term “monitor” comes from operating system theory, where it
means a particular bit of code managing access to some resource and
which only ever executes on behalf of one process at any one time.