mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-10 14:00:21 +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.
|
||||
* Futures:: Delayed execution in new threads.
|
||||
* Parallel Forms:: Parallel execution of forms.
|
||||
* Mutexes:: Synchronization primitives.
|
||||
@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
|
||||
indicate a resource is in use, and unlocked when done.
|
||||
|
||||
An arbiter is like a light-weight mutex (@pxref{Low level thread
|
||||
primitives}). It uses less memory and may be a little faster, but
|
||||
there's no way for a thread to block waiting on an arbiter, it can
|
||||
only test and get the status returned.
|
||||
An arbiter is like a light-weight mutex (@pxref{Mutexes}). It uses
|
||||
less memory and may be faster, but there's no way for a thread to
|
||||
block waiting on an arbiter, it can only test and get the status
|
||||
returned.
|
||||
|
||||
@deffn {Scheme Procedure} 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.
|
||||
@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")
|
||||
@deffn {Scheme Procedure} make-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
|
||||
@code{(ice-9 threads)} module. These provide standardized
|
||||
thread creation and mutex interaction.
|
||||
thread creation.
|
||||
|
||||
@deffn macro make-thread proc [args@dots{}]
|
||||
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.
|
||||
@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
|
||||
@subsubsection C level thread interface
|
||||
|
||||
You can create and manage threads, mutexes, and condition variables
|
||||
with the C versions of the primitives above. For example, you can
|
||||
create a mutex with @code{scm_make_mutex} and lock it with
|
||||
@code{scm_lock_mutex}. In addition to these primitives there is also
|
||||
a second set of primitives for threading related things. These
|
||||
You can create and manage threads
|
||||
with the C versions of the primitives above.
|
||||
These
|
||||
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
|
||||
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.
|
||||
@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
|
||||
This data type represents a condition variable, to be used with
|
||||
scm_cond_init, etc.
|
||||
@end deftp
|
||||
|
||||
@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
|
||||
|
||||
@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
|
||||
|
||||
|
||||
@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 TeX-master: "guile.texi"
|
||||
@c End:
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue