1
0
Fork 0
cl-sites/guile.html_node/Multi_002dThreading.html

253 lines
12 KiB
HTML
Raw Normal View History

2024-12-17 12:49:28 +01:00
<!DOCTYPE html>
<html>
<!-- Created by GNU Texinfo 7.1, https://www.gnu.org/software/texinfo/ -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!-- This manual documents Guile version 3.0.10.
Copyright (C) 1996-1997, 2000-2005, 2009-2023 Free Software Foundation,
Inc.
Copyright (C) 2021 Maxime Devos
Copyright (C) 2024 Tomas Volf
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with no
Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A
copy of the license is included in the section entitled "GNU Free
Documentation License." -->
<title>Multi-Threading (Guile Reference Manual)</title>
<meta name="description" content="Multi-Threading (Guile Reference Manual)">
<meta name="keywords" content="Multi-Threading (Guile Reference Manual)">
<meta name="resource-type" content="document">
<meta name="distribution" content="global">
<meta name="Generator" content=".texi2any-real">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link href="index.html" rel="start" title="Top">
<link href="Concept-Index.html" rel="index" title="Concept Index">
<link href="index.html#SEC_Contents" rel="contents" title="Table of Contents">
<link href="General-Libguile-Concepts.html" rel="up" title="General Libguile Concepts">
<link href="Asynchronous-Signals.html" rel="prev" title="Asynchronous Signals">
<style type="text/css">
<!--
a.copiable-link {visibility: hidden; text-decoration: none; line-height: 0em}
div.example {margin-left: 3.2em}
span:hover a.copiable-link {visibility: visible}
-->
</style>
<link rel="stylesheet" type="text/css" href="https://www.gnu.org/software/gnulib/manual.css">
</head>
<body lang="en">
<div class="subsection-level-extent" id="Multi_002dThreading">
<div class="nav-panel">
<p>
Previous: <a href="Asynchronous-Signals.html" accesskey="p" rel="prev">Asynchronous Signals</a>, Up: <a href="General-Libguile-Concepts.html" accesskey="u" rel="up">General concepts for using libguile</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Concept-Index.html" title="Index" rel="index">Index</a>]</p>
</div>
<hr>
<h4 class="subsection" id="Multi_002dThreading-1"><span>5.4.5 Multi-Threading<a class="copiable-link" href="#Multi_002dThreading-1"> &para;</a></span></h4>
<p>Guile can be used in multi-threaded programs just as well as in
single-threaded ones.
</p>
<p>Each thread that wants to use functions from libguile must put itself
into <em class="emph">guile mode</em> and must then follow a few rules. If it doesn&rsquo;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).
</p>
<p>Threads enter guile mode by calling <code class="code">scm_with_guile</code>,
<code class="code">scm_boot_guile</code>, or <code class="code">scm_init_guile</code>. 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 class="code">SCM</code> 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 class="code">all-threads</code>, for example.
</p>
<p>Threads in guile mode can block (e.g., do blocking I/O) without causing
any problems<a class="footnote" id="DOCF5" href="#FOOT5"><sup>5</sup></a>; temporarily leaving guile mode with
<code class="code">scm_without_guile</code> before blocking slightly improves GC
performance, though. 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 class="code">scm_pthread_mutex_lock</code> which is just like
<code class="code">pthread_mutex_lock</code> except that it leaves guile mode while
blocking.
</p>
<p>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.
</p>
<p>A program might still produce nonsensical 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.
</p>
<p>Thus, you need to put in additional synchronizations when multiple
threads want to use a single hashtable, or any other mutable Scheme
object.
</p>
<p>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:
</p>
<div class="example">
<pre class="example-preformatted">SCM
my_list_to_vector (SCM list)
{
SCM vector = scm_make_vector (scm_length (list), SCM_UNDEFINED);
size_t len, i;
len = scm_c_vector_length (vector);
i = 0;
while (i &lt; len &amp;&amp; scm_is_pair (list))
{
scm_c_vector_set_x (vector, i, scm_car (list));
list = scm_cdr (list);
i++;
}
return vector;
}
</pre></div>
<p>The first thing to note is that storing into a <code class="code">SCM</code> location
concurrently from multiple threads is guaranteed to be robust: you don&rsquo;t
know which value wins but it will in any case be a valid <code class="code">SCM</code>
value.
</p>
<p>But there is no guarantee that the list referenced by <var class="var">list</var> 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&rsquo;t
overrun the vector and that it doesn&rsquo;t overrun the list. Otherwise,
<code class="code">scm_c_vector_set_x</code> would raise an error if the index is out of
range, and <code class="code">scm_car</code> and <code class="code">scm_cdr</code> would raise an error if the
value is not a pair.
</p>
<p>It is safe to use <code class="code">scm_car</code> and <code class="code">scm_cdr</code> on the local
variable <var class="var">list</var> 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).
</p>
<p>Likewise, a vector such as the one returned by <code class="code">scm_make_vector</code> is
guaranteed to always stay the same length so that it is safe to only use
scm_c_vector_length once and store the result. (In the example,
<var class="var">vector</var> is safe anyway since it is a fresh object that no other
thread can possibly know about until it is returned from
<code class="code">my_list_to_vector</code>.)
</p>
<p>Of course the behavior of <code class="code">my_list_to_vector</code> is suboptimal when
<var class="var">list</var> does indeed get 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.
</p>
<p>Here is another version that is also correct:
</p>
<div class="example">
<pre class="example-preformatted">SCM
my_pedantic_list_to_vector (SCM list)
{
SCM vector = scm_make_vector (scm_length (list), SCM_UNDEFINED);
size_t len, i;
len = scm_c_vector_length (vector);
i = 0;
while (i &lt; len)
{
scm_c_vector_set_x (vector, i, scm_car (list));
list = scm_cdr (list);
i++;
}
return vector;
}
</pre></div>
<p>This version relies on the error-checking behavior of <code class="code">scm_car</code> and
<code class="code">scm_cdr</code>. When the list is shortened (that is, when <var class="var">list</var>
holds a non-pair), <code class="code">scm_car</code> will throw an error. This might be
preferable to just returning a half-initialized vector.
</p>
<p>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
<em class="emph">reserve</em> 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.
</p>
<p>Real thread-safety often requires that a critical section of code is
executed in a certain restricted manner. A common requirement is that
the code section is not entered a second time when it is already being
executed. Locking a mutex while in that section ensures that no other
thread will start executing it, blocking asyncs ensures that no
asynchronous code enters the section again from the current thread, and
the error checking of Guile mutexes guarantees that an error is
signaled when the current thread accidentally reenters the critical
section via recursive function calls.
</p>
<p>Guile provides two mechanisms to support critical sections as outlined
above. You can either use the macros
<code class="code">SCM_CRITICAL_SECTION_START</code> and <code class="code">SCM_CRITICAL_SECTION_END</code>
for very simple sections; or use a dynwind context together with a
call to <code class="code">scm_dynwind_critical_section</code>.
</p>
<p>The macros only work reliably for critical sections that are
guaranteed to not cause a non-local exit. They also do not detect an
accidental reentry by the current thread. Thus, you should probably
only use them to delimit critical sections that do not contain calls
to libguile functions or to other external functions that might do
complicated things.
</p>
<p>The function <code class="code">scm_dynwind_critical_section</code>, on the other hand,
will correctly deal with non-local exits because it requires a dynwind
context. Also, by using a separate mutex for each critical section,
it can detect accidental reentries.
</p>
</div>
<div class="footnotes-segment">
<hr>
<h4 class="footnotes-heading">Footnotes</h4>
<h5 class="footnote-body-heading"><a id="FOOT5" href="#DOCF5">(5)</a></h5>
<p>In Guile 1.8, a thread blocking in guile mode
would prevent garbage collection to occur. Thus, threads had to leave
guile mode whenever they could block. This is no longer needed with
Guile 2.<var class="var">x</var>.</p>
</div>
<hr>
<div class="nav-panel">
<p>
Previous: <a href="Asynchronous-Signals.html">Asynchronous Signals</a>, Up: <a href="General-Libguile-Concepts.html">General concepts for using libguile</a> &nbsp; [<a href="index.html#SEC_Contents" title="Table of contents" rel="contents">Contents</a>][<a href="Concept-Index.html" title="Index" rel="index">Index</a>]</p>
</div>
</body>
</html>