mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-24 20:30:28 +02:00
cancel-thread via asyncs, not pthread_cancel
* module/ice-9/threads.scm (cancel-tag): New variable. (cancel-thread): New Scheme function. (call-with-new-thread): Install a prompt around the thread. * libguile/threads.h (scm_i_thread): Remove cancelled member. * libguile/threads.c (scm_cancel_thread): Call out to Scheme. Always available, and works on the current thread too. (scm_set_thread_cleanup_x, scm_thread_cleanup): Adapt. (scm_init_ice_9_threads): Capture cancel-thread var. * doc/ref/api-scheduling.texi (Threads): Update. * NEWS: Update.
This commit is contained in:
parent
c957ec7ab0
commit
a04739b31a
5 changed files with 44 additions and 43 deletions
3
NEWS
3
NEWS
|
@ -38,6 +38,9 @@ trivial unused data structure. Now that we have deprecated the old
|
||||||
only refer to "asyncs".
|
only refer to "asyncs".
|
||||||
|
|
||||||
* Bug fixes
|
* Bug fixes
|
||||||
|
** cancel-thread uses asynchronous interrupts, not pthread_cancel
|
||||||
|
|
||||||
|
See "Asyncs" in the manual, for more on asynchronous interrupts.
|
||||||
|
|
||||||
|
|
||||||
Previous changes in 2.1.x (changes since the 2.0.x series):
|
Previous changes in 2.1.x (changes since the 2.0.x series):
|
||||||
|
|
|
@ -114,17 +114,14 @@ immediate context switch to one of them. Otherwise, yield has no effect.
|
||||||
|
|
||||||
@deffn {Scheme Procedure} cancel-thread thread
|
@deffn {Scheme Procedure} cancel-thread thread
|
||||||
@deffnx {C Function} scm_cancel_thread (thread)
|
@deffnx {C Function} scm_cancel_thread (thread)
|
||||||
Asynchronously notify @var{thread} to exit. Immediately after
|
Asynchronously interrupt @var{thread} and ask it to terminate.
|
||||||
receiving this notification, @var{thread} will call its cleanup handler
|
@code{dynamic-wind} post thunks will run, but throw handlers will not.
|
||||||
(if one has been set) and then terminate, aborting any evaluation that
|
If @var{thread} has already terminated or been signaled to terminate,
|
||||||
is in progress.
|
this function is a no-op.
|
||||||
|
|
||||||
Because Guile threads are isomorphic with POSIX threads, @var{thread}
|
Under this hood, thread cancellation uses @code{system-async-mark} and
|
||||||
will not receive its cancellation signal until it reaches a cancellation
|
@code{abort-to-prompt}. @xref{Asyncs} for more on asynchronous
|
||||||
point. See your operating system's POSIX threading documentation for
|
interrupts.
|
||||||
more information on cancellation points; note that in Guile, unlike
|
|
||||||
native POSIX threads, a thread can receive a cancellation notification
|
|
||||||
while attempting to lock a mutex.
|
|
||||||
@end deffn
|
@end deffn
|
||||||
|
|
||||||
@deffn {Scheme Procedure} set-thread-cleanup! thread proc
|
@deffn {Scheme Procedure} set-thread-cleanup! thread proc
|
||||||
|
|
|
@ -438,7 +438,6 @@ guilify_self_1 (struct GC_stack_base *base)
|
||||||
abort ();
|
abort ();
|
||||||
|
|
||||||
scm_i_pthread_mutex_init (&t.admin_mutex, NULL);
|
scm_i_pthread_mutex_init (&t.admin_mutex, NULL);
|
||||||
t.canceled = 0;
|
|
||||||
t.exited = 0;
|
t.exited = 0;
|
||||||
t.guile_mode = 0;
|
t.guile_mode = 0;
|
||||||
|
|
||||||
|
@ -1012,34 +1011,14 @@ SCM_DEFINE (scm_yield, "yield", 0, 0, 0,
|
||||||
/* Some systems, notably Android, lack 'pthread_cancel'. Don't provide
|
/* Some systems, notably Android, lack 'pthread_cancel'. Don't provide
|
||||||
'cancel-thread' on these systems. */
|
'cancel-thread' on these systems. */
|
||||||
|
|
||||||
#if !SCM_USE_PTHREAD_THREADS || defined HAVE_PTHREAD_CANCEL
|
static SCM cancel_thread_var;
|
||||||
|
|
||||||
SCM_DEFINE (scm_cancel_thread, "cancel-thread", 1, 0, 0,
|
SCM
|
||||||
(SCM thread),
|
scm_cancel_thread (SCM thread)
|
||||||
"Asynchronously force the target @var{thread} to terminate. @var{thread} "
|
|
||||||
"cannot be the current thread, and if @var{thread} has already terminated or "
|
|
||||||
"been signaled to terminate, this function is a no-op.")
|
|
||||||
#define FUNC_NAME s_scm_cancel_thread
|
|
||||||
{
|
{
|
||||||
scm_i_thread *t = NULL;
|
scm_call_1 (scm_variable_ref (cancel_thread_var), thread);
|
||||||
|
|
||||||
SCM_VALIDATE_THREAD (1, thread);
|
|
||||||
t = SCM_I_THREAD_DATA (thread);
|
|
||||||
scm_i_scm_pthread_mutex_lock (&t->admin_mutex);
|
|
||||||
if (!t->canceled)
|
|
||||||
{
|
|
||||||
t->canceled = 1;
|
|
||||||
scm_i_pthread_mutex_unlock (&t->admin_mutex);
|
|
||||||
scm_i_pthread_cancel (t->pthread);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scm_i_pthread_mutex_unlock (&t->admin_mutex);
|
|
||||||
|
|
||||||
return SCM_UNSPECIFIED;
|
return SCM_UNSPECIFIED;
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SCM_DEFINE (scm_set_thread_cleanup_x, "set-thread-cleanup!", 2, 0, 0,
|
SCM_DEFINE (scm_set_thread_cleanup_x, "set-thread-cleanup!", 2, 0, 0,
|
||||||
(SCM thread, SCM proc),
|
(SCM thread, SCM proc),
|
||||||
|
@ -1056,7 +1035,7 @@ SCM_DEFINE (scm_set_thread_cleanup_x, "set-thread-cleanup!", 2, 0, 0,
|
||||||
t = SCM_I_THREAD_DATA (thread);
|
t = SCM_I_THREAD_DATA (thread);
|
||||||
scm_i_pthread_mutex_lock (&t->admin_mutex);
|
scm_i_pthread_mutex_lock (&t->admin_mutex);
|
||||||
|
|
||||||
if (!(t->exited || t->canceled))
|
if (!t->exited)
|
||||||
t->cleanup_handler = proc;
|
t->cleanup_handler = proc;
|
||||||
|
|
||||||
scm_i_pthread_mutex_unlock (&t->admin_mutex);
|
scm_i_pthread_mutex_unlock (&t->admin_mutex);
|
||||||
|
@ -1077,7 +1056,7 @@ SCM_DEFINE (scm_thread_cleanup, "thread-cleanup", 1, 0, 0,
|
||||||
|
|
||||||
t = SCM_I_THREAD_DATA (thread);
|
t = SCM_I_THREAD_DATA (thread);
|
||||||
scm_i_pthread_mutex_lock (&t->admin_mutex);
|
scm_i_pthread_mutex_lock (&t->admin_mutex);
|
||||||
ret = (t->exited || t->canceled) ? SCM_BOOL_F : t->cleanup_handler;
|
ret = t->exited ? SCM_BOOL_F : t->cleanup_handler;
|
||||||
scm_i_pthread_mutex_unlock (&t->admin_mutex);
|
scm_i_pthread_mutex_unlock (&t->admin_mutex);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2073,6 +2052,9 @@ scm_init_ice_9_threads (void *unused)
|
||||||
{
|
{
|
||||||
#include "libguile/threads.x"
|
#include "libguile/threads.x"
|
||||||
|
|
||||||
|
cancel_thread_var =
|
||||||
|
scm_module_variable (scm_current_module (),
|
||||||
|
scm_from_latin1_symbol ("cancel-thread"));
|
||||||
call_with_new_thread_var =
|
call_with_new_thread_var =
|
||||||
scm_module_variable (scm_current_module (),
|
scm_module_variable (scm_current_module (),
|
||||||
scm_from_latin1_symbol ("call-with-new-thread"));
|
scm_from_latin1_symbol ("call-with-new-thread"));
|
||||||
|
|
|
@ -68,7 +68,6 @@ typedef struct scm_i_thread {
|
||||||
scm_i_pthread_mutex_t *held_mutex;
|
scm_i_pthread_mutex_t *held_mutex;
|
||||||
|
|
||||||
SCM result;
|
SCM result;
|
||||||
int canceled;
|
|
||||||
int exited;
|
int exited;
|
||||||
|
|
||||||
/* Boolean indicating whether the thread is in guile mode. */
|
/* Boolean indicating whether the thread is in guile mode. */
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
(define-module (ice-9 threads)
|
(define-module (ice-9 threads)
|
||||||
#:use-module (ice-9 match)
|
#:use-module (ice-9 match)
|
||||||
|
#:use-module (ice-9 control)
|
||||||
;; These bindings are marked as #:replace because when deprecated code
|
;; These bindings are marked as #:replace because when deprecated code
|
||||||
;; is enabled, (ice-9 deprecated) also exports these names.
|
;; is enabled, (ice-9 deprecated) also exports these names.
|
||||||
;; (Referencing one of the deprecated names prints a warning directing
|
;; (Referencing one of the deprecated names prints a warning directing
|
||||||
|
@ -86,6 +87,21 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(define cancel-tag (make-prompt-tag "cancel"))
|
||||||
|
(define (cancel-thread thread)
|
||||||
|
"Asynchronously interrupt the target @var{thread} and ask it to
|
||||||
|
terminate. @code{dynamic-wind} post thunks will run, but throw handlers
|
||||||
|
will not. If @var{thread} has already terminated or been signaled to
|
||||||
|
terminate, this function is a no-op."
|
||||||
|
(system-async-mark
|
||||||
|
(lambda ()
|
||||||
|
(catch #t
|
||||||
|
(lambda ()
|
||||||
|
(abort-to-prompt cancel-tag))
|
||||||
|
(lambda _
|
||||||
|
(error "thread cancellation failed, throwing error instead???"))))
|
||||||
|
thread))
|
||||||
|
|
||||||
(define* (call-with-new-thread thunk #:optional handler)
|
(define* (call-with-new-thread thunk #:optional handler)
|
||||||
"Call @code{thunk} in a new thread and with a new dynamic state,
|
"Call @code{thunk} in a new thread and with a new dynamic state,
|
||||||
returning a new thread object representing the thread. The procedure
|
returning a new thread object representing the thread. The procedure
|
||||||
|
@ -106,11 +122,15 @@ Once @var{thunk} or @var{handler} returns, the return value is made the
|
||||||
(with-mutex mutex
|
(with-mutex mutex
|
||||||
(%call-with-new-thread
|
(%call-with-new-thread
|
||||||
(lambda ()
|
(lambda ()
|
||||||
(lock-mutex mutex)
|
(call-with-prompt cancel-tag
|
||||||
(set! thread (current-thread))
|
(lambda ()
|
||||||
(signal-condition-variable cv)
|
(lock-mutex mutex)
|
||||||
(unlock-mutex mutex)
|
(set! thread (current-thread))
|
||||||
(thunk)))
|
(signal-condition-variable cv)
|
||||||
|
(unlock-mutex mutex)
|
||||||
|
(thunk))
|
||||||
|
(lambda (k . args)
|
||||||
|
(apply values args)))))
|
||||||
(let lp ()
|
(let lp ()
|
||||||
(unless thread
|
(unless thread
|
||||||
(wait-condition-variable cv mutex)
|
(wait-condition-variable cv mutex)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue