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".
|
||||
|
||||
* 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):
|
||||
|
|
|
@ -114,17 +114,14 @@ immediate context switch to one of them. Otherwise, yield has no effect.
|
|||
|
||||
@deffn {Scheme Procedure} cancel-thread thread
|
||||
@deffnx {C Function} scm_cancel_thread (thread)
|
||||
Asynchronously notify @var{thread} to exit. Immediately after
|
||||
receiving this notification, @var{thread} will call its cleanup handler
|
||||
(if one has been set) and then terminate, aborting any evaluation that
|
||||
is in progress.
|
||||
Asynchronously interrupt @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.
|
||||
|
||||
Because Guile threads are isomorphic with POSIX threads, @var{thread}
|
||||
will not receive its cancellation signal until it reaches a cancellation
|
||||
point. See your operating system's POSIX threading documentation for
|
||||
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.
|
||||
Under this hood, thread cancellation uses @code{system-async-mark} and
|
||||
@code{abort-to-prompt}. @xref{Asyncs} for more on asynchronous
|
||||
interrupts.
|
||||
@end deffn
|
||||
|
||||
@deffn {Scheme Procedure} set-thread-cleanup! thread proc
|
||||
|
|
|
@ -438,7 +438,6 @@ guilify_self_1 (struct GC_stack_base *base)
|
|||
abort ();
|
||||
|
||||
scm_i_pthread_mutex_init (&t.admin_mutex, NULL);
|
||||
t.canceled = 0;
|
||||
t.exited = 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
|
||||
'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 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
|
||||
scm_cancel_thread (SCM thread)
|
||||
{
|
||||
scm_i_thread *t = NULL;
|
||||
|
||||
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);
|
||||
|
||||
scm_call_1 (scm_variable_ref (cancel_thread_var), thread);
|
||||
return SCM_UNSPECIFIED;
|
||||
}
|
||||
#undef FUNC_NAME
|
||||
|
||||
#endif
|
||||
|
||||
SCM_DEFINE (scm_set_thread_cleanup_x, "set-thread-cleanup!", 2, 0, 0,
|
||||
(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);
|
||||
scm_i_pthread_mutex_lock (&t->admin_mutex);
|
||||
|
||||
if (!(t->exited || t->canceled))
|
||||
if (!t->exited)
|
||||
t->cleanup_handler = proc;
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
return ret;
|
||||
|
@ -2073,6 +2052,9 @@ scm_init_ice_9_threads (void *unused)
|
|||
{
|
||||
#include "libguile/threads.x"
|
||||
|
||||
cancel_thread_var =
|
||||
scm_module_variable (scm_current_module (),
|
||||
scm_from_latin1_symbol ("cancel-thread"));
|
||||
call_with_new_thread_var =
|
||||
scm_module_variable (scm_current_module (),
|
||||
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 result;
|
||||
int canceled;
|
||||
int exited;
|
||||
|
||||
/* Boolean indicating whether the thread is in guile mode. */
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
|
||||
(define-module (ice-9 threads)
|
||||
#:use-module (ice-9 match)
|
||||
#:use-module (ice-9 control)
|
||||
;; These bindings are marked as #:replace because when deprecated code
|
||||
;; is enabled, (ice-9 deprecated) also exports these names.
|
||||
;; (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)
|
||||
"Call @code{thunk} in a new thread and with a new dynamic state,
|
||||
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
|
||||
(%call-with-new-thread
|
||||
(lambda ()
|
||||
(lock-mutex mutex)
|
||||
(set! thread (current-thread))
|
||||
(signal-condition-variable cv)
|
||||
(unlock-mutex mutex)
|
||||
(thunk)))
|
||||
(call-with-prompt cancel-tag
|
||||
(lambda ()
|
||||
(lock-mutex mutex)
|
||||
(set! thread (current-thread))
|
||||
(signal-condition-variable cv)
|
||||
(unlock-mutex mutex)
|
||||
(thunk))
|
||||
(lambda (k . args)
|
||||
(apply values args)))))
|
||||
(let lp ()
|
||||
(unless thread
|
||||
(wait-condition-variable cv mutex)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue