X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fruntime%2Fthread.c;h=8e3f7fd8703218c3ba8c6d14cd699fc9940dfceb;hb=6793d7dd32d1fa48d2ee395e240e1b7ff857912e;hp=69a32b3e577b3dc82e6fc081c11bb5cccae06aeb;hpb=ce027af4a3f623440d33e7be119fe46caaecee65;p=sbcl.git diff --git a/src/runtime/thread.c b/src/runtime/thread.c index 69a32b3..8e3f7fd 100644 --- a/src/runtime/thread.c +++ b/src/runtime/thread.c @@ -33,7 +33,6 @@ #include "runtime.h" #include "validate.h" /* for BINDING_STACK_SIZE etc */ -#include "alloc.h" #include "thread.h" #include "arch.h" #include "target-arch-os.h" @@ -43,6 +42,7 @@ #include "genesis/cons.h" #include "genesis/fdefn.h" #include "interr.h" /* for lose() */ +#include "alloc.h" #include "gc-internal.h" #ifdef LISP_FEATURE_WIN32 @@ -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 @@ -83,7 +81,7 @@ static struct thread_post_mortem * volatile pending_thread_post_mortem = 0; #endif int dynamic_values_bytes=TLS_SIZE*sizeof(lispobj); /* same for all threads */ -struct thread * volatile all_threads; +struct thread *all_threads; extern struct interrupt_data * global_interrupt_data; #ifdef LISP_FEATURE_SB_THREAD @@ -94,6 +92,7 @@ static pthread_mutex_t create_thread_lock = PTHREAD_MUTEX_INITIALIZER; #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) @@ -129,13 +128,21 @@ initial_thread_trampoline(struct thread *th) #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; link_thread(th); th->os_thread=thread_self(); #ifndef LISP_FEATURE_WIN32 - protect_control_stack_guard_page(1); + protect_control_stack_hard_guard_page(1, NULL); + protect_binding_stack_hard_guard_page(1, NULL); + protect_alien_stack_hard_guard_page(1, NULL); + 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) @@ -145,8 +152,17 @@ initial_thread_trampoline(struct thread *th) #endif } +#ifdef LISP_FEATURE_SB_THREAD +#define THREAD_STATE_LOCK_SIZE \ + (sizeof(pthread_mutex_t))+(sizeof(pthread_cond_t)) +#else +#define THREAD_STATE_LOCK_SIZE 0 +#endif + #define THREAD_STRUCT_SIZE (thread_control_stack_size + BINDING_STACK_SIZE + \ - ALIEN_STACK_SIZE + dynamic_values_bytes + \ + ALIEN_STACK_SIZE + \ + THREAD_STATE_LOCK_SIZE + \ + dynamic_values_bytes + \ 32 * SIGSTKSZ + \ THREAD_ALIGNMENT_BYTES) @@ -249,6 +265,9 @@ new_thread_trampoline(struct thread *th) int result, lock_ret; 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) { @@ -257,11 +276,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); @@ -271,8 +292,8 @@ new_thread_trampoline(struct thread *th) result = funcall0(function); /* Block GC */ - block_blockable_signals(); - th->state=STATE_DEAD; + block_blockable_signals(0, 0); + set_thread_state(th, STATE_DEAD); /* SIG_STOP_FOR_GC is blocked and GC might be waiting for this * thread, but since we are already dead it won't wait long. */ @@ -285,6 +306,9 @@ new_thread_trampoline(struct thread *th) gc_assert(lock_ret == 0); if(th->tls_cookie>=0) arch_os_thread_cleanup(th); + pthread_mutex_destroy(th->state_lock); + pthread_cond_destroy(th->state_cond); + os_invalidate((os_vm_address_t)th->interrupt_data, (sizeof (struct interrupt_data))); @@ -351,7 +375,8 @@ create_thread_struct(lispobj initial_function) { (aligned_spaces+ thread_control_stack_size+ BINDING_STACK_SIZE+ - ALIEN_STACK_SIZE); + ALIEN_STACK_SIZE + + THREAD_STATE_LOCK_SIZE); #ifdef LISP_FEATURE_SB_THREAD for(i = 0; i < (dynamic_values_bytes / sizeof(lispobj)); i++) @@ -371,10 +396,14 @@ create_thread_struct(lispobj initial_function) { make_fixnum(THREAD_SLOT_OFFSET_WORDS(field)) STATIC_TLS_INIT(BINDING_STACK_START,binding_stack_start); +#ifdef BINDING_STACK_POINTER STATIC_TLS_INIT(BINDING_STACK_POINTER,binding_stack_pointer); +#endif STATIC_TLS_INIT(CONTROL_STACK_START,control_stack_start); STATIC_TLS_INIT(CONTROL_STACK_END,control_stack_end); +#ifdef ALIEN_STACK STATIC_TLS_INIT(ALIEN_STACK,alien_stack_pointer); +#endif #if defined(LISP_FEATURE_X86) || defined (LISP_FEATURE_X86_64) STATIC_TLS_INIT(PSEUDO_ATOMIC_BITS,pseudo_atomic_bits); #endif @@ -388,13 +417,20 @@ create_thread_struct(lispobj initial_function) { th->binding_stack_start= (lispobj*)((void*)th->control_stack_start+thread_control_stack_size); th->control_stack_end = th->binding_stack_start; + th->control_stack_guard_page_protected = T; th->alien_stack_start= (lispobj*)((void*)th->binding_stack_start+BINDING_STACK_SIZE); - th->binding_stack_pointer=th->binding_stack_start; + set_binding_stack_pointer(th,th->binding_stack_start); th->this=th; th->os_thread=0; #ifdef LISP_FEATURE_SB_THREAD th->os_attr=malloc(sizeof(pthread_attr_t)); + th->state_lock=(pthread_mutex_t *)((void *)th->alien_stack_start + + ALIEN_STACK_SIZE); + pthread_mutex_init(th->state_lock, NULL); + th->state_cond=(pthread_cond_t *)((void *)th->state_lock + + (sizeof(pthread_mutex_t))); + pthread_cond_init(th->state_cond, NULL); #endif th->state=STATE_RUNNING; #ifdef LISP_FEATURE_STACK_GROWS_DOWNWARD_NOT_UPWARD @@ -403,12 +439,22 @@ create_thread_struct(lispobj initial_function) { #else th->alien_stack_pointer=((void *)th->alien_stack_start); #endif -#if defined(LISP_FEATURE_X86) || defined (LISP_FEATURE_X86_64) +#if defined(LISP_FEATURE_X86) || defined (LISP_FEATURE_X86_64) || defined(LISP_FEATURE_SB_THREAD) th->pseudo_atomic_bits=0; #endif #ifdef LISP_FEATURE_GENCGC gc_set_region_empty(&th->alloc_region); #endif +#ifdef LISP_FEATURE_SB_THREAD + /* This parallels the same logic in globals.c for the + * single-threaded foreign_function_call_active, KLUDGE and + * all. */ +#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) + th->foreign_function_call_active = 0; +#else + th->foreign_function_call_active = 1; +#endif +#endif #ifndef LISP_FEATURE_SB_THREAD /* the tls-points-into-struct-thread trick is only good for threaded @@ -421,12 +467,8 @@ create_thread_struct(lispobj initial_function) { SetSymbolValue(CONTROL_STACK_START,(lispobj)th->control_stack_start,th); SetSymbolValue(CONTROL_STACK_END,(lispobj)th->control_stack_end,th); #if defined(LISP_FEATURE_X86) || defined (LISP_FEATURE_X86_64) - SetSymbolValue(BINDING_STACK_POINTER,(lispobj)th->binding_stack_pointer,th); SetSymbolValue(ALIEN_STACK,(lispobj)th->alien_stack_pointer,th); SetSymbolValue(PSEUDO_ATOMIC_BITS,(lispobj)th->pseudo_atomic_bits,th); -#else - current_binding_stack_pointer=th->binding_stack_pointer; - current_control_stack_pointer=th->control_stack_start; #endif #endif bind_variable(CURRENT_CATCH_BLOCK,make_fixnum(0),th); @@ -437,9 +479,15 @@ create_thread_struct(lispobj initial_function) { bind_variable(ALLOW_WITH_INTERRUPTS,T,th); bind_variable(GC_PENDING,NIL,th); bind_variable(ALLOC_SIGNAL,NIL,th); +#ifdef PINNED_OBJECTS + bind_variable(PINNED_OBJECTS,NIL,th); +#endif #ifdef LISP_FEATURE_SB_THREAD bind_variable(STOP_FOR_GC_PENDING,NIL,th); #endif +#ifndef LISP_FEATURE_C_STACK_IS_CONTROL_STACK + access_control_stack_pointer(th)=th->control_stack_start; +#endif th->interrupt_data = (struct interrupt_data *) os_validate(0,(sizeof (struct interrupt_data))); @@ -448,6 +496,10 @@ create_thread_struct(lispobj initial_function) { return 0; } th->interrupt_data->pending_handler = 0; + th->interrupt_data->gc_blocked_deferrables = 0; +#ifdef LISP_FEATURE_PPC + th->interrupt_data->allocation_trap_context = 0; +#endif th->no_tls_value_marker=initial_function; th->stepping = NIL; @@ -462,6 +514,9 @@ kern_return_t mach_thread_init(mach_port_t thread_exception_port); 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(); @@ -481,100 +536,66 @@ 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. */ + block_deferrable_signals(0, &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 - * others, we use this. */ - (pthread_attr_setstack(th->os_attr,th->control_stack_start,thread_control_stack_size)) || + /* call_into_lisp_first_time switches the stack for the initial + * thread. For the others, we use this. */ + (pthread_attr_setstack(th->os_attr,th->control_stack_start, + thread_control_stack_size)) || (retcode = pthread_create (kid_tid,th->os_attr,(void *(*)(void *))new_thread_trampoline,th))) { FSHOW_SIGNAL((stderr, "init = %d\n", initcode)); - FSHOW_SIGNAL((stderr, printf("pthread_create returned %d, errno %d\n", retcode, errno))); + FSHOW_SIGNAL((stderr, "pthread_create returned %d, errno %d\n", + retcode, errno)); if(retcode < 0) { perror("create_os_thread"); } 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 @@ -608,14 +629,16 @@ void gc_stop_the_world() for(p=all_threads; p; p=p->next) { gc_assert(p->os_thread != 0); FSHOW_SIGNAL((stderr,"/gc_stop_the_world: thread=%lu, state=%x\n", - p->os_thread, p->state)); - if((p!=th) && ((p->state==STATE_RUNNING))) { + p->os_thread, thread_state(p))); + if((p!=th) && ((thread_state(p)==STATE_RUNNING))) { FSHOW_SIGNAL((stderr,"/gc_stop_the_world: suspending thread %lu\n", p->os_thread)); - status=kill_thread_safely(p->os_thread,SIG_STOP_FOR_GC); + /* 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. */ - gc_assert(p->state==STATE_DEAD); + gc_assert(thread_state(p)==STATE_DEAD); } else if (status) { lose("cannot send suspend thread=%lu: %d, %s\n", p->os_thread,status,strerror(status)); @@ -623,26 +646,15 @@ void gc_stop_the_world() } } FSHOW_SIGNAL((stderr,"/gc_stop_the_world:signals sent\n")); - /* wait for the running threads to stop or finish */ - { -#if QSHOW_SIGNALS - struct thread *prev = 0; -#endif - for(p=all_threads;p;) { -#if QSHOW_SIGNALS - if ((p!=th)&&(p!=prev)&&(p->state==STATE_RUNNING)) { - FSHOW_SIGNAL - ((stderr, - "/gc_stop_the_world: waiting for thread=%lu: state=%x\n", - p->os_thread, p->state)); - prev=p; - } -#endif - if((p!=th) && (p->state==STATE_RUNNING)) { - sched_yield(); - } else { - p=p->next; - } + for(p=all_threads;p;p=p->next) { + if (p!=th) { + FSHOW_SIGNAL + ((stderr, + "/gc_stop_the_world: waiting for thread=%lu: state=%x\n", + p->os_thread, thread_state(p))); + wait_for_thread_state_change(p, STATE_RUNNING); + if (p->state == STATE_RUNNING) + lose("/gc_stop_the_world: unexpected state"); } } FSHOW_SIGNAL((stderr,"/gc_stop_the_world:end\n")); @@ -651,7 +663,7 @@ void gc_stop_the_world() void gc_start_the_world() { struct thread *p,*th=arch_os_get_current_thread(); - int status, lock_ret; + int lock_ret; /* if a resumed thread creates a new thread before we're done with * this loop, the new thread will get consed on the front of * all_threads, but it won't have been stopped so won't need @@ -659,30 +671,19 @@ void gc_start_the_world() FSHOW_SIGNAL((stderr,"/gc_start_the_world:begin\n")); for(p=all_threads;p;p=p->next) { gc_assert(p->os_thread!=0); - if((p!=th) && (p->state!=STATE_DEAD)) { - if(p->state!=STATE_SUSPENDED) { - lose("gc_start_the_world: wrong thread state is %d\n", - fixnum_value(p->state)); - } - FSHOW_SIGNAL((stderr, "/gc_start_the_world: resuming %lu\n", - p->os_thread)); - p->state=STATE_RUNNING; - -#if defined(SIG_RESUME_FROM_GC) - status=kill_thread_safely(p->os_thread,SIG_RESUME_FROM_GC); -#else - status=kill_thread_safely(p->os_thread,SIG_STOP_FOR_GC); -#endif - if (status) { - lose("cannot resume thread=%lu: %d, %s\n", - p->os_thread,status,strerror(status)); + if (p!=th) { + lispobj state = thread_state(p); + if (state != STATE_DEAD) { + if(state != STATE_SUSPENDED) { + lose("gc_start_the_world: wrong thread state is %d\n", + fixnum_value(state)); + } + FSHOW_SIGNAL((stderr, "/gc_start_the_world: resuming %lu\n", + p->os_thread)); + set_thread_state(p, STATE_RUNNING); } } } - /* If we waited here until all threads leave STATE_SUSPENDED, then - * SIG_STOP_FOR_GC wouldn't need to be a rt signal. That has some - * performance implications, but does away with the 'rt signal - * queue full' problem. */ lock_ret = pthread_mutex_unlock(&all_threads_lock); gc_assert(lock_ret == 0); @@ -704,3 +705,71 @@ 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. */ + block_deferrable_signals(0, &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); + /* Dubious (as in don't know why it works) workaround for the + * signal sometimes not being generated on darwin. */ +#ifdef LISP_FEATURE_DARWIN + { + sigset_t oldset; + sigprocmask(SIG_BLOCK, &deferrable_sigset, &oldset); + status = raise(signal); + sigprocmask(SIG_SETMASK,&oldset,0); + } +#else + status = raise(signal); +#endif + if (status == 0) { + return 0; + } else { + lose("cannot raise signal %d, %d %s\n", + signal, status, strerror(errno)); + } +#endif + } +}