mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-12 06:41:13 +02:00
Move call-with-new-thread to Scheme
* libguile/threads.c (scm_call_with_new_thread): Trampoline to Scheme. (launch_data, really_launch, scm_sys_call_with_new_thread): Simplify. (scm_init_ice_9_threads): Capture call-with-new-thread variable. * module/ice-9/threads.scm (call-with-new-thread): Add implementation in Scheme. Should allow for easier cancel-thread via prompt abort.
This commit is contained in:
parent
9807d2dced
commit
f3bfe29235
2 changed files with 62 additions and 52 deletions
|
@ -858,34 +858,29 @@ scm_without_guile (void *(*func)(void *), void *data)
|
||||||
|
|
||||||
/*** Thread creation */
|
/*** Thread creation */
|
||||||
|
|
||||||
|
/* Because (ice-9 boot-9) loads up (ice-9 threads), we know that this
|
||||||
|
variable will get loaded before a call to scm_call_with_new_thread
|
||||||
|
and therefore no lock or pthread_once_t is needed. */
|
||||||
|
static SCM call_with_new_thread_var;
|
||||||
|
|
||||||
|
SCM
|
||||||
|
scm_call_with_new_thread (SCM thunk, SCM handler)
|
||||||
|
{
|
||||||
|
SCM call_with_new_thread = scm_variable_ref (call_with_new_thread_var);
|
||||||
|
if (SCM_UNBNDP (handler))
|
||||||
|
return scm_call_1 (call_with_new_thread, thunk);
|
||||||
|
return scm_call_2 (call_with_new_thread, thunk, handler);
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
SCM parent;
|
SCM parent;
|
||||||
SCM thunk;
|
SCM thunk;
|
||||||
SCM handler;
|
|
||||||
SCM thread;
|
|
||||||
scm_i_pthread_mutex_t mutex;
|
|
||||||
scm_i_pthread_cond_t cond;
|
|
||||||
} launch_data;
|
} launch_data;
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
really_launch (void *d)
|
really_launch (void *d)
|
||||||
{
|
{
|
||||||
launch_data *data = (launch_data *)d;
|
SCM_I_CURRENT_THREAD->result = scm_call_0 (((launch_data *)d)->thunk);
|
||||||
SCM thunk = data->thunk, handler = data->handler;
|
|
||||||
scm_i_thread *t;
|
|
||||||
|
|
||||||
t = SCM_I_CURRENT_THREAD;
|
|
||||||
|
|
||||||
scm_i_scm_pthread_mutex_lock (&data->mutex);
|
|
||||||
data->thread = scm_current_thread ();
|
|
||||||
scm_i_pthread_cond_signal (&data->cond);
|
|
||||||
scm_i_pthread_mutex_unlock (&data->mutex);
|
|
||||||
|
|
||||||
if (SCM_UNBNDP (handler))
|
|
||||||
t->result = scm_call_0 (thunk);
|
|
||||||
else
|
|
||||||
t->result = scm_catch (SCM_BOOL_T, thunk, handler);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -898,51 +893,29 @@ launch_thread (void *d)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCM_DEFINE (scm_call_with_new_thread, "call-with-new-thread", 1, 1, 0,
|
SCM_INTERNAL SCM scm_sys_call_with_new_thread (SCM);
|
||||||
(SCM thunk, SCM handler),
|
SCM_DEFINE (scm_sys_call_with_new_thread, "%call-with-new-thread", 1, 0, 0,
|
||||||
"Call @code{thunk} in a new thread and with a new dynamic state,\n"
|
(SCM thunk), "")
|
||||||
"returning a new thread object representing the thread. The procedure\n"
|
#define FUNC_NAME s_scm_sys_call_with_new_thread
|
||||||
"@var{thunk} is called via @code{with-continuation-barrier}.\n"
|
|
||||||
"\n"
|
|
||||||
"When @var{handler} is specified, then @var{thunk} is called from\n"
|
|
||||||
"within a @code{catch} with tag @code{#t} that has @var{handler} as its\n"
|
|
||||||
"handler. This catch is established inside the continuation barrier.\n"
|
|
||||||
"\n"
|
|
||||||
"Once @var{thunk} or @var{handler} returns, the return value is made\n"
|
|
||||||
"the @emph{exit value} of the thread and the thread is terminated.")
|
|
||||||
#define FUNC_NAME s_scm_call_with_new_thread
|
|
||||||
{
|
{
|
||||||
launch_data data;
|
launch_data *data;
|
||||||
scm_i_pthread_t id;
|
scm_i_pthread_t id;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
SCM_ASSERT (scm_is_true (scm_thunk_p (thunk)), thunk, SCM_ARG1, FUNC_NAME);
|
SCM_ASSERT (scm_is_true (scm_thunk_p (thunk)), thunk, SCM_ARG1, FUNC_NAME);
|
||||||
SCM_ASSERT (SCM_UNBNDP (handler) || scm_is_true (scm_procedure_p (handler)),
|
|
||||||
handler, SCM_ARG2, FUNC_NAME);
|
|
||||||
|
|
||||||
GC_collect_a_little ();
|
GC_collect_a_little ();
|
||||||
data.parent = scm_current_dynamic_state ();
|
data = scm_gc_typed_calloc (launch_data);
|
||||||
data.thunk = thunk;
|
data->parent = scm_current_dynamic_state ();
|
||||||
data.handler = handler;
|
data->thunk = thunk;
|
||||||
data.thread = SCM_BOOL_F;
|
err = scm_i_pthread_create (&id, NULL, launch_thread, data);
|
||||||
scm_i_pthread_mutex_init (&data.mutex, NULL);
|
|
||||||
scm_i_pthread_cond_init (&data.cond, NULL);
|
|
||||||
|
|
||||||
scm_i_scm_pthread_mutex_lock (&data.mutex);
|
|
||||||
err = scm_i_pthread_create (&id, NULL, launch_thread, &data);
|
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
scm_i_pthread_mutex_unlock (&data.mutex);
|
|
||||||
errno = err;
|
errno = err;
|
||||||
scm_syserror (NULL);
|
scm_syserror (NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (scm_is_false (data.thread))
|
return SCM_UNSPECIFIED;
|
||||||
scm_i_scm_pthread_cond_wait (&data.cond, &data.mutex);
|
|
||||||
|
|
||||||
scm_i_pthread_mutex_unlock (&data.mutex);
|
|
||||||
|
|
||||||
return data.thread;
|
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
@ -2097,6 +2070,10 @@ static void
|
||||||
scm_init_ice_9_threads (void *unused)
|
scm_init_ice_9_threads (void *unused)
|
||||||
{
|
{
|
||||||
#include "libguile/threads.x"
|
#include "libguile/threads.x"
|
||||||
|
|
||||||
|
call_with_new_thread_var =
|
||||||
|
scm_module_variable (scm_current_module (),
|
||||||
|
scm_from_latin1_symbol ("call-with-new-thread"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -86,6 +86,39 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(define* (call-with-new-thread thunk #:optional handler)
|
||||||
|
"Call @code{thunk} in a new thread and with a new dynamic state,
|
||||||
|
returning a new thread object representing the thread. The procedure
|
||||||
|
@var{thunk} is called via @code{with-continuation-barrier}.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
Once @var{thunk} or @var{handler} returns, the return value is made the
|
||||||
|
@emph{exit value} of the thread and the thread is terminated."
|
||||||
|
(let ((cv (make-condition-variable))
|
||||||
|
(mutex (make-mutex))
|
||||||
|
(thunk (if handler
|
||||||
|
(lambda () (catch #t thunk handler))
|
||||||
|
thunk))
|
||||||
|
(thread #f))
|
||||||
|
(with-mutex mutex
|
||||||
|
(%call-with-new-thread
|
||||||
|
(lambda ()
|
||||||
|
(lock-mutex mutex)
|
||||||
|
(set! thread (current-thread))
|
||||||
|
(signal-condition-variable cv)
|
||||||
|
(unlock-mutex mutex)
|
||||||
|
(thunk)))
|
||||||
|
(let lp ()
|
||||||
|
(unless thread
|
||||||
|
(wait-condition-variable cv mutex)
|
||||||
|
(lp))))
|
||||||
|
thread))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;; Macros first, so that the procedures expand correctly.
|
;;; Macros first, so that the procedures expand correctly.
|
||||||
|
|
||||||
(define-syntax-rule (begin-thread e0 e1 ...)
|
(define-syntax-rule (begin-thread e0 e1 ...)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue