mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-12 06:41:13 +02:00
(Mutexes): New datatype-centric section, adding
fair mutexes and collecting up material from ... (Low level thread primitives, Higher level thread procedures, C level thread interface): ... these nodes.
This commit is contained in:
parent
02d9c82a02
commit
3cf066df9b
1 changed files with 153 additions and 96 deletions
|
@ -20,6 +20,7 @@ reviewed and largely reorganized.]
|
||||||
* Fluids:: Thread-local variables.
|
* Fluids:: Thread-local variables.
|
||||||
* Futures:: Delayed execution in new threads.
|
* Futures:: Delayed execution in new threads.
|
||||||
* Parallel Forms:: Parallel execution of forms.
|
* Parallel Forms:: Parallel execution of forms.
|
||||||
|
* Mutexes:: Synchronization primitives.
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,10 +32,10 @@ Arbiters are synchronization objects, they can be used by threads to
|
||||||
control access to a shared resource. An arbiter can be locked to
|
control access to a shared resource. An arbiter can be locked to
|
||||||
indicate a resource is in use, and unlocked when done.
|
indicate a resource is in use, and unlocked when done.
|
||||||
|
|
||||||
An arbiter is like a light-weight mutex (@pxref{Low level thread
|
An arbiter is like a light-weight mutex (@pxref{Mutexes}). It uses
|
||||||
primitives}). It uses less memory and may be a little faster, but
|
less memory and may be faster, but there's no way for a thread to
|
||||||
there's no way for a thread to block waiting on an arbiter, it can
|
block waiting on an arbiter, it can only test and get the status
|
||||||
only test and get the status returned.
|
returned.
|
||||||
|
|
||||||
@deffn {Scheme Procedure} make-arbiter name
|
@deffn {Scheme Procedure} make-arbiter name
|
||||||
@deffnx {C Function} scm_make_arbiter (name)
|
@deffnx {C Function} scm_make_arbiter (name)
|
||||||
|
@ -351,40 +352,6 @@ If one or more threads are waiting to execute, calling yield forces an
|
||||||
immediate context switch to one of them. Otherwise, yield has no effect.
|
immediate context switch to one of them. Otherwise, yield has no effect.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@c begin (texi-doc-string "guile" "make-mutex")
|
|
||||||
@deffn {Scheme Procedure} make-mutex
|
|
||||||
Create a new mutex object.
|
|
||||||
@end deffn
|
|
||||||
|
|
||||||
@c begin (texi-doc-string "guile" "lock-mutex")
|
|
||||||
@deffn {Scheme Procedure} lock-mutex mutex
|
|
||||||
Lock @var{mutex}. If the mutex is already locked, the calling thread
|
|
||||||
blocks until the mutex becomes available. The function returns when
|
|
||||||
the calling thread owns the lock on @var{mutex}. Locking a mutex that
|
|
||||||
a thread already owns will succeed right away and will not block the
|
|
||||||
thread. That is, Guile's mutexes are @emph{recursive}.
|
|
||||||
|
|
||||||
When a system async is activated for a thread that is blocked in a
|
|
||||||
call to @code{lock-mutex}, the waiting is interrupted and the async is
|
|
||||||
executed. When the async returns, the waiting is resumed.
|
|
||||||
@end deffn
|
|
||||||
|
|
||||||
@deffn {Scheme Procedure} try-mutex mutex
|
|
||||||
Try to lock @var{mutex}. If the mutex is already locked by someone
|
|
||||||
else, return @code{#f}. Else lock the mutex and return @code{#t}.
|
|
||||||
@end deffn
|
|
||||||
|
|
||||||
@c begin (texi-doc-string "guile" "unlock-mutex")
|
|
||||||
@deffn {Scheme Procedure} unlock-mutex mutex
|
|
||||||
Unlocks @var{mutex} if the calling thread owns the lock on
|
|
||||||
@var{mutex}. Calling unlock-mutex on a mutex not owned by the current
|
|
||||||
thread results in undefined behaviour. Once a mutex has been unlocked,
|
|
||||||
one thread blocked on @var{mutex} is awakened and grabs the mutex
|
|
||||||
lock. Every call to @code{lock-mutex} by this thread must be matched
|
|
||||||
with a call to @code{unlock-mutex}. Only the last call to
|
|
||||||
@code{unlock-mutex} will actually unlock the mutex.
|
|
||||||
@end deffn
|
|
||||||
|
|
||||||
@c begin (texi-doc-string "guile" "make-condition-variable")
|
@c begin (texi-doc-string "guile" "make-condition-variable")
|
||||||
@deffn {Scheme Procedure} make-condition-variable
|
@deffn {Scheme Procedure} make-condition-variable
|
||||||
Make a new condition variable.
|
Make a new condition variable.
|
||||||
|
@ -425,7 +392,7 @@ Wake up all threads that are waiting for @var{cv}.
|
||||||
|
|
||||||
Higher level thread procedures are available by loading the
|
Higher level thread procedures are available by loading the
|
||||||
@code{(ice-9 threads)} module. These provide standardized
|
@code{(ice-9 threads)} module. These provide standardized
|
||||||
thread creation and mutex interaction.
|
thread creation.
|
||||||
|
|
||||||
@deffn macro make-thread proc [args@dots{}]
|
@deffn macro make-thread proc [args@dots{}]
|
||||||
Apply @var{proc} to @var{args} in a new thread formed by
|
Apply @var{proc} to @var{args} in a new thread formed by
|
||||||
|
@ -439,31 +406,12 @@ Evaluate forms @var{first} and @var{rest} in a new thread formed by
|
||||||
the error to the current error port.
|
the error to the current error port.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn macro with-mutex m [body@dots{}]
|
|
||||||
Lock mutex @var{m}, evaluate @var{body}, and then unlock @var{m}.
|
|
||||||
These sub-operations form the branches of a @code{dynamic-wind}.
|
|
||||||
@end deffn
|
|
||||||
|
|
||||||
@deffn macro monitor body@dots{}
|
|
||||||
Evaluate @var{body}, with a mutex locked so only one thread can
|
|
||||||
execute that code at any one time. Each @code{monitor} form has its
|
|
||||||
own private mutex and the locking is done as per @code{with-mutex}
|
|
||||||
above. The return value is the return from the last form in
|
|
||||||
@var{body}.
|
|
||||||
|
|
||||||
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.
|
|
||||||
@end deffn
|
|
||||||
|
|
||||||
@node C level thread interface
|
@node C level thread interface
|
||||||
@subsubsection C level thread interface
|
@subsubsection C level thread interface
|
||||||
|
|
||||||
You can create and manage threads, mutexes, and condition variables
|
You can create and manage threads
|
||||||
with the C versions of the primitives above. For example, you can
|
with the C versions of the primitives above.
|
||||||
create a mutex with @code{scm_make_mutex} and lock it with
|
These
|
||||||
@code{scm_lock_mutex}. In addition to these primitives there is also
|
|
||||||
a second set of primitives for threading related things. These
|
|
||||||
functions and data types are only available from C and can not be
|
functions and data types are only available from C and can not be
|
||||||
mixed with the first set from above. However, they might be more
|
mixed with the first set from above. However, they might be more
|
||||||
efficient and can be used in situations where Scheme data types are
|
efficient and can be used in situations where Scheme data types are
|
||||||
|
@ -539,47 +487,13 @@ might have been detached by the time it terminates.
|
||||||
Return the handle of the calling thread.
|
Return the handle of the calling thread.
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@deftp {C Data Type} scm_t_mutex
|
|
||||||
This data type represents a mutex, to be used with scm_mutex_init,
|
|
||||||
etc.
|
|
||||||
@end deftp
|
|
||||||
|
|
||||||
@deftypefn {C Function} void scm_mutex_init (scm_t_mutex *m)
|
|
||||||
Initialize the mutex structure pointed to by @var{m}.
|
|
||||||
@end deftypefn
|
|
||||||
|
|
||||||
@deftypefn {C Function} void scm_mutex_destroy (scm_t_mutex *m)
|
|
||||||
Deallocate all resources associated with @var{m}.
|
|
||||||
@end deftypefn
|
|
||||||
|
|
||||||
@deftypefn {C Function} void scm_mutex_lock (scm_t_mutex *m)
|
|
||||||
Lock the mutex @var{m}. When it is already locked by a different
|
|
||||||
thread, wait until it becomes available. Locking a mutex that is
|
|
||||||
already locked by the current threads is not allowd and results in
|
|
||||||
undefined behavior. The mutices are not guaranteed to be fair. That
|
|
||||||
is, a thread that attempts a lock after yourself might be granted it
|
|
||||||
before you.
|
|
||||||
@end deftypefn
|
|
||||||
|
|
||||||
@deftypefn {C Function} int scm_mutex_trylock (scm_t_mutex *m)
|
|
||||||
Lock @var{m} as with @code{scm_mutex_lock} but don't wait when this
|
|
||||||
does succeed immediately. Returns non-zero when the mutex could in
|
|
||||||
fact be locked , and zero when it is already locked by some other
|
|
||||||
thread.
|
|
||||||
@end deftypefn
|
|
||||||
|
|
||||||
@deftypefn {C Function} void scm_mutex_unlock (scm_t_mutex *m)
|
|
||||||
Unlock the mutex @var{m}. The mutex must have been locked by the
|
|
||||||
current thread, else the behavior is undefined.
|
|
||||||
@end deftypefn
|
|
||||||
|
|
||||||
@deftp {C Data Type} scm_t_cond
|
@deftp {C Data Type} scm_t_cond
|
||||||
This data type represents a condition variable, to be used with
|
This data type represents a condition variable, to be used with
|
||||||
scm_cond_init, etc.
|
scm_cond_init, etc.
|
||||||
@end deftp
|
@end deftp
|
||||||
|
|
||||||
@deftypefn {C Function} void scm_cond_init (scm_t_cond *c)
|
@deftypefn {C Function} void scm_cond_init (scm_t_cond *c)
|
||||||
Initialize the mutex structure pointed to by @var{c}.
|
Initialize the condition variable structure pointed to by @var{c}.
|
||||||
@end deftypefn
|
@end deftypefn
|
||||||
|
|
||||||
@deftypefn {C Function} void scm_cond_destroy (scm_t_cond *c)
|
@deftypefn {C Function} void scm_cond_destroy (scm_t_cond *c)
|
||||||
|
@ -858,6 +772,149 @@ completed, it doesn't need to wait for all to finish.
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
|
|
||||||
|
@node Mutexes
|
||||||
|
@subsection Mutexes
|
||||||
|
@cindex mutex
|
||||||
|
|
||||||
|
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, ``standard'' and ``fair''. They're
|
||||||
|
created by @code{make-mutex} and @code{make-fair-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.
|
||||||
|
|
||||||
|
@sp 1
|
||||||
|
@deffn {Scheme Procedure} make-mutex
|
||||||
|
@deffnx {Scheme Procedure} make-fair-mutex
|
||||||
|
Return a new mutex object.
|
||||||
|
|
||||||
|
@code{make-mutex} creates a standard mutex. This is fast, but its
|
||||||
|
features are restricted. Recursive locking (multiple lock calls by
|
||||||
|
one thread) is not permitted, and an unlock can be done only when
|
||||||
|
already locked and only by the owning thread. When multiple threads
|
||||||
|
are blocked waiting to acquire the mutex, it's unspecified which will
|
||||||
|
get it next.
|
||||||
|
|
||||||
|
@code{make-fair-mutex} creates a fair mutex. This has more features
|
||||||
|
and error checking. Recursive locking is allowed, a given thread can
|
||||||
|
make multiple lock calls and the mutex is released when a balancing
|
||||||
|
number of unlocks are done. Other threads blocked waiting to acquire
|
||||||
|
the mutex form a queue and the one waiting longest will be the next to
|
||||||
|
acquire it.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} lock-mutex mutex
|
||||||
|
Lock @var{mutex}. If the mutex is already locked by another thread
|
||||||
|
then block and return only when @var{mutex} has been acquired.
|
||||||
|
|
||||||
|
For standard mutexes (@code{make-mutex}), if the thread has itself
|
||||||
|
already locked @var{mutex} it must not call @code{lock-mutex} on it a
|
||||||
|
further time. Behaviour is unspecified if this is done.
|
||||||
|
|
||||||
|
For a fair mutex (@code{make-fair-mutex}), if the thread has itself
|
||||||
|
already locked @var{mutex}, then a further @code{lock-mutex} call
|
||||||
|
increments the lock count. An additional @code{unlock-mutex} will be
|
||||||
|
required to finally release.
|
||||||
|
|
||||||
|
When a system async (@pxref{System asyncs}) is activated for a thread
|
||||||
|
blocked in @code{lock-mutex}, the wait is interrupted and the async is
|
||||||
|
executed. When the async returns the wait resumes.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} try-mutex mutex
|
||||||
|
Try to lock @var{mutex} as per @code{lock-mutex}. If @var{mutex} can
|
||||||
|
be acquired immediately then this is done and the return is @code{#t}.
|
||||||
|
If @var{mutex} is locked by some other thread then nothing is done and
|
||||||
|
the return is @code{#f}.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn {Scheme Procedure} unlock-mutex mutex
|
||||||
|
Unlock @var{mutex}.
|
||||||
|
|
||||||
|
For a standard mutex (@code{make-mutex}), if @var{mutex} is not locked
|
||||||
|
by the calling thread then behaviour is unspecified.
|
||||||
|
|
||||||
|
For a fair mutex (@code{make-fair-mutex}), if @var{mutex} is not
|
||||||
|
locked by the calling thread then an error is thrown.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@sp 1
|
||||||
|
The following are higher level operations on mutexes. These are
|
||||||
|
available from
|
||||||
|
|
||||||
|
@example
|
||||||
|
(use-modules (ice-9 threads))
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@deffn macro with-mutex mutex [body@dots{}]
|
||||||
|
Lock @var{mutex}, evaluate the @var{body} forms, then unlock
|
||||||
|
@var{mutex}. The return value is the return from the last @var{body}
|
||||||
|
form.
|
||||||
|
|
||||||
|
The lock, body and unlock form the branches of a @code{dynamic-wind}
|
||||||
|
(@pxref{Dynamic Wind}), so @var{mutex} is automatically unlocked if an
|
||||||
|
error or new continuation exits @var{body}, and is re-locked if
|
||||||
|
@var{body} is re-entered by a captured continuation.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@deffn macro monitor body@dots{}
|
||||||
|
Evaluate the @var{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 @var{body} form.
|
||||||
|
|
||||||
|
Each @code{monitor} form has its own private mutex and the locking and
|
||||||
|
evaluation is as per @code{with-mutex} above. A standard mutex
|
||||||
|
(@code{make-mutex}) is used, which means @var{body} must not
|
||||||
|
recursively re-enter the @code{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.
|
||||||
|
@end deffn
|
||||||
|
|
||||||
|
@sp 1
|
||||||
|
The following provide access to standard mutexes from C code.
|
||||||
|
|
||||||
|
@deftp {C Data Type} scm_t_mutex
|
||||||
|
A mutex, to be used with @code{scm_mutex_init}, etc.
|
||||||
|
@end deftp
|
||||||
|
|
||||||
|
@deftypefn {C Function} void scm_mutex_init (scm_t_mutex *m)
|
||||||
|
Initialize the mutex structure pointed to by @var{m}.
|
||||||
|
@end deftypefn
|
||||||
|
|
||||||
|
@deftypefn {C Function} void scm_mutex_destroy (scm_t_mutex *m)
|
||||||
|
Free all resources associated with @var{m}.
|
||||||
|
@end deftypefn
|
||||||
|
|
||||||
|
@deftypefn {C Function} void scm_mutex_lock (scm_t_mutex *m)
|
||||||
|
Lock the mutex @var{m}. This is as per @code{lock-mutex} above on a
|
||||||
|
standard mutex.
|
||||||
|
@end deftypefn
|
||||||
|
|
||||||
|
@deftypefn {C Function} int scm_mutex_trylock (scm_t_mutex *m)
|
||||||
|
Attempt to lock mutex @var{m}, as per @code{scm_mutex_lock}. If
|
||||||
|
@var{m} is unlocked then this is done and the return is non-zero. If
|
||||||
|
@var{m} is already locked by another thread then do nothing and return
|
||||||
|
zero.
|
||||||
|
@end deftypefn
|
||||||
|
|
||||||
|
@deftypefn {C Function} void scm_mutex_unlock (scm_t_mutex *m)
|
||||||
|
Unlock the mutex @var{m}. The mutex must have been locked by the
|
||||||
|
current thread, otherwise the behavior is undefined.
|
||||||
|
@end deftypefn
|
||||||
|
|
||||||
|
|
||||||
@c Local Variables:
|
@c Local Variables:
|
||||||
@c TeX-master: "guile.texi"
|
@c TeX-master: "guile.texi"
|
||||||
@c End:
|
@c End:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue