From mboxrd@z Thu Jan 1 00:00:00 1970 Path: main.gmane.org!not-for-mail From: Marius Vollmer Newsgroups: gmane.lisp.guile.user Subject: Some introductory docs about C level threading Date: Fri, 21 Jan 2005 19:29:35 +0100 Message-ID: NNTP-Posting-Host: deer.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: sea.gmane.org 1106333683 1019 80.91.229.6 (21 Jan 2005 18:54:43 GMT) X-Complaints-To: usenet@sea.gmane.org NNTP-Posting-Date: Fri, 21 Jan 2005 18:54:43 +0000 (UTC) Original-X-From: guile-user-bounces+guile-user=m.gmane.org@gnu.org Fri Jan 21 19:54:37 2005 Return-path: Original-Received: from lists.gnu.org ([199.232.76.165]) by deer.gmane.org with esmtp (Exim 3.35 #1 (Debian)) id 1Cs3vZ-0007PM-00 for ; Fri, 21 Jan 2005 19:54:37 +0100 Original-Received: from localhost ([127.0.0.1] helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Cs47d-0005f6-D5 for guile-user@m.gmane.org; Fri, 21 Jan 2005 14:07:05 -0500 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1Cs47Y-0005eH-Qt for guile-user@gnu.org; Fri, 21 Jan 2005 14:07:00 -0500 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1Cs47Y-0005do-1r for guile-user@gnu.org; Fri, 21 Jan 2005 14:07:00 -0500 Original-Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1Cs40Q-000486-Fe for guile-user@gnu.org; Fri, 21 Jan 2005 13:59:38 -0500 Original-Received: from [129.217.163.1] (helo=mail.dt.e-technik.uni-dortmund.de) by monty-python.gnu.org with esmtp (Exim 4.34) id 1Cs3XO-0004ho-S2 for guile-user@gnu.org; Fri, 21 Jan 2005 13:29:39 -0500 Original-Received: from localhost (localhost [127.0.0.1]) by mail.dt.e-technik.uni-dortmund.de (Postfix) with ESMTP id 0969F44234 for ; Fri, 21 Jan 2005 19:29:37 +0100 (CET) Original-Received: from mail.dt.e-technik.uni-dortmund.de ([127.0.0.1]) by localhost (krusty [127.0.0.1]) (amavisd-new, port 10024) with LMTP id 26132-03-2 for ; Fri, 21 Jan 2005 19:29:36 +0100 (CET) Original-Received: from troy.dt.e-technik.uni-dortmund.de (troy.dt.e-technik.uni-dortmund.de [129.217.163.17]) by mail.dt.e-technik.uni-dortmund.de (Postfix) with ESMTP id 68B5A44232 for ; Fri, 21 Jan 2005 19:29:36 +0100 (CET) Original-Received: by troy.dt.e-technik.uni-dortmund.de (Postfix, from userid 520) id A57F3B9A7; Fri, 21 Jan 2005 19:29:35 +0100 (CET) Original-To: guile-user@gnu.org User-Agent: Gnus/5.1003 (Gnus v5.10.3) Emacs/21.3 (gnu/linux) X-Virus-Scanned: by amavisd-new at dt.e-technik.uni-dortmund.de X-BeenThere: guile-user@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: General Guile related discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: guile-user-bounces+guile-user=m.gmane.org@gnu.org Errors-To: guile-user-bounces+guile-user=m.gmane.org@gnu.org Xref: main.gmane.org gmane.lisp.guile.user:4126 X-Report-Spam: http://spam.gmane.org/gmane.lisp.guile.user:4126 Hi, this describes the model for C level threading for Guile 1.8. What do you think? 4.3.4 Asynchronous Signals -------------------------- You can not call libguile functions from handlers for POSIX signals, but you can register Scheme handlers for POSIX signals such as `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 _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 `scm_cons' can contain a safe point and when a signal handler is pending for your thread, calling `scm_cons' will run this handler and anything might happen, including a non-local exit although `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 `scm_frame_block_asyncs', for example. See *Note 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 , for example. You can do this with the macro `SCM_TICK'. This macro is syntactically a statement. That is, you could use it like this: while (1) { SCM_TICK; do_some_work (); } Frequent execution of a safe point is even more important in multi threaded programs, *Note Multi-Threading::. 4.3.5 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 _guile mode_ and must then follow a few rules. If it doesn't want to honer 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 `scm_with_guile', `scm_boot_guile', or `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 `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 `all-threads', for example. While in guile mode, a thread promises to reach a safe point reasonably frequently (*note 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 `scm_without_guile' or `scm_leave_guile'/`scm_enter_guile'. For some comming 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 `scm_pthread_mutex_lock' which is just like `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 hash tables 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.) 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: 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; } The first thing to note is that storing into a `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 `SCM' value. But there is no guarantee that the list referenced by 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 (`SCM_SIMPLE_VECTOR_SET' does no range-checking) and that it doesn't overrung the list (`SCM_CAR' and `SCM_CDR' likewise do no type checking). It is safe to use `SCM_CAR' and `SCM_CDR' on the local variable 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 `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, VECTOR is safe anyway since it is a fresh object that no other thread can possibly know about until it is returned from `my_list_to_vector'.) Of course the behavior of `my_list_to_vector' is suboptimal when 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 modifiying the list asynchronously. Here is another version that is also correct: 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; } This version uses the type-checking and thread-robust functions `scm_car' and `scm_cdr' instead of the faster, but less robust macros `SCM_CAR' and `SCM_CDR'. When the list is shortened (that is, when LIST holds a non-pair), `scm_car' will throw an error. This might be preferable to just returning a half-initialized vector. _______________________________________________ Guile-user mailing list Guile-user@gnu.org http://lists.gnu.org/mailman/listinfo/guile-user