* Semaphores::
* Waitqueue/condition variables::
* Sessions/Debugging::
-* Implementation (Linux x86)::
+* Foreign threads::
+* Implementation (Linux x86/x86-64)::
@end menu
@node Threading basics
@code{sb-ext:quit} terminates all threads in the current session, but
leaves other sessions running.
-@node Implementation (Linux x86)
+@node Foreign threads
+@comment node-name, next, previous, up
+@section Foreign threads
+
+Direct calls to @code{pthread_create} (instead of @code{MAKE-THREAD})
+create threads that SBCL is not aware of, these are called foreign
+threads. Currently, it is not possible to run Lisp code in such
+threads. This means that the Lisp side signal handlers cannot work.
+The best solution is to start foreign threads with signals blocked,
+but since third party libraries may create threads, it is not always
+feasible to do so. As a workaround, upon receiving a signal in a
+foreign thread, SBCL changes the thread's sigmask to block all signals
+that it wants to handle and resends the signal to the current process
+which should land in a thread that does not block it, that is, a Lisp
+thread.
+
+The resignalling trick cannot work for synchronously triggered signals
+(SIGSEGV and co), take care not to trigger any. Resignalling for
+synchronously triggered signals in foreign threads is subject to
+@code{--lose-on-corruption}, see @ref{Runtime Options}.
+
+@node Implementation (Linux x86/x86-64)
@comment node-name, next, previous, up
@section Implementation (Linux x86/x86-64)
#include "genesis/simple-fun.h"
#include "genesis/cons.h"
+/* When we catch an internal error, should we pass it back to Lisp to
+ * be handled in a high-level way? (Early in cold init, the answer is
+ * 'no', because Lisp is still too brain-dead to handle anything.
+ * After sufficient initialization has been completed, the answer
+ * becomes 'yes'.) */
+boolean internal_errors_enabled = 0;
+
+#ifndef LISP_FEATURE_WIN32
+static
+void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
+#endif
+union interrupt_handler interrupt_handlers[NSIG];
+
/* Under Linux on some architectures, we appear to have to restore the
* FPU control word from the context, as after the signal is delivered
* we appear to have a null FPU control word. */
os_context_t *context = arch_os_get_context(&void_context);
#endif
+/* Foreign code may want to start some threads on its own.
+ * Non-targetted, truly asynchronous signals can be delivered to
+ * basically any thread, but invoking Lisp handlers in such foregign
+ * threads is really bad, so let's resignal it.
+ *
+ * This should at least bring attention to the problem, but it cannot
+ * work for SIGSEGV and similar. It is good enough for timers, and
+ * maybe all deferrables. */
+
+static void
+add_handled_signals(sigset_t *sigset)
+{
+ int i;
+ for(i = 1; i < NSIG; i++) {
+ if (!(ARE_SAME_HANDLER(interrupt_low_level_handlers[i], SIG_DFL)) ||
+ !(ARE_SAME_HANDLER(interrupt_handlers[i].c, SIG_DFL))) {
+ sigaddset(sigset, i);
+ }
+ }
+}
+
+void block_signals(sigset_t *what, sigset_t *where, sigset_t *old);
+
+static boolean
+maybe_resignal_to_lisp_thread(int signal, os_context_t *context)
+{
+#ifdef LISP_FEATURE_SB_THREAD
+ if (!pthread_getspecific(lisp_thread)) {
+ if (!(sigismember(&deferrable_sigset,signal))) {
+ corruption_warning_and_maybe_lose
+ ("Received signal %d in non-lisp thread %lu, resignalling to a lisp thread.",
+ signal,
+ pthread_self());
+ }
+ {
+ sigset_t sigset;
+ sigemptyset(&sigset);
+ add_handled_signals(&sigset);
+ block_signals(&sigset, 0, 0);
+ block_signals(&sigset, os_context_sigmask_addr(context), 0);
+ kill(getpid(), signal);
+ }
+ return 1;
+ } else
+#endif
+ return 0;
+}
+
/* These are to be used in signal handlers. Currently all handlers are
* called from one of:
*
* kernel properly, so we fix it up ourselves in the
* arch_os_get_context(..) function. -- CSR, 2002-07-23
*/
-#define SAVE_ERRNO(context,void_context) \
+#define SAVE_ERRNO(signal,context,void_context) \
{ \
int _saved_errno = errno; \
RESTORE_FP_CONTROL_WORD(context,void_context); \
+ if (!maybe_resignal_to_lisp_thread(signal, context)) \
{
#define RESTORE_ERRNO \
}
#endif
}
-
-/* When we catch an internal error, should we pass it back to Lisp to
- * be handled in a high-level way? (Early in cold init, the answer is
- * 'no', because Lisp is still too brain-dead to handle anything.
- * After sufficient initialization has been completed, the answer
- * becomes 'yes'.) */
-boolean internal_errors_enabled = 0;
-
-#ifndef LISP_FEATURE_WIN32
-static
-void (*interrupt_low_level_handlers[NSIG]) (int, siginfo_t*, os_context_t*);
-#endif
-union interrupt_handler interrupt_handlers[NSIG];
-
\f
/*
* utility routines used by various signal handlers
static void
maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
struct thread *thread = arch_os_get_current_thread();
struct interrupt_data *data = thread->interrupt_data;
-
if(!maybe_defer_handler(interrupt_handle_now,data,signal,info,context))
interrupt_handle_now(signal, info, context);
RESTORE_ERRNO;
static void
low_level_maybe_now_maybe_later(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
struct thread *thread = arch_os_get_current_thread();
struct interrupt_data *data = thread->interrupt_data;
void
interrupt_handle_now_handler(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
#ifndef LISP_FEATURE_WIN32
if ((signal == SIGILL) || (signal == SIGBUS)
#ifndef LISP_FEATURE_LINUX
static void
unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
sigset_t unblock;
sigemptyset(&unblock);
static void
low_level_unblock_me_trampoline(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
sigset_t unblock;
sigemptyset(&unblock);
static void
low_level_handle_now_handler(int signal, siginfo_t *info, void *void_context)
{
- SAVE_ERRNO(context,void_context);
+ SAVE_ERRNO(signal,context,void_context);
(*interrupt_low_level_handlers[signal])(signal, info, context);
RESTORE_ERRNO;
}
#ifdef LISP_FEATURE_GCC_TLS
__thread struct thread *current_thread;
#endif
+pthread_key_t lisp_thread = 0;
#endif
#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64)
lispobj *args = NULL;
#endif
+#ifdef LISP_FEATURE_SB_THREAD
+ pthread_setspecific(lisp_thread, (void *)1);
+#endif
function = th->no_tls_value_marker;
th->no_tls_value_marker = NO_TLS_VALUE_MARKER_WIDETAG;
if(arch_os_thread_init(th)==0) return 1;
FSHOW((stderr,"/creating thread %lu\n", thread_self()));
check_deferrables_blocked_or_lose(0);
check_gc_signals_unblocked_or_lose(0);
+ pthread_setspecific(lisp_thread, (void *)1);
function = th->no_tls_value_marker;
th->no_tls_value_marker = NO_TLS_VALUE_MARKER_WIDETAG;
if(arch_os_thread_init(th)==0) {
void create_initial_thread(lispobj initial_function) {
struct thread *th=create_thread_struct(initial_function);
+#ifdef LISP_FEATURE_SB_THREAD
+ pthread_key_create(&lisp_thread, 0);
+#endif
if(th) {
#ifdef LISP_FEATURE_MACH_EXCEPTION_HANDLER
setup_mach_exception_handling_thread();
--- /dev/null
+;;;; Testing signal handling in non-lisp threads.
+
+;;;; This software is part of the SBCL system. See the README file for
+;;;; more information.
+;;;;
+;;;; While most of SBCL is derived from the CMU CL system, the test
+;;;; files (like this one) were written from scratch after the fork
+;;;; from CMU CL.
+;;;;
+;;;; This software is in the public domain and is provided with
+;;;; absolutely no warranty. See the COPYING and CREDITS files for
+;;;; more information.
+
+#-sb-thread
+(sb-ext:quit :unix-status 104)
+
+(use-package :sb-alien)
+
+(defun run (program &rest arguments)
+ (let* ((proc nil)
+ (output
+ (with-output-to-string (s)
+ (setf proc (run-program program arguments
+ :search (not (eql #\. (char program 0)))
+ :output s)))))
+ (unless (zerop (process-exit-code proc))
+ (error "Bad exit code: ~S~%Output:~% ~S"
+ (process-exit-code proc)
+ output))
+ output))
+
+(run "cc" "-O3"
+ "-I" "../src/runtime/"
+ "kill-non-lisp-thread.c"
+ #+(and (or linux freebsd) (or x86-64 ppc mips)) "-fPIC"
+ #+(and x86-64 darwin) "-arch" #+(and x86-64 darwin) "x86_64"
+ #+darwin "-bundle" #-darwin "-shared"
+ "-o" "kill-non-lisp-thread.so")
+
+(load-shared-object (truename "kill-non-lisp-thread.so"))
+
+(define-alien-routine kill-non-lisp-thread void)
+
+(with-test (:name :kill-non-lisp-thread)
+ (let ((receivedp nil))
+ (push (lambda ()
+ (setq receivedp t))
+ (sb-thread::thread-interruptions sb-thread:*current-thread*))
+ (kill-non-lisp-thread)
+ (sleep 1)
+ (assert receivedp)))
+
+(delete-file "kill-non-lisp-thread.so")