summaryrefslogtreecommitdiff
path: root/doc/ref
diff options
context:
space:
mode:
authorMarius Vollmer <mvo@zagadka.de>2005-01-24 19:14:54 +0000
committerMarius Vollmer <mvo@zagadka.de>2005-01-24 19:14:54 +0000
commita54a94b39707f47a1f30533bcf7664094d65d073 (patch)
tree69a524a4c3fbb084e1e2fef05da61e1852700909 /doc/ref
parentbe1b896c82273d97b79cd839d7281b46e54920f8 (diff)
downloadguile-a54a94b39707f47a1f30533bcf7664094d65d073.tar.gz
Threading changes.
Diffstat (limited to 'doc/ref')
-rw-r--r--doc/ref/api-init.texi128
-rw-r--r--doc/ref/api-scheduling.texi2
-rw-r--r--doc/ref/libguile-concepts.texi221
3 files changed, 302 insertions, 49 deletions
diff --git a/doc/ref/api-init.texi b/doc/ref/api-init.texi
index 94407114b..795b6d5f9 100644
--- a/doc/ref/api-init.texi
+++ b/doc/ref/api-init.texi
@@ -8,50 +8,96 @@
@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.
-
-@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.
-
-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.
-
-See @code{scm_init_guile}, below, for a function that can find the real
-base of the stack, but not in a portable way.
+Each thread that wants to use function 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.
+
+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}.
+
+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.
+
+@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}.
+
+When a throw happens while @var{func} runs (such as a signalled error)
+that is not caught, a short message is printed to the current error port
+and @code{scm_with_guile} returns @code{NULL}. When a continuation is
+invoked that would make the control flow cross this call to
+@code{scm_with_guile}, an error will be signalled at the point of
+continuation invokation. Thus, @code{scm_with_guile} guaranteed to
+return 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.
-
-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.
-
-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.
+Arrange things so as if all of the code of the current thread would be
+executed 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).
+
+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.
+
+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 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})
diff --git a/doc/ref/api-scheduling.texi b/doc/ref/api-scheduling.texi
index dd830fa34..7abaed331 100644
--- a/doc/ref/api-scheduling.texi
+++ b/doc/ref/api-scheduling.texi
@@ -113,7 +113,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.
diff --git a/doc/ref/libguile-concepts.texi b/doc/ref/libguile-concepts.texi
index 870c051f3..7fc0a6c34 100644
--- a/doc/ref/libguile-concepts.texi
+++ b/doc/ref/libguile-concepts.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.