mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-28 07:50:20 +02:00
Updates for the new thread stuff.
This commit is contained in:
parent
9de87eea47
commit
b4fddbbeda
7 changed files with 742 additions and 457 deletions
|
@ -372,10 +372,9 @@ location resumed is expecting multiple values (@pxref{Multiple
|
|||
Values}) then they should be passed as multiple arguments, for
|
||||
instance @code{(@var{cont} @var{x} @var{y} @var{z})}.
|
||||
|
||||
@var{cont} may only be used from the dynamic root in which it was
|
||||
created (@pxref{Dynamic Roots}), and in a multi-threaded program only
|
||||
from the thread in which it was created, since each thread is a
|
||||
separate dynamic root.
|
||||
@var{cont} may only be used from the same side of a continuation
|
||||
barrier as it was created (@pxref{Continuation Barriers}), and in a
|
||||
multi-threaded program only from the thread in which it was created.
|
||||
|
||||
The call to @var{proc} is not part of the continuation captured, it runs
|
||||
only when the continuation is created. Often a program will want to
|
||||
|
|
|
@ -326,13 +326,15 @@ this procedure directly, use the procedures @code{read-enable},
|
|||
|
||||
@rnindex eval
|
||||
@c ARGFIXME environment/environment specifier
|
||||
@deffn {Scheme Procedure} eval exp module
|
||||
@deffnx {C Function} scm_eval (exp, module)
|
||||
@deffn {Scheme Procedure} eval exp module_or_state
|
||||
@deffnx {C Function} scm_eval (exp, module_or_state)
|
||||
Evaluate @var{exp}, a list representing a Scheme expression,
|
||||
in the top-level environment specified by @var{module}.
|
||||
While @var{exp} is evaluated (using @code{primitive-eval}),
|
||||
@var{module} is made the current module. The current module
|
||||
is reset to its previous value when @var{eval} returns.
|
||||
XXX - dynamic states.
|
||||
Example: (eval '(+ 1 2) (interaction-environment))
|
||||
@end deffn
|
||||
|
||||
@rnindex interaction-environment
|
||||
|
|
|
@ -8,50 +8,92 @@
|
|||
@node Initialization
|
||||
@section Initializing Guile
|
||||
|
||||
@deftypefn {C Function} void scm_boot_guile (int @var{argc}, char **@var{argv}, void (*@var{main_func}) (void *@var{data}, int @var{argc}, char **@var{argv}), void *@var{data})
|
||||
Initialize the Guile Scheme interpreter. Then call @var{main_func},
|
||||
passing it @var{data}, @var{argc}, and @var{argv} as indicated. The
|
||||
function @var{main_func} should do all the work of the program
|
||||
(initializing other packages, defining application-specific functions,
|
||||
reading user input, and so on) before returning. When @var{main_func}
|
||||
returns, @code{scm_boot_guile} calls @code{exit (0)};
|
||||
@code{scm_boot_guile} never returns. If you want some other exit
|
||||
value, have @var{main_func} call @code{exit} itself.
|
||||
Each thread that wants to use functions from the Guile API needs to
|
||||
put itself into guile mode with either @code{scm_with_guile} or
|
||||
@code{scm_init_guile}. The global state of Guile is initialized
|
||||
automatically when the first thread enters guile mode.
|
||||
|
||||
@code{scm_boot_guile} arranges for the Scheme @code{command-line}
|
||||
function to return the strings given by @var{argc} and @var{argv}. If
|
||||
@var{main_func} modifies @var{argc} or @var{argv}, it should call
|
||||
@code{scm_set_program_arguments} with the final list, so Scheme code
|
||||
will know which arguments have been processed.
|
||||
When a thread wants to block outside of a Guile API function, it should
|
||||
leave guile mode temporarily with either @code{scm_without_guile} or
|
||||
@code{scm_leave_guile}, @xref{Threads}.
|
||||
|
||||
Why must the caller do all the real work from @var{main_func}? Guile's
|
||||
garbage collector scans the stack to find all local variables that
|
||||
reference Scheme objects. To do this, it needs to know the bounds of
|
||||
the stack that might contain such references. Because there is no
|
||||
portable way in C to find the base of the stack, @code{scm_boot_guile}
|
||||
assumes that all references are above its own stack frame. If you try
|
||||
to manipulate Scheme objects after this function returns, it's the luck
|
||||
of the draw whether Guile's storage manager will be able to find the
|
||||
objects you allocate. So, @code{scm_boot_guile} function exits, rather
|
||||
than returning, to discourage you from making that mistake.
|
||||
Threads that are created by @code{call-with-new-thread} or
|
||||
@code{scm_spawn_thread} start out in guile mode so you don't need to
|
||||
initialize them.
|
||||
|
||||
See @code{scm_init_guile}, below, for a function that can find the real
|
||||
base of the stack, but not in a portable way.
|
||||
@deftypefn {C Function} void *scm_with_guile (void *(*func)(void *), void *data)
|
||||
Call @var{func}, passing it @var{data} and return what @var{func}
|
||||
returns. While @var{func} is running, the current thread is in guile
|
||||
mode and can thus use the Guile API.
|
||||
|
||||
When @code{scm_with_guile} is called from guile mode, the thread remains
|
||||
in guile mode when @code{scm_with_guile} returns.
|
||||
|
||||
Otherwise, it puts the current thread into guile mode and, if needed,
|
||||
gives it a Scheme representation that is contained in the list returned
|
||||
by @code{all-threads}, for example. This Scheme representation is not
|
||||
removed when @code{scm_with_guile} returns so that a given thread is
|
||||
always represented by the same Scheme value during its lifetime, if at
|
||||
all.
|
||||
|
||||
When this is the first thread that enters guile mode, the global state
|
||||
of Guile is initialized before calling @code{func}.
|
||||
|
||||
The function @var{func} is called via
|
||||
@code{scm_with_continuation_barrier}; thus, @code{scm_with_guile}
|
||||
returns exactly once.
|
||||
|
||||
When @code{scm_with_guile} returns, the thread is no longer in guile
|
||||
mode (except when @code{scm_with_guile} was called from guile mode, see
|
||||
above). Thus, only @code{func} can store @code{SCM} variables on the
|
||||
stack and be sure that they are protected from the garbage collector.
|
||||
See @code{scm_init_guile} for another approach at initializing Guile
|
||||
that does not have this restriction.
|
||||
|
||||
It is OK to call @code{scm_with_guile} while a thread has temporarily
|
||||
left guile mode via @code{scm_without_guile} or @code{scm_leave_guile}.
|
||||
It will then simply temporarily enter guile mode again.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_init_guile ()
|
||||
Initialize the Guile Scheme interpreter.
|
||||
Arrange things so that all of the code in the current thread executes as
|
||||
if from within a call to @code{scm_with_guile}. That is, all functions
|
||||
called by the current thread can assume that @code{SCM} values on their
|
||||
stack frames are protected from the garbage collector (except when the
|
||||
thread has explicitely left guile mode, of course).
|
||||
|
||||
In contrast to @code{scm_boot_guile}, this function knows how to find
|
||||
the true base of the stack and thus does not need to usurp the control
|
||||
flow of your program. However, since finding the stack base can not be
|
||||
done portably, this function might not be available in all installations
|
||||
of Guile. If you can, you should use @code{scm_boot_guile} instead.
|
||||
When @code{scm_init_guile} is called from a thread that already has been
|
||||
in guile mode once, nothing happens. This behavior matters when you
|
||||
call @code{scm_init_guile} while the thread has only temporarily left
|
||||
guile mode: in that case the thread will not be in guile mode after
|
||||
@code{scm_init_guile} returns. Thus, you should not use
|
||||
@code{scm_init_guile} in such a scenario.
|
||||
|
||||
Note that @code{scm_init_guile} does not inform Guile about the command
|
||||
line arguments that should be returned by the Scheme function
|
||||
@code{command-line}. You can use @code{scm_set_program_arguments} to do
|
||||
this.
|
||||
When a uncaught throw happens in a thread that has been put into guile
|
||||
mode via @code{scm_init_guile}, a short message is printed to the
|
||||
current error port and the thread is exited via @code{scm_pthread_exit
|
||||
(NULL)}. No restrictions are placed on continuations.
|
||||
|
||||
The function @code{scm_init_guile} might not be available on all
|
||||
platforms since it requires some stack-bounds-finding magic that might
|
||||
not have been ported to all platforms that Guile runs on. Thus, if you
|
||||
can, it is better to use @code{scm_with_guile} or its variation
|
||||
@code{scm_boot_guile} instead of this function.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_boot_guile (int @var{argc}, char **@var{argv}, void (*@var{main_func}) (void *@var{data}, int @var{argc}, char **@var{argv}), void *@var{data})
|
||||
Enter guile mode as with @code{scm_with_guile} and call @var{main_func},
|
||||
passing it @var{data}, @var{argc}, and @var{argv} as indicated. When
|
||||
@var{main_func} returns, @code{scm_boot_guile} calls @code{exit (0)};
|
||||
@code{scm_boot_guile} never returns. If you want some other exit value,
|
||||
have @var{main_func} call @code{exit} itself. If you don't want to exit
|
||||
at all, use @code{scm_with_guile} instead of @code{scm_boot_guile}.
|
||||
|
||||
The function @code{scm_boot_guile} arranges for the Scheme
|
||||
@code{command-line} function to return the strings given by @var{argc}
|
||||
and @var{argv}. If @var{main_func} modifies @var{argc} or @var{argv},
|
||||
it should call @code{scm_set_program_arguments} with the final list, so
|
||||
Scheme code will know which arguments have been processed.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_shell (int @var{argc}, char **@var{argv})
|
||||
|
|
|
@ -15,12 +15,13 @@ reviewed and largely reorganized.]
|
|||
@menu
|
||||
* Arbiters:: Synchronization primitives.
|
||||
* Asyncs:: Asynchronous procedure invocation.
|
||||
* Dynamic Roots:: Root frames of execution.
|
||||
* Continuation Barriers:: Protection from non-local control flow.
|
||||
* Threads:: Multiple threads of execution.
|
||||
* Fluids:: Thread-local variables.
|
||||
* Blocking:: How to block properly in guile mode.
|
||||
* Fluids and Dynamic States:: Thread-local variables, etc.
|
||||
* Futures:: Delayed execution in new threads.
|
||||
* Parallel Forms:: Parallel execution of forms.
|
||||
* Mutexes:: Synchronization primitives.
|
||||
* Mutexes and Condition Variables:: Synchronization primitives.
|
||||
@end menu
|
||||
|
||||
|
||||
|
@ -32,10 +33,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{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.
|
||||
An arbiter is like a light-weight mutex (@pxref{Mutexes and Condition
|
||||
Variables}). 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)
|
||||
|
@ -113,7 +114,7 @@ them temporarily.
|
|||
|
||||
In addition to the C versions of @code{call-with-blocked-asyncs} and
|
||||
@code{call-with-unblocked-asyncs}, C code can use
|
||||
@code{scm_with_blocked_asyncs} and @code{scm_with_unblocked_asyncs}
|
||||
@code{scm_frame_block_asyncs} and @code{scm_frame_unblock_asyncs}
|
||||
inside a @dfn{frame} (@pxref{Frames}) to block or unblock system asyncs
|
||||
temporarily.
|
||||
|
||||
|
@ -196,114 +197,31 @@ Mark the user async @var{a} for future execution.
|
|||
Execute all thunks from the marked asyncs of the list @var{list_of_a}.
|
||||
@end deffn
|
||||
|
||||
@node Continuation Barriers
|
||||
@subsection Continuation Barriers
|
||||
|
||||
@node Dynamic Roots
|
||||
@subsection Dynamic Roots
|
||||
@cindex dynamic roots
|
||||
The non-local flow of control caused by continuations might sometimes
|
||||
not be wanted. You can use @code{with-continuation-barrier} etc to
|
||||
errect fences that continuations can not pass.
|
||||
|
||||
A @dfn{dynamic root} is a root frame of Scheme evaluation.
|
||||
The top-level repl, for example, is an instance of a dynamic root.
|
||||
@deffn {Sheme Procedure} with-continuation-barrier proc
|
||||
@deffnx {C Function} scm_with_continuation_barrier (proc)
|
||||
Call @var{proc} and return its result. Do not allow the invocation of
|
||||
continuations that would leave or enter the dynamic extent of the call
|
||||
to @code{with-continuation-barrier}. Such an attempt causes an error
|
||||
to be signaled.
|
||||
|
||||
Each dynamic root has its own chain of dynamic-wind information. Each
|
||||
has its own set of continuations, jump-buffers, and pending CATCH
|
||||
statements which are inaccessible from the dynamic scope of any
|
||||
other dynamic root.
|
||||
Throws (such as errors) that are not caught from within @var{proc} are
|
||||
caught by @code{with-continuation-barrier}. In that case, a short
|
||||
message is printed to the current error port and @code{#f} is returned.
|
||||
|
||||
In a thread-based system, each thread has its own dynamic root. Therefore,
|
||||
continuations created by one thread may not be invoked by another.
|
||||
|
||||
Even in a single-threaded system, it is sometimes useful to create a new
|
||||
dynamic root. For example, if you want to apply a procedure, but to
|
||||
not allow that procedure to capture the current continuation, calling
|
||||
the procedure under a new dynamic root will do the job.
|
||||
|
||||
@deffn {Scheme Procedure} call-with-dynamic-root thunk handler
|
||||
@deffnx {C Function} scm_call_with_dynamic_root (thunk, handler)
|
||||
Evaluate @code{(thunk)} in a new dynamic context, returning its value.
|
||||
|
||||
If an error occurs during evaluation, apply @var{handler} to the
|
||||
arguments to the throw, just as @code{throw} would. If this happens,
|
||||
@var{handler} is called outside the scope of the new root -- it is
|
||||
called in the same dynamic context in which
|
||||
@code{call-with-dynamic-root} was evaluated.
|
||||
|
||||
If @var{thunk} captures a continuation, the continuation is rooted at
|
||||
the call to @var{thunk}. In particular, the call to
|
||||
@code{call-with-dynamic-root} is not captured. Therefore,
|
||||
@code{call-with-dynamic-root} always returns at most one time.
|
||||
|
||||
Before calling @var{thunk}, the dynamic-wind chain is un-wound back to
|
||||
the root and a new chain started for @var{thunk}. Therefore, this call
|
||||
may not do what you expect:
|
||||
|
||||
@lisp
|
||||
;; Almost certainly a bug:
|
||||
(with-output-to-port
|
||||
some-port
|
||||
|
||||
(lambda ()
|
||||
(call-with-dynamic-root
|
||||
(lambda ()
|
||||
(display 'fnord)
|
||||
(newline))
|
||||
(lambda (errcode) errcode))))
|
||||
@end lisp
|
||||
|
||||
The problem is, on what port will @samp{fnord} be displayed? You
|
||||
might expect that because of the @code{with-output-to-port} that
|
||||
it will be displayed on the port bound to @code{some-port}. But it
|
||||
probably won't -- before evaluating the thunk, dynamic winds are
|
||||
unwound, including those created by @code{with-output-to-port}.
|
||||
So, the standard output port will have been re-set to its default value
|
||||
before @code{display} is evaluated.
|
||||
|
||||
(This function was added to Guile mostly to help calls to functions in C
|
||||
libraries that can not tolerate non-local exits or calls that return
|
||||
multiple times. If such functions call back to the interpreter, it should
|
||||
be under a new dynamic root.)
|
||||
Thus, @code{with-continuation-barrier} returns exactly once.
|
||||
@end deffn
|
||||
|
||||
|
||||
@deffn {Scheme Procedure} dynamic-root
|
||||
@deffnx {C Function} scm_dynamic_root ()
|
||||
Return an object representing the current dynamic root.
|
||||
|
||||
These objects are only useful for comparison using @code{eq?}.
|
||||
They are currently represented as numbers, but your code should
|
||||
in no way depend on this.
|
||||
@end deffn
|
||||
|
||||
@c begin (scm-doc-string "boot-9.scm" "quit")
|
||||
@deffn {Scheme Procedure} quit [exit_val]
|
||||
Throw back to the error handler of the current dynamic root.
|
||||
|
||||
If integer @var{exit_val} is specified and if Guile is being used
|
||||
stand-alone and if quit is called from the initial dynamic-root,
|
||||
@var{exit_val} becomes the exit status of the Guile process and the
|
||||
process exits.
|
||||
@end deffn
|
||||
|
||||
When Guile is run interactively, errors are caught from within the
|
||||
read-eval-print loop. An error message will be printed and @code{abort}
|
||||
called. A default set of signal handlers is installed, e.g., to allow
|
||||
user interrupt of the interpreter.
|
||||
|
||||
It is possible to switch to a "batch mode", in which the interpreter
|
||||
will terminate after an error and in which all signals cause their
|
||||
default actions. Switching to batch mode causes any handlers installed
|
||||
from Scheme code to be removed. An example of where this is useful is
|
||||
after forking a new process intended to run non-interactively.
|
||||
|
||||
@c begin (scm-doc-string "boot-9.scm" "batch-mode?")
|
||||
@deffn {Scheme Procedure} batch-mode?
|
||||
Returns a boolean indicating whether the interpreter is in batch mode.
|
||||
@end deffn
|
||||
|
||||
@c begin (scm-doc-string "boot-9.scm" "set-batch-mode?!")
|
||||
@deffn {Scheme Procedure} set-batch-mode?! arg
|
||||
If @var{arg} is true, switches the interpreter to batch mode.
|
||||
The @code{#f} case has not been implemented.
|
||||
@end deffn
|
||||
@deftypefn {C Function} void *scm_c_with_continuation_barrier (void *(*func) (void *), void *data)
|
||||
Like @code{scm_with_continuation_barrier} but call @var{func} on
|
||||
@var{data}. When an error is caught, @code{NULL} is returned.
|
||||
@end deftypefn
|
||||
|
||||
@node Threads
|
||||
@subsection Threads
|
||||
|
@ -311,21 +229,6 @@ The @code{#f} case has not been implemented.
|
|||
@cindex Guile threads
|
||||
@cindex POSIX threads
|
||||
|
||||
Guile threads are implemented using POSIX threads, they run
|
||||
pre-emptively and concurrently through both Scheme code and system
|
||||
calls. The only exception is for garbage collection, where all
|
||||
threads must rendezvous.
|
||||
|
||||
@menu
|
||||
* Low level thread primitives::
|
||||
* Higher level thread procedures::
|
||||
* C level thread interface::
|
||||
@end menu
|
||||
|
||||
|
||||
@node Low level thread primitives
|
||||
@subsubsection Low level thread primitives
|
||||
|
||||
@deffn {Scheme Procedure} all-threads
|
||||
@deffnx {C Function} scm_all_threads ()
|
||||
Return a list of all threads.
|
||||
|
@ -337,23 +240,38 @@ Return the thread that called this function.
|
|||
@end deffn
|
||||
|
||||
@c begin (texi-doc-string "guile" "call-with-new-thread")
|
||||
@deffn {Scheme Procedure} call-with-new-thread thunk error-handler
|
||||
Evaluate @code{(thunk)} in a new thread, and new dynamic context,
|
||||
returning a new thread object representing the thread.
|
||||
@deffn {Scheme Procedure} call-with-new-thread thunk handler
|
||||
Call @code{thunk} in a new thread and with a new dynamic state,
|
||||
returning the new thread. The procedure @var{thunk} is called via
|
||||
@code{with-continuation-barrier}.
|
||||
|
||||
If an error occurs during evaluation, call error-handler, passing it
|
||||
an error code. If this happens, the error-handler is called outside
|
||||
the scope of the new root -- it is called in the same dynamic context
|
||||
in which with-new-thread was evaluated, but not in the caller's
|
||||
thread.
|
||||
When @var{handler} is specified, then @var{thunk} is called from
|
||||
within a @code{catch} with tag @code{#t} that has @var{handler} as its
|
||||
handler. This catch is established inside the continuation barrier.
|
||||
|
||||
All the evaluation rules for dynamic roots apply to threads.
|
||||
Once @var{thunk} or @var{handler} returns, the return value is made
|
||||
the @emph{exit value} of the thread and the thread is terminated.
|
||||
@end deffn
|
||||
|
||||
@deftypefn {C Function} SCM scm_spawn_thread (scm_t_catch_body body, void *body_data, scm_t_catch_handler handler, void *handler_data)
|
||||
Call @var{body} in a new thread, passing it @var{body_data}, returning
|
||||
the new thread. The function @var{body} is called via
|
||||
@code{scm_c_with_continuation_barrier}.
|
||||
|
||||
When @var{handler} is non-@code{NULL}, @var{body} is called via
|
||||
@code{scm_internal_catch} with tag @code{SCM_BOOL_T} that has
|
||||
@var{handler} and @var{handler_data} as the handler and its data. This
|
||||
catch is established inside the continuation barrier.
|
||||
|
||||
Once @var{body} or @var{handler} returns, the return value is made the
|
||||
@emph{exit value} of the thread and the thread is terminated.
|
||||
@end deftypefn
|
||||
|
||||
@c begin (texi-doc-string "guile" "join-thread")
|
||||
@deffn {Scheme Procedure} join-thread thread
|
||||
Suspend execution of the calling thread until the target @var{thread}
|
||||
terminates, unless the target @var{thread} has already terminated.
|
||||
Wait for @var{thread} to terminate and return its exit value. Threads
|
||||
that have not been created with @code{call-with-new-thread} or
|
||||
@code{scm_spawn_thread} have an exit value of @code{#f}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} thread-exited? thread
|
||||
|
@ -367,49 +285,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-condition-variable")
|
||||
@deffn {Scheme Procedure} make-condition-variable
|
||||
Make a new condition variable.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} make-fair-condition-variable
|
||||
@deffnx {C Function} scm_make_fair_condition_variable ()
|
||||
Make a new fair condition variable.
|
||||
@end deffn
|
||||
|
||||
@c begin (texi-doc-string "guile" "wait-condition-variable")
|
||||
@deffn {Scheme Procedure} wait-condition-variable cond-var mutex [time]
|
||||
Wait until @var{cond-var} has been signalled. While waiting,
|
||||
@var{mutex} is atomically unlocked (as with @code{unlock-mutex}) and
|
||||
is locked again when this function returns. When @var{time} is given,
|
||||
it specifies a point in time where the waiting should be aborted. It
|
||||
can be either a integer as returned by @code{current-time} or a pair
|
||||
as returned by @code{gettimeofday}. When the waiting is aborted,
|
||||
@code{#f} is returned. When the condition variable has in fact been
|
||||
signalled, @code{#t} is returned. The mutex is re-locked in any case
|
||||
before @code{wait-condition-variable} returns.
|
||||
|
||||
When a system async is activated for a thread that is blocked in a
|
||||
call to @code{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.
|
||||
@end deffn
|
||||
|
||||
@c begin (texi-doc-string "guile" "signal-condition-variable")
|
||||
@deffn {Scheme Procedure} signal-condition-variable cond-var
|
||||
Wake up one thread that is waiting for @var{cv}.
|
||||
@end deffn
|
||||
|
||||
@c begin (texi-doc-string "guile" "broadcast-condition-variable")
|
||||
@deffn {Scheme Procedure} broadcast-condition-variable cond-var
|
||||
Wake up all threads that are waiting for @var{cv}.
|
||||
@end deffn
|
||||
|
||||
@node Higher level thread procedures
|
||||
@subsubsection Higher level thread procedures
|
||||
|
||||
@c new by ttn, needs review
|
||||
|
||||
Higher level thread procedures are available by loading the
|
||||
@code{(ice-9 threads)} module. These provide standardized
|
||||
thread creation.
|
||||
|
@ -417,7 +292,8 @@ thread creation.
|
|||
@deffn macro make-thread proc [args@dots{}]
|
||||
Apply @var{proc} to @var{args} in a new thread formed by
|
||||
@code{call-with-new-thread} using a default error handler that display
|
||||
the error to the current error port.
|
||||
the error to the current error port. The @var{args@dots{}}
|
||||
expressions are evaluated in the new thread.
|
||||
@end deffn
|
||||
|
||||
@deffn macro begin-thread first [rest@dots{}]
|
||||
|
@ -426,171 +302,80 @@ Evaluate forms @var{first} and @var{rest} in a new thread formed by
|
|||
the error to the current error port.
|
||||
@end deffn
|
||||
|
||||
@node C level thread interface
|
||||
@subsubsection C level thread interface
|
||||
@node Blocking
|
||||
@subsection Blocking in Guile Mode
|
||||
|
||||
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
|
||||
not allowed or are inconvenient to use.
|
||||
A thread must not block outside of a libguile function while it is in
|
||||
guile mode. The following functions can be used to temporily leave
|
||||
guile mode or to perform some common blocking operations in a supported
|
||||
way.
|
||||
|
||||
Furthermore, they are the primitives that Guile relies on for its own
|
||||
higher level threads. By reimplementing them, you can adapt Guile to
|
||||
different low-level thread implementations.
|
||||
@deftypefn {C Function} scm_t_guile_ticket scm_leave_guile ()
|
||||
Leave guile mode and return a ticket that can be used with
|
||||
@code{scm_enter_guile} to enter it again.
|
||||
|
||||
C code in a thread must call a libguile function periodically. When
|
||||
one thread finds garbage collection is required, it waits for all
|
||||
threads to rendezvous before doing that GC. Such a rendezvous is
|
||||
checked within libguile functions. If C code wants to sleep or block
|
||||
in a thread it should use one of the libguile functions provided.
|
||||
|
||||
Only threads created by Guile can use the libguile functions. Threads
|
||||
created directly with say @code{pthread_create} are unknown to Guile
|
||||
and they cannot call libguile. The stack in such foreign threads is
|
||||
not scanned during GC, so @code{SCM} values generally cannot be held
|
||||
there.
|
||||
|
||||
@c FIXME:
|
||||
@c
|
||||
@c Describe SCM_TICK which can be called if no other libguile
|
||||
@c function is being used by a C function.
|
||||
@c
|
||||
@c Describe "Guile mode", which a thread can enter and exit. There
|
||||
@c are no functions for doing this yet.
|
||||
@c
|
||||
@c When in guile mode a thread can call libguile, is subject to the
|
||||
@c tick rule, and its stack is scanned. When not in guile mode it
|
||||
@c cannot call libguile, it doesn't have to tick, and its stack is
|
||||
@c not scanned. The strange guile control flow things like
|
||||
@c exceptions, continuations and asyncs only occur when in guile
|
||||
@c mode.
|
||||
@c
|
||||
@c When guile mode is exited, the portion of the stack allocated
|
||||
@c while it was in guile mode is still scanned. This portion may not
|
||||
@c be modified when outside guile mode. The stack ends up
|
||||
@c partitioned into alternating guile and non-guile regions.
|
||||
@c
|
||||
@c Leaving guile mode is convenient when running an extended
|
||||
@c calculation not involving guile, since one doesn't need to worry
|
||||
@c about SCM_TICK calls.
|
||||
|
||||
|
||||
@deftp {C Data Type} scm_t_thread
|
||||
This data type represents a thread, to be used with scm_thread_create,
|
||||
etc.
|
||||
@end deftp
|
||||
|
||||
@deftypefn {C Function} int scm_thread_create (scm_t_thread *t, void (*proc)(void *), void *data)
|
||||
Create a new thread that will start by calling @var{proc}, passing it
|
||||
@var{data}. A handle for the new thread is stored in @var{t}, which
|
||||
must be non-NULL. The thread terminated when @var{proc} returns.
|
||||
When the thread has not been detached, its handle remains valid after
|
||||
is has terminated so that it can be used with @var{scm_thread_join},
|
||||
for example. When it has been detached, the handle becomes invalid as
|
||||
soon as the thread terminates.
|
||||
While a thread has left guile mode, it must not call any libguile
|
||||
functions except @code{scm_enter_guile} and must not use any libguile
|
||||
macros. Also, local variables of type @code{SCM} that are allocated
|
||||
while not in guile mode are not protected from the garbage collector.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_thread_detach (scm_t_thread t)
|
||||
Detach the thread @var{t}. See @code{scm_thread_create}.
|
||||
@deftypefn {C Function} void scm_enter_guile (scm_t_guile_ticket ticket)
|
||||
Enter guile mode again.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_thread_join (scm_t_thread t)
|
||||
Wait for thread @var{t} to terminate. The thread must not have been
|
||||
detached at the time that @code{scm_thread_join} is called, but it
|
||||
might have been detached by the time it terminates.
|
||||
@deftypefn {C Function} void *scm_without_guile (void *(*func) (void *), void *data)
|
||||
Leave guile mode, call @var{func} on @var{data}, enter guile mode and
|
||||
return the result of calling @var{func}.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} scm_t_thread scm_thread_self ()
|
||||
Return the handle of the calling thread.
|
||||
@deftypefn {C Function} int scm_pthread_mutex_lock (pthread_mutex_t *mutex)
|
||||
Like @code{pthread_mutex_lock}, but leaves guile mode while waiting for
|
||||
the mutex.
|
||||
@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 condition variable structure pointed to by @var{c}.
|
||||
@deftypefn {C Function} int scm_pthread_cond_wait (pthread_cond_t *cond, pthread_mutex_t *mutex)
|
||||
@deftypefnx {C Function} int scm_pthread_cond_timedwait (pthread_cond_t *cond, pthread_mutex_t *mutex, struct timespec *abstime)
|
||||
Like @code{pthread_cond_wait} and @code{pthread_cond_timedwait}, but
|
||||
leaves guile mode while waiting for the condition variable.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_cond_destroy (scm_t_cond *c)
|
||||
Deallocate all resources associated with @var{c}.
|
||||
@deftypefn {C Function} int scm_std_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
|
||||
Like @code{select} but leaves guile mode while waiting. Also, the
|
||||
delivery of a system async causes this function to be interrupted with
|
||||
error code @code{EINTR}.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_cond_wait (scm_t_cond *c, scm_t_mutex *m)
|
||||
Wait for @var{c} to be signalled. While waiting @var{m} is unlocked
|
||||
and locked again before @code{scm_cond_wait} returns.
|
||||
@deftypefn {C Function} {unsigned int} scm_std_sleep ({unsigned int} seconds)
|
||||
Like @code{sleep}, but leaves guile mode while sleeping. Also, the
|
||||
delivery of a system async causes this function to be interrupted.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_cond_timedwait (scm_t_cond *c, scm_t_mutex *m, timespec *abstime)
|
||||
Wait for @var{c} to be signalled as with @code{scm_cond_wait} but
|
||||
don't wait longer than the point in time specified by @var{abstime}.
|
||||
when the waiting is aborted, zero is returned; non-zero else.
|
||||
@deftypefn {C Function} {unsigned long} scm_std_usleep ({unsigned long} usecs)
|
||||
Like @code{usleep}, but leaves guile mode while sleeping. Also, the
|
||||
delivery of a system async causes this function to be interrupted.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_cond_signal (scm_t_cond *c)
|
||||
Signal the condition variable @var{c}. When one or more threads are
|
||||
waiting for it to be signalled, select one arbitrarily and let its
|
||||
wait succeed.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_cond_broadcast (scm_t_cond *c)
|
||||
Signal the condition variable @var{c}. When there are threads waiting
|
||||
for it to be signalled, wake them all up and make all their waits
|
||||
succeed.
|
||||
@end deftypefn
|
||||
|
||||
@deftp {C Type} scm_t_key
|
||||
This type represents a key for a thread-specific value.
|
||||
@end deftp
|
||||
|
||||
@deftypefn {C Function} void scm_key_create (scm_t_key *keyp)
|
||||
Create a new key for a thread-specific value. Each thread has its own
|
||||
value associated to such a handle. The new handle is stored into
|
||||
@var{keyp}, which must be non-NULL.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_key_delete (scm_t_key key)
|
||||
This function makes @var{key} invalid as a key for thread-specific data.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} void scm_key_setspecific (scm_t_key key, const void *value)
|
||||
Associate @var{value} with @var{key} in the calling thread.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} int scm_key_getspecific (scm_t_key key)
|
||||
Return the value currently associated with @var{key} in the calling
|
||||
thread. When @code{scm_key_setspecific} has not yet been called in
|
||||
this thread with this key, @code{NULL} is returned.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Function} int scm_thread_select (...)
|
||||
This function does the same thing as the system's @code{select}
|
||||
function, but in a way that is friendly to the thread implementation.
|
||||
You should call it in preference to the system @code{select}.
|
||||
@end deftypefn
|
||||
|
||||
@node Fluids
|
||||
@subsection Fluids
|
||||
@node Fluids and Dynamic States
|
||||
@subsection Fluids and Dynamic States
|
||||
|
||||
@cindex fluids
|
||||
|
||||
Fluids are objects to store values in. They have a few properties
|
||||
which make them useful in certain situations: Fluids can have one
|
||||
value per dynamic root (@pxref{Dynamic Roots}), so that changes to the
|
||||
value in a fluid are only visible in the same dynamic root. Since
|
||||
threads are executed in separate dynamic roots, fluids can be used for
|
||||
thread local storage (@pxref{Threads}).
|
||||
A @emph{fluid} is an object that can store one value per @emph{dynamic
|
||||
state}. Each thread has a current dynamic state, and when accessing a
|
||||
fluid, this current dynamic state is used to provide the actual value.
|
||||
In this way, fluids can be used for thread local storage, but they are
|
||||
in fact more flexible: dynamic states are objects of their own and can
|
||||
be made current for more than one thread at the same time, or only be
|
||||
made current temporarily, for example.
|
||||
|
||||
Fluids can be used to simulate the desirable effects of dynamically
|
||||
scoped variables. Dynamically scoped variables are useful when you
|
||||
want to set a variable to a value during some dynamic extent in the
|
||||
execution of your program and have them revert to their original value
|
||||
when the control flow is outside of this dynamic extent. See the
|
||||
description of @code{with-fluids} below for details.
|
||||
Fluids can also be used to simulate the desirable effects of
|
||||
dynamically scoped variables. Dynamically scoped variables are useful
|
||||
when you want to set a variable to a value during some dynamic extent
|
||||
in the execution of your program and have them revert to their
|
||||
original value when the control flow is outside of this dynamic
|
||||
extent. See the description of @code{with-fluids} below for details.
|
||||
|
||||
New fluids are created with @code{make-fluid} and @code{fluid?} is
|
||||
used for testing whether an object is actually a fluid. The values
|
||||
|
@ -600,12 +385,12 @@ stored in a fluid can be accessed with @code{fluid-ref} and
|
|||
@deffn {Scheme Procedure} make-fluid
|
||||
@deffnx {C Function} scm_make_fluid ()
|
||||
Return a newly created fluid.
|
||||
Fluids are objects of a certain type (a smob) that can hold one SCM
|
||||
value per dynamic root. That is, modifications to this value are
|
||||
only visible to code that executes within the same dynamic root as
|
||||
the modifying code. When a new dynamic root is constructed, it
|
||||
inherits the values from its parent. Because each thread executes
|
||||
in its own dynamic root, you can use fluids for thread local storage.
|
||||
Fluids are objects that can hold one
|
||||
value per dynamic state. That is, modifications to this value are
|
||||
only visible to code that executes with the same dynamic state as
|
||||
the modifying code. When a new dynamic state is constructed, it
|
||||
inherits the values from its parent. Because each thread normally executes
|
||||
with its own dynamic state, you can use fluids for thread local storage.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} fluid? obj
|
||||
|
@ -675,6 +460,50 @@ value whenever the frame is entered or left. The backup value is
|
|||
initialized with the @var{val} argument.
|
||||
@end deftypefn
|
||||
|
||||
@deffn {Scheme Procedure} make-dynamic-state [parent]
|
||||
@deffnx {C Function} scm_make_dynamic_state (parent)
|
||||
Return a copy of the dynamic state object @var{parent}
|
||||
or of the current dynamic state when @var{parent} is omitted.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} dynamic-state? obj
|
||||
@deffnx {C Function} scm_dynamic_state_p (obj)
|
||||
Return @code{#t} if @var{obj} is a dynamic state object;
|
||||
return @code{#f} otherwise.
|
||||
@end deffn
|
||||
|
||||
@deftypefn {C Procedure} int scm_is_dynamic_state (SCM obj)
|
||||
Return non-zero if @var{obj} is a dynamic state object;
|
||||
return zero otherwise.
|
||||
@end deftypefn
|
||||
|
||||
@deffn {Scheme Procedure} current-dynamic-state
|
||||
@deffnx {C Function} scm_current_dynamic_state ()
|
||||
Return the current dynamic state object.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} set-current-dynamic-state state
|
||||
@deffnx {C Function} scm_set_current_dynamic_state (state)
|
||||
Set the current dynamic state object to @var{state}
|
||||
and return the previous current dynamic state object.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} with-dynamic-state state proc
|
||||
@deffnx {C Function} scm_with_dynamic_state (state, proc)
|
||||
Call @var{proc} while @var{state} is the current dynamic
|
||||
state object.
|
||||
@end deffn
|
||||
|
||||
@deftypefn {C Procedure} void scm_frame_current_dynamic_state (SCM state)
|
||||
Set the current dynamic state to @var{state} for the dynamic extent of
|
||||
the current frame.
|
||||
@end deftypefn
|
||||
|
||||
@deftypefn {C Procedure} void *scm_c_with_dynamic_state (SCM state, void *(*func)(void *), void *data)
|
||||
Like @code{scm_with_dynamic_state}, but call @var{func} with
|
||||
@var{data}.
|
||||
@end deftypefn
|
||||
|
||||
@node Futures
|
||||
@subsection Futures
|
||||
@cindex futures
|
||||
|
@ -798,9 +627,10 @@ completed, it doesn't need to wait for all to finish.
|
|||
@end deffn
|
||||
|
||||
|
||||
@node Mutexes
|
||||
@subsection Mutexes
|
||||
@node Mutexes and Condition Variables
|
||||
@subsection Mutexes and Condition Variables
|
||||
@cindex mutex
|
||||
@cindex condition variable
|
||||
|
||||
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
|
||||
|
@ -808,9 +638,10 @@ 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.
|
||||
There are two types of mutexes in Guile, ``standard'' and
|
||||
``recursive''. They're created by @code{make-mutex} and
|
||||
@code{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
|
||||
|
@ -821,43 +652,31 @@ 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.
|
||||
Return a new standard mutex. It is initially unlocked.
|
||||
@end deffn
|
||||
|
||||
@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.
|
||||
@deffn {Scheme Procedure} make-recursive-mutex
|
||||
Return a new recursive mutex. It is initialloy unlocked.
|
||||
@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 standard mutexes (@code{make-mutex}), and error is signalled if
|
||||
the thread has itself already locked @var{mutex}.
|
||||
|
||||
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.
|
||||
For a recursive mutex (@code{make-recursive-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.
|
||||
executed. When the async returns, the wait resumes.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} try-mutex mutex
|
||||
@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
|
||||
|
@ -865,13 +684,43 @@ the return is @code{#f}.
|
|||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} unlock-mutex mutex
|
||||
Unlock @var{mutex}.
|
||||
Unlock @var{mutex}. An error is signalled if @var{mutex} is not
|
||||
locked by the calling thread.
|
||||
@end deffn
|
||||
|
||||
For a standard mutex (@code{make-mutex}), if @var{mutex} is not locked
|
||||
by the calling thread then behaviour is unspecified.
|
||||
@c begin (texi-doc-string "guile" "make-condition-variable")
|
||||
@deffn {Scheme Procedure} make-condition-variable
|
||||
Return a new condition variable.
|
||||
@end deffn
|
||||
|
||||
For a fair mutex (@code{make-fair-mutex}), if @var{mutex} is not
|
||||
locked by the calling thread then an error is thrown.
|
||||
@c begin (texi-doc-string "guile" "wait-condition-variable")
|
||||
@deffn {Scheme Procedure} wait-condition-variable cond-var mutex [time]
|
||||
Wait until @var{cond-var} has been signalled. While waiting,
|
||||
@var{mutex} is atomically unlocked (as with @code{unlock-mutex}) and
|
||||
is locked again when this function returns. When @var{time} is given,
|
||||
it specifies a point in time where the waiting should be aborted. It
|
||||
can be either a integer as returned by @code{current-time} or a pair
|
||||
as returned by @code{gettimeofday}. When the waiting is aborted,
|
||||
@code{#f} is returned. When the condition variable has in fact been
|
||||
signalled, @code{#t} is returned. The mutex is re-locked in any case
|
||||
before @code{wait-condition-variable} returns.
|
||||
|
||||
When a system async is activated for a thread that is blocked in a
|
||||
call to @code{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.
|
||||
@end deffn
|
||||
|
||||
@c begin (texi-doc-string "guile" "signal-condition-variable")
|
||||
@deffn {Scheme Procedure} signal-condition-variable cond-var
|
||||
Wake up one thread that is waiting for @var{cv}.
|
||||
@end deffn
|
||||
|
||||
@c begin (texi-doc-string "guile" "broadcast-condition-variable")
|
||||
@deffn {Scheme Procedure} broadcast-condition-variable cond-var
|
||||
Wake up all threads that are waiting for @var{cv}.
|
||||
@end deffn
|
||||
|
||||
@sp 1
|
||||
|
@ -908,38 +757,6 @@ 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"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@c -*-texinfo-*-
|
||||
@c This is part of the GNU Guile Reference Manual.
|
||||
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004
|
||||
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004, 2005
|
||||
@c Free Software Foundation, Inc.
|
||||
@c See the file guile.texi for copying conditions.
|
||||
|
||||
|
@ -8,12 +8,12 @@
|
|||
@node General Libguile Concepts
|
||||
@section General concepts for using libguile
|
||||
|
||||
When you want to embed the Guile Scheme interpreter into your program,
|
||||
you need to link it against the @file{libguile} library (@pxref{Linking
|
||||
Programs With Guile}). Once you have done this, your C code has access
|
||||
to a number of data types and functions that can be used to invoke the
|
||||
interpreter, or make new functions that you have written in C available
|
||||
to be called from Scheme code, among other things.
|
||||
When you want to embed the Guile Scheme interpreter into your program or
|
||||
library, you need to link it against the @file{libguile} library
|
||||
(@pxref{Linking Programs With Guile}). Once you have done this, your C
|
||||
code has access to a number of data types and functions that can be used
|
||||
to invoke the interpreter, or make new functions that you have written
|
||||
in C available to be called from Scheme code, among other things.
|
||||
|
||||
Scheme is different from C in a number of significant ways, and Guile
|
||||
tries to make the advantages of Scheme available to C as well. Thus, in
|
||||
|
@ -26,10 +26,16 @@ You need to understand how libguile offers them to C programs in order
|
|||
to use the rest of libguile. Also, the more general control flow of
|
||||
Scheme caused by continuations needs to be dealt with.
|
||||
|
||||
Running asynchronous signal handlers and multi-threading is known to C
|
||||
code already, but there are of course a few additional rules when using
|
||||
them together with libguile.
|
||||
|
||||
@menu
|
||||
* Dynamic Types:: Dynamic Types.
|
||||
* Garbage Collection:: Garbage Collection.
|
||||
* Control Flow:: Control Flow.
|
||||
* Asynchronous Signals:: Asynchronous Signals
|
||||
* Multi-Threading:: Multi-Threading
|
||||
@end menu
|
||||
|
||||
@node Dynamic Types
|
||||
|
@ -377,3 +383,204 @@ corresponding @code{scm_internal_dynamic_wind} function, but it might
|
|||
prefer to use the @dfn{frames} concept that is more natural for C code,
|
||||
(@pxref{Frames}).
|
||||
|
||||
@node Asynchronous Signals
|
||||
@subsection Asynchronous Signals
|
||||
|
||||
You can not call libguile functions from handlers for POSIX signals, but
|
||||
you can register Scheme handlers for POSIX signals such as
|
||||
@code{SIGINT}. These handlers do not run during the actual signal
|
||||
delivery. Instead, they are run when the program (more precisely, the
|
||||
thread that the handler has been registered for) reaches the next
|
||||
@emph{safe point}.
|
||||
|
||||
The libguile functions themselves have many such safe points.
|
||||
Consequently, you must be prepared for arbitrary actions anytime you
|
||||
call a libguile function. For example, even @code{scm_cons} can contain
|
||||
a safe point and when a signal handler is pending for your thread,
|
||||
calling @code{scm_cons} will run this handler and anything might happen,
|
||||
including a non-local exit although @code{scm_cons} would not ordinarily
|
||||
do such a thing on its own.
|
||||
|
||||
If you do not want to allow the running of asynchronous signal handlers,
|
||||
you can block them temporarily with @code{scm_frame_block_asyncs}, for
|
||||
example. See @xref{System asyncs}.
|
||||
|
||||
Since signal handling in Guile relies on safe points, you need to make
|
||||
sure that your functions do offer enough of them. Normally, calling
|
||||
libguile functions in the normal course of action is all that is needed.
|
||||
But when a thread might spent a long time in a code section that calls
|
||||
no libguile function, it is good to include explicit safe points. This
|
||||
can allow the user to interrupt your code with @key{C-c}, for example.
|
||||
|
||||
You can do this with the macro @code{SCM_TICK}. This macro is
|
||||
syntactically a statement. That is, you could use it like this:
|
||||
|
||||
@example
|
||||
while (1)
|
||||
@{
|
||||
SCM_TICK;
|
||||
do_some_work ();
|
||||
@}
|
||||
@end example
|
||||
|
||||
Frequent execution of a safe point is even more important in multi
|
||||
threaded programs, @xref{Multi-Threading}.
|
||||
|
||||
@node Multi-Threading
|
||||
@subsection Multi-Threading
|
||||
|
||||
Guile can be used in multi-threaded programs just as well as in
|
||||
single-threaded ones.
|
||||
|
||||
Each thread that wants to use functions from libguile must put itself
|
||||
into @emph{guile mode} and must then follow a few rules. If it doesn't
|
||||
want to honor these rules in certain situations, a thread can
|
||||
temporarily leave guile mode (but can no longer use libguile functions
|
||||
during that time, of course).
|
||||
|
||||
Threads enter guile mode by calling @code{scm_with_guile},
|
||||
@code{scm_boot_guile}, or @code{scm_init_guile}. As explained in the
|
||||
reference documentation for these functions, Guile will then learn about
|
||||
the stack bounds of the thread and can protect the @code{SCM} values
|
||||
that are stored in local variables. When a thread puts itself into
|
||||
guile mode for the first time, it gets a Scheme representation and is
|
||||
listed by @code{all-threads}, for example.
|
||||
|
||||
While in guile mode, a thread promises to reach a safe point reasonably
|
||||
frequently (@pxref{Asynchronous Signals}). In addition to running
|
||||
signal handlers, these points are also potential rendezvous points of
|
||||
all guile mode threads where Guile can orchestrate global things like
|
||||
garbage collection. Consequently, when a thread in guile mode blocks
|
||||
and does no longer frequent safe points, it might cause all other guile
|
||||
mode threads to block as well. To prevent this from happening, a guile
|
||||
mode thread should either only block in libguile functions (who know how
|
||||
to do it right), or should temporarily leave guile mode with
|
||||
@code{scm_without_guile} or
|
||||
@code{scm_leave_guile}/@code{scm_enter_guile}.
|
||||
|
||||
For some common blocking operations, Guile provides convenience
|
||||
functions. For example, if you want to lock a pthread mutex while in
|
||||
guile mode, you might want to use @code{scm_pthread_mutex_lock} which is
|
||||
just like @code{pthread_mutex_lock} except that it leaves guile mode
|
||||
while blocking.
|
||||
|
||||
|
||||
All libguile functions are (intended to be) robust in the face of
|
||||
multiple threads using them concurrently. This means that there is no
|
||||
risk of the internal data structures of libguile becoming corrupted in
|
||||
such a way that the process crashes.
|
||||
|
||||
A program might still produce non-sensical results, though. Taking
|
||||
hashtables as an example, Guile guarantees that you can use them from
|
||||
multiple threads concurrently and a hashtable will always remain a valid
|
||||
hashtable and Guile will not crash when you access it. It does not
|
||||
guarantee, however, that inserting into it concurrently from two threads
|
||||
will give useful results: only one insertion might actually happen, none
|
||||
might happen, or the table might in general be modified in a totally
|
||||
arbitrary manner. (It will still be a valid hashtable, but not the one
|
||||
that you might have expected.) Guile might also signal an error when it
|
||||
detects a harmful race condition.
|
||||
|
||||
Thus, you need to put in additional synchronizations when multiple
|
||||
threads want to use a single hashtable, or any other mutable Scheme
|
||||
object.
|
||||
|
||||
When writing C code for use with libguile, you should try to make it
|
||||
robust as well. An example that converts a list into a vector will help
|
||||
to illustrate. Here is a correct version:
|
||||
|
||||
@example
|
||||
SCM
|
||||
my_list_to_vector (SCM list)
|
||||
@{
|
||||
SCM vector = scm_make_vector (scm_length (list), SCM_UNDEFINED);
|
||||
size_t len, i;
|
||||
|
||||
len = SCM_SIMPLE_VECTOR_LENGTH (vector);
|
||||
i = 0;
|
||||
while (i < len && scm_is_pair (list))
|
||||
@{
|
||||
SCM_SIMPLE_VECTOR_SET (vector, i, SCM_CAR (list));
|
||||
list = SCM_CDR (list);
|
||||
i++;
|
||||
@}
|
||||
|
||||
return vector;
|
||||
@}
|
||||
@end example
|
||||
|
||||
The first thing to note is that storing into a @code{SCM} location
|
||||
concurrently from multiple threads is guaranteed to be robust: you don't
|
||||
know which value wins but it will in any case be a valid @code{SCM}
|
||||
value.
|
||||
|
||||
But there is no guarantee that the list referenced by @var{list} is not
|
||||
modified in another thread while the loop iterates over it. Thus, while
|
||||
copying its elements into the vector, the list might get longer or
|
||||
shorter. For this reason, the loop must check both that it doesn't
|
||||
overrun the vector (@code{SCM_SIMPLE_VECTOR_SET} does no range-checking)
|
||||
and that it doesn't overrung the list (@code{SCM_CAR} and @code{SCM_CDR}
|
||||
likewise do no type checking).
|
||||
|
||||
It is safe to use @code{SCM_CAR} and @code{SCM_CDR} on the local
|
||||
variable @var{list} once it is known that the variable contains a pair.
|
||||
The contents of the pair might change spontaneously, but it will always
|
||||
stay a valid pair (and a local variable will of course not spontaneously
|
||||
point to a different Scheme object).
|
||||
|
||||
Likewise, a simple vector such as the one returned by
|
||||
@code{scm_make_vector} is guaranteed to always stay the same length so
|
||||
that it is safe to only use SCM_SIMPLE_VECTOR_LENGTH once and store the
|
||||
result. (In the example, @var{vector} is safe anyway since it is a
|
||||
fresh object that no other thread can possibly know about until it is
|
||||
returned from @code{my_list_to_vector}.)
|
||||
|
||||
Of course the behavior of @code{my_list_to_vector} is suboptimal when
|
||||
@var{list} does indeed gets asynchronously lengthened or shortened in
|
||||
another thread. But it is robust: it will always return a valid vector.
|
||||
That vector might be shorter than expected, or its last elements might
|
||||
be unspecified, but it is a valid vector and if a program wants to rule
|
||||
out these cases, it must avoid modifying the list asynchronously.
|
||||
|
||||
Here is another version that is also correct:
|
||||
|
||||
@example
|
||||
SCM
|
||||
my_pedantic_list_to_vector (SCM list)
|
||||
@{
|
||||
SCM vector = scm_make_vector (scm_length (list), SCM_UNDEFINED);
|
||||
size_t len, i;
|
||||
|
||||
len = SCM_SIMPLE_VECTOR_LENGTH (vector);
|
||||
i = 0;
|
||||
while (i < len)
|
||||
@{
|
||||
SCM_SIMPLE_VECTOR_SET (vector, i, scm_car (list));
|
||||
list = scm_cdr (list);
|
||||
i++;
|
||||
@}
|
||||
|
||||
return vector;
|
||||
@}
|
||||
@end example
|
||||
|
||||
This version uses the type-checking and thread-robust functions
|
||||
@code{scm_car} and @code{scm_cdr} instead of the faster, but less robust
|
||||
macros @code{SCM_CAR} and @code{SCM_CDR}. When the list is shortened
|
||||
(that is, when @var{list} holds a non-pair), @code{scm_car} will throw
|
||||
an error. This might be preferable to just returning a half-initialized
|
||||
vector.
|
||||
|
||||
The API for accessing vectors and arrays of various kinds from C takes a
|
||||
slightly different approach to thread-robustness. In order to get at
|
||||
the raw memory that stores the elements of an array, you need to
|
||||
@emph{reserve} that array as long as you need the raw memory. During
|
||||
the time an array is reserved, its elements can still spontaneously
|
||||
change their values, but the memory itself and other things like the
|
||||
size of the array are guaranteed to stay fixed. Any operation that
|
||||
would change these parameters of an array that is currently reserved
|
||||
will signal an error. In order to avoid these errors, a program should
|
||||
of course put suitable synchronization mechanisms in place. As you can
|
||||
see, Guile itself is again only concerned about robustness, not about
|
||||
correctness: without proper synchronization, your program will likely
|
||||
not be correct, but the worst consequence is an error message.
|
||||
|
|
|
@ -32,7 +32,7 @@ Convert the homogeneous numeric vector @var{uvec} to a list.
|
|||
|
||||
@deffn {Scheme Procedure} make-u8vector len [fill]
|
||||
@deffnx {C Function} scm_make_u8vector (len, fill)
|
||||
Return a newly allocated homogeneous numeric vector which can
|
||||
Return a newly allocated uniform numeric vector which can
|
||||
hold @var{len} elements. If @var{fill} is given, it is used to
|
||||
initialize the elements, otherwise the contents of the vector
|
||||
is unspecified.
|
||||
|
@ -40,18 +40,235 @@ is unspecified.
|
|||
|
||||
@deffn {Scheme Procedure} u8vector . l
|
||||
@deffnx {C Function} scm_u8vector (l)
|
||||
Return a newly allocated homogeneous numeric vector containing
|
||||
Return a newly allocated uniform numeric vector containing
|
||||
all argument values.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} list->u8vector l
|
||||
@deffnx {C Function} scm_list_to_u8vector (l)
|
||||
Convert the list @var{l} to a numeric homogeneous vector.
|
||||
Convert the list @var{l} to a numeric uniform vector.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} any->u8vector obj
|
||||
@deffnx {C Function} scm_any_to_u8vector (obj)
|
||||
Convert @var{obj}, which can be a list, vector, or
|
||||
homogenous vector, to a numeric homogenous vector of
|
||||
uniform vector, to a numeric uniform vector of
|
||||
type u8.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} with-continuation-barrier proc
|
||||
@deffnx {C Function} scm_with_continuation_barrier (proc)
|
||||
Call @var{proc} and return the returned value but do not allow the invocation of continuations that would exit or reenter the dynamic extent of the call to @var{proc}. When a uncaught throw happens during the call to @var{proc}, a message is printed to the current error port and @code{#f} is returned.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} dynamic-state? obj
|
||||
@deffnx {C Function} scm_dynamic_state_p (obj)
|
||||
Return @code{#t} if @var{obj} is a dynamic state object;
|
||||
return @code{#f} otherwise
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} current-dynamic-state
|
||||
@deffnx {C Function} scm_current_dynamic_state ()
|
||||
Return the current dynamic state object.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} set-current-dynamic-state state
|
||||
@deffnx {C Function} scm_set_current_dynamic_state (state)
|
||||
Set the current dynamic state object to @var{state}
|
||||
and return the previous current dynamic state object.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} with-dynamic-state state proc
|
||||
@deffnx {C Function} scm_with_dynamic_state (state, proc)
|
||||
Call @var{proc} while @var{state} is the current dynamic
|
||||
state object.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} call-with-dynamic-root thunk handler
|
||||
@deffnx {C Function} scm_call_with_dynamic_root (thunk, handler)
|
||||
Evaluate @code{(thunk)} in a new dynamic context, returning its value.
|
||||
|
||||
If an error occurs during evaluation, apply @var{handler} to the
|
||||
arguments to the throw, just as @code{throw} would. If this happens,
|
||||
@var{handler} is called outside the scope of the new root -- it is
|
||||
called in the same dynamic context in which
|
||||
@code{call-with-dynamic-root} was evaluated.
|
||||
|
||||
If @var{thunk} captures a continuation, the continuation is rooted at
|
||||
the call to @var{thunk}. In particular, the call to
|
||||
@code{call-with-dynamic-root} is not captured. Therefore,
|
||||
@code{call-with-dynamic-root} always returns at most one time.
|
||||
|
||||
Before calling @var{thunk}, the dynamic-wind chain is un-wound back to
|
||||
the root and a new chain started for @var{thunk}. Therefore, this call
|
||||
may not do what you expect:
|
||||
|
||||
@lisp
|
||||
;; Almost certainly a bug:
|
||||
(with-output-to-port
|
||||
some-port
|
||||
|
||||
(lambda ()
|
||||
(call-with-dynamic-root
|
||||
(lambda ()
|
||||
(display 'fnord)
|
||||
(newline))
|
||||
(lambda (errcode) errcode))))
|
||||
@end lisp
|
||||
|
||||
The problem is, on what port will @samp{fnord} be displayed? You
|
||||
might expect that because of the @code{with-output-to-port} that
|
||||
it will be displayed on the port bound to @code{some-port}. But it
|
||||
probably won't -- before evaluating the thunk, dynamic winds are
|
||||
unwound, including those created by @code{with-output-to-port}.
|
||||
So, the standard output port will have been re-set to its default value
|
||||
before @code{display} is evaluated.
|
||||
|
||||
(This function was added to Guile mostly to help calls to functions in C
|
||||
libraries that can not tolerate non-local exits or calls that return
|
||||
multiple times. If such functions call back to the interpreter, it should
|
||||
be under a new dynamic root.)
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} dynamic-root
|
||||
@deffnx {C Function} scm_dynamic_root ()
|
||||
Return an object representing the current dynamic root.
|
||||
|
||||
These objects are only useful for comparison using @code{eq?}.
|
||||
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} uniform-vector-ref v idx
|
||||
@deffnx {C Function} scm_uniform_vector_ref (v, idx)
|
||||
Return the element at index @var{idx} of the
|
||||
homogenous numeric vector @var{v}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} uniform-vector-length v
|
||||
@deffnx {C Function} scm_uniform_vector_length (v)
|
||||
Return the number of elements in the uniform vector @var{v}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} uniform-vector-read! uvec [port_or_fd [start [end]]]
|
||||
@deffnx {C Function} scm_uniform_vector_read_x (uvec, port_or_fd, start, end)
|
||||
Fill the elements of @var{uvec} by reading
|
||||
raw bytes from @var{port-or-fdes}, using host byte order.
|
||||
|
||||
The optional arguments @var{start} (inclusive) and @var{end}
|
||||
(exclusive) allow a specified region to be read,
|
||||
leaving the remainder of the vector unchanged.
|
||||
|
||||
When @var{port-or-fdes} is a port, all specified elements
|
||||
of @var{uvec} are attempted to be read, potentially blocking
|
||||
while waiting formore input or end-of-file.
|
||||
When @var{port-or-fd} is an integer, a single call to
|
||||
read(2) is made.
|
||||
|
||||
An error is signalled when the last element has only
|
||||
been partially filled before reaching end-of-file or in
|
||||
the single call to read(2).
|
||||
|
||||
@code{uniform-vector-read!} returns the number of elements
|
||||
read.
|
||||
|
||||
@var{port-or-fdes} may be omitted, in which case it defaults
|
||||
to the value returned by @code{(current-input-port)}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} uniform-vector-write uvec [port_or_fd [start [end]]]
|
||||
@deffnx {C Function} scm_uniform_vector_write (uvec, port_or_fd, start, end)
|
||||
Write the elements of @var{uvec} as raw bytes to
|
||||
@var{port-or-fdes}, in the host byte order.
|
||||
|
||||
The optional arguments @var{start} (inclusive)
|
||||
and @var{end} (exclusive) allow
|
||||
a specified region to be written.
|
||||
|
||||
When @var{port-or-fdes} is a port, all specified elements
|
||||
of @var{uvec} are attempted to be written, potentially blocking
|
||||
while waiting for more room.
|
||||
When @var{port-or-fd} is an integer, a single call to
|
||||
write(2) is made.
|
||||
|
||||
An error is signalled when the last element has only
|
||||
been partially written in the single call to write(2).
|
||||
|
||||
The number of objects actually written is returned.
|
||||
@var{port-or-fdes} may be
|
||||
omitted, in which case it defaults to the value returned by
|
||||
@code{(current-output-port)}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} string-any-c-code char_pred s [start [end]]
|
||||
@deffnx {C Function} scm_string_any (char_pred, s, start, end)
|
||||
Check if the predicate @var{pred} is true for any character in
|
||||
the string @var{s}.
|
||||
|
||||
Calls to @var{pred} are made from left to right across @var{s}.
|
||||
When it returns true (ie.@: non-@code{#f}), that return value
|
||||
is the return from @code{string-any}.
|
||||
|
||||
The SRFI-13 specification requires that the call to @var{pred}
|
||||
on the last character of @var{s} (assuming that point is
|
||||
reached) be a tail call, but currently in Guile this is not the
|
||||
case.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} string-every-c-code char_pred s [start [end]]
|
||||
@deffnx {C Function} scm_string_every (char_pred, s, start, end)
|
||||
Check if the predicate @var{pred} is true for every character
|
||||
in the string @var{s}.
|
||||
|
||||
Calls to @var{pred} are made from left to right across @var{s}.
|
||||
If the predicate is true for every character then the return
|
||||
value from the last @var{pred} call is the return from
|
||||
@code{string-every}.
|
||||
|
||||
If there are no characters in @var{s} (ie.@: @var{start} equals
|
||||
@var{end}) then the return is @code{#t}.
|
||||
|
||||
The SRFI-13 specification requires that the call to @var{pred}
|
||||
on the last character of @var{s} (assuming that point is
|
||||
reached) be a tail call, but currently in Guile this is not the
|
||||
case.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} make-recursive-mutex
|
||||
@deffnx {C Function} scm_make_recursive_mutex ()
|
||||
Create a new recursive mutex.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} vector-copy vec
|
||||
@deffnx {C Function} scm_vector_copy (vec)
|
||||
Return a copy of @var{vec}.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} dimensions->uniform-array dims prot [fill]
|
||||
@deffnx {Scheme Procedure} make-uniform-vector length prototype [fill]
|
||||
@deffnx {C Function} scm_dimensions_to_uniform_array (dims, prot, fill)
|
||||
Create and return a uniform array or vector of type
|
||||
corresponding to @var{prototype} with dimensions @var{dims} or
|
||||
length @var{length}. If @var{fill} is supplied, it's used to
|
||||
fill the array, otherwise @var{prototype} is used.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} list->uniform-array ndim prot lst
|
||||
@deffnx {C Function} scm_list_to_uniform_array (ndim, prot, lst)
|
||||
Return a uniform array of the type indicated by prototype
|
||||
@var{prot} with elements the same as those of @var{lst}.
|
||||
Elements must be of the appropriate type, no coercions are
|
||||
done.
|
||||
|
||||
The argument @var{ndim} determines the number of dimensions
|
||||
of the array. It is either an exact integer, giving the
|
||||
number directly, or a list of exact integers, whose length
|
||||
specifies the number of dimensions and each element is the
|
||||
lower index bound of its dimension.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} array-prototype ra
|
||||
@deffnx {C Function} scm_array_prototype (ra)
|
||||
Return an object that would produce an array of the same type
|
||||
as @var{array}, if used as the @var{prototype} for
|
||||
@code{make-uniform-array}.
|
||||
@end deffn
|
||||
|
|
|
@ -2356,22 +2356,23 @@ scope and the result from that @var{thunk} is the return from
|
|||
@code{with-parameters*}.
|
||||
|
||||
This function is a Guile-specific addition to the SRFI, it's similar
|
||||
to the core @code{with-fluids*} (@pxref{Fluids}).
|
||||
to the core @code{with-fluids*} (@pxref{Fluids and Dynamic States}).
|
||||
@end defun
|
||||
|
||||
|
||||
@sp 1
|
||||
Parameter objects are implemented using fluids (@pxref{Fluids}), so
|
||||
each dynamic root has it's own parameter locations. That includes the
|
||||
separate locations when outside any @code{parameterize} form. When a
|
||||
parameter is created it gets a separate initial location in each
|
||||
dynamic root, all initialized to the given @var{init} value.
|
||||
Parameter objects are implemented using fluids (@pxref{Fluids and
|
||||
Dynamic States}), so each dynamic state has it's own parameter
|
||||
locations. That includes the separate locations when outside any
|
||||
@code{parameterize} form. When a parameter is created it gets a
|
||||
separate initial location in each dynamic state, all initialized to
|
||||
the given @var{init} value.
|
||||
|
||||
As alluded to above, because each thread is a separate dynamic root,
|
||||
each thread has it's own locations behind parameter objects, and
|
||||
changes in one thread are not visible to any other. When a new
|
||||
dynamic root or thread is created, the values of parameters in the
|
||||
originating context are copied, into new locations.
|
||||
As alluded to above, because each thread usually has a separate
|
||||
dynamic state, each thread has it's own locations behind parameter
|
||||
objects, and changes in one thread are not visible to any other. When
|
||||
a new dynamic state or thread is created, the values of parameters in
|
||||
the originating context are copied, into new locations.
|
||||
|
||||
SRFI-39 doesn't specify the interaction between parameter objects and
|
||||
threads, so the threading behaviour described here should be regarded
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue