X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fruntime%2Fthread.c;h=e1586801591e56ac236de9d0f41caff73989a5e8;hb=619189958917e80786d5bb2efa4dc38d908d2553;hp=24daaae10e51ac2a31df06e014b7a74abab8ff11;hpb=409fcc671231b594ec010adf683f72a871ec1d6a;p=sbcl.git diff --git a/src/runtime/thread.c b/src/runtime/thread.c index 24daaae..e158680 100644 --- a/src/runtime/thread.c +++ b/src/runtime/thread.c @@ -63,8 +63,6 @@ #define LOCK_CREATE_THREAD #endif -#define ALIEN_STACK_SIZE (1*1024*1024) /* 1Mb size chosen at random */ - #ifdef LISP_FEATURE_SB_THREAD struct thread_post_mortem { #ifdef DELAY_THREAD_POST_MORTEM @@ -135,7 +133,9 @@ initial_thread_trampoline(struct thread *th) link_thread(th); th->os_thread=thread_self(); #ifndef LISP_FEATURE_WIN32 - protect_control_stack_guard_page(1); + protect_control_stack_guard_page(1, NULL); + protect_binding_stack_guard_page(1, NULL); + protect_alien_stack_guard_page(1, NULL); #endif #if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) @@ -258,6 +258,8 @@ new_thread_trampoline(struct thread *th) int result, lock_ret; FSHOW((stderr,"/creating thread %lu\n", thread_self())); + check_deferrables_blocked_or_lose(); + check_gc_signals_unblocked_or_lose(); function = th->no_tls_value_marker; th->no_tls_value_marker = NO_TLS_VALUE_MARKER_WIDETAG; if(arch_os_thread_init(th)==0) { @@ -266,11 +268,13 @@ new_thread_trampoline(struct thread *th) } th->os_thread=thread_self(); - protect_control_stack_guard_page(1); + protect_control_stack_guard_page(1, NULL); + protect_binding_stack_guard_page(1, NULL); + protect_alien_stack_guard_page(1, NULL); /* Since GC can only know about this thread from the all_threads - * list and we're just adding this thread to it there is no danger - * of deadlocking even with SIG_STOP_FOR_GC blocked (which it is - * not). */ + * list and we're just adding this thread to it, there is no + * danger of deadlocking even with SIG_STOP_FOR_GC blocked (which + * it is not). */ lock_ret = pthread_mutex_lock(&all_threads_lock); gc_assert(lock_ret == 0); link_thread(th); @@ -467,6 +471,7 @@ create_thread_struct(lispobj initial_function) { return 0; } th->interrupt_data->pending_handler = 0; + th->interrupt_data->gc_blocked_deferrables = 0; th->no_tls_value_marker=initial_function; th->stepping = NIL; @@ -500,23 +505,22 @@ boolean create_os_thread(struct thread *th,os_thread_t *kid_tid) { /* The new thread inherits the restrictive signal mask set here, * and enables signals again when it is set up properly. */ - sigset_t newset,oldset; + sigset_t oldset; boolean r=1; int retcode = 0, initcode; FSHOW_SIGNAL((stderr,"/create_os_thread: creating new thread\n")); + /* Blocking deferrable signals is enough, no need to block + * SIG_STOP_FOR_GC because the child process is not linked onto + * all_threads until it's ready. */ + thread_sigmask(SIG_BLOCK, &deferrable_sigset, &oldset); + #ifdef LOCK_CREATE_THREAD retcode = pthread_mutex_lock(&create_thread_lock); gc_assert(retcode == 0); FSHOW_SIGNAL((stderr,"/create_os_thread: got lock\n")); #endif - sigemptyset(&newset); - /* Blocking deferrable signals is enough, no need to block - * SIG_STOP_FOR_GC because the child process is not linked onto - * all_threads until it's ready. */ - sigaddset_deferrable(&newset); - thread_sigmask(SIG_BLOCK, &newset, &oldset); if((initcode = pthread_attr_init(th->os_attr)) || /* call_into_lisp_first_time switches the stack for the initial thread. For the @@ -532,68 +536,33 @@ boolean create_os_thread(struct thread *th,os_thread_t *kid_tid) r=0; } - thread_sigmask(SIG_SETMASK,&oldset,0); #ifdef LOCK_CREATE_THREAD retcode = pthread_mutex_unlock(&create_thread_lock); gc_assert(retcode == 0); FSHOW_SIGNAL((stderr,"/create_os_thread: released lock\n")); #endif + thread_sigmask(SIG_SETMASK,&oldset,0); return r; } os_thread_t create_thread(lispobj initial_function) { - struct thread *th; - os_thread_t kid_tid; + struct thread *th, *thread = arch_os_get_current_thread(); + os_thread_t kid_tid = 0; + + /* Must defend against async unwinds. */ + if (SymbolValue(INTERRUPTS_ENABLED, thread) != NIL) + lose("create_thread is not safe when interrupts are enabled.\n"); /* Assuming that a fresh thread struct has no lisp objects in it, * linking it to all_threads can be left to the thread itself * without fear of gc lossage. initial_function violates this * assumption and must stay pinned until the child starts up. */ th = create_thread_struct(initial_function); - if(th==0) return 0; - - if (create_os_thread(th,&kid_tid)) { - return kid_tid; - } else { + if (th && !create_os_thread(th,&kid_tid)) { free_thread_struct(th); - return 0; - } -} - -/* Send the signo to os_thread, retry if the rt signal queue is - * full. */ -int -kill_thread_safely(os_thread_t os_thread, int signo) -{ - int r; - /* The man page does not mention EAGAIN as a valid return value - * for either pthread_kill or kill. But that's theory, this is - * practice. By waiting here we assume that the delivery of this - * signal is not necessary for the delivery of the signals in the - * queue. In other words, we _assume_ there are no deadlocks. */ - while ((r=pthread_kill(os_thread,signo))==EAGAIN) { - /* wait a bit then try again in the hope of the rt signal - * queue not being full */ - FSHOW_SIGNAL((stderr,"/rt signal queue full\n")); - /* FIXME: some kind of backoff (random, exponential) would be - * nice. */ - sleep(1); - } - return r; -} - -int signal_interrupt_thread(os_thread_t os_thread) -{ - int status = kill_thread_safely(os_thread, SIG_INTERRUPT_THREAD); - FSHOW_SIGNAL((stderr,"/signal_interrupt_thread: %lu\n", os_thread)); - if (status == 0) { - return 0; - } else if (status == ESRCH) { - return -1; - } else { - lose("cannot send SIG_INTERRUPT_THREAD to thread=%lu: %d, %s\n", - os_thread, status, strerror(status)); + kid_tid = 0; } + return kid_tid; } /* stopping the world is a two-stage process. From this thread we signal @@ -631,6 +600,8 @@ void gc_stop_the_world() if((p!=th) && ((thread_state(p)==STATE_RUNNING))) { FSHOW_SIGNAL((stderr,"/gc_stop_the_world: suspending thread %lu\n", p->os_thread)); + /* We already hold all_thread_lock, P can become DEAD but + * cannot exit, ergo it's safe to use pthread_kill. */ status=pthread_kill(p->os_thread,SIG_STOP_FOR_GC); if (status==ESRCH) { /* This thread has exited. */ @@ -701,3 +672,60 @@ thread_yield() return 0; #endif } + +/* If the thread id given does not belong to a running thread (it has + * exited or never even existed) pthread_kill _may_ fail with ESRCH, + * but it is also allowed to just segfault, see + * . + * + * Relying on thread ids can easily backfire since ids are recycled + * (NPTL recycles them extremely fast) so a signal can be sent to + * another process if the one it was sent to exited. + * + * We send signals in two places: signal_interrupt_thread sends a + * signal that's harmless if delivered to another thread, but + * SIG_STOP_FOR_GC is fatal. + * + * For these reasons, we must make sure that the thread is still alive + * when the pthread_kill is called and return if the thread is + * exiting. */ +int +kill_safely(os_thread_t os_thread, int signal) +{ + FSHOW_SIGNAL((stderr,"/kill_safely: %lu, %d\n", os_thread, signal)); + { +#ifdef LISP_FEATURE_SB_THREAD + sigset_t oldset; + struct thread *thread; + /* pthread_kill is not async signal safe and we don't want to be + * interrupted while holding the lock. */ + thread_sigmask(SIG_BLOCK, &deferrable_sigset, &oldset); + pthread_mutex_lock(&all_threads_lock); + for (thread = all_threads; thread; thread = thread->next) { + if (thread->os_thread == os_thread) { + int status = pthread_kill(os_thread, signal); + if (status) + lose("kill_safely: pthread_kill failed with %d\n", status); + break; + } + } + pthread_mutex_unlock(&all_threads_lock); + thread_sigmask(SIG_SETMASK,&oldset,0); + if (thread) + return 0; + else + return -1; +#else + int status; + if (os_thread != 0) + lose("kill_safely: who do you want to kill? %d?\n", os_thread); + status = raise(signal); + if (status == 0) { + return 0; + } else { + lose("cannot raise signal %d, %d %s\n", + signal, status, strerror(errno)); + } +#endif + } +}