X-Git-Url: http://repo.macrolet.net/gitweb/?a=blobdiff_plain;f=src%2Fruntime%2Fthread.c;h=759a1c649f6fbd69689506274d569cbec10fcc2b;hb=dc71db379ab4162a45c393a2e828f619dae9fa32;hp=2a174d69575ce2c015bbf0ff52770f1a635bec98;hpb=78fa16bf55be44cc16845be84d98023e83fb14bc;p=sbcl.git diff --git a/src/runtime/thread.c b/src/runtime/thread.c index 2a174d6..759a1c6 100644 --- a/src/runtime/thread.c +++ b/src/runtime/thread.c @@ -27,13 +27,18 @@ int dynamic_values_bytes=4096*sizeof(lispobj); /* same for all threads */ struct thread *all_threads; volatile lispobj all_threads_lock; +volatile lispobj thread_start_lock; extern struct interrupt_data * global_interrupt_data; +extern int linux_no_threads_p; int initial_thread_trampoline(struct thread *th) { lispobj function; +#if defined(LISP_FEATURE_X86) || defined(LISP_FEATURE_X86_64) lispobj *args = NULL; +#endif + function = th->unbound_marker; th->unbound_marker = UNBOUND_MARKER_WIDETAG; if(arch_os_thread_init(th)==0) return 1; @@ -88,16 +93,15 @@ struct thread * create_thread_struct(lispobj initial_function) { BINDING_STACK_SIZE+ ALIEN_STACK_SIZE+ dynamic_values_bytes+ - 32*SIGSTKSZ - ); - if(!spaces) goto cleanup; + 32*SIGSTKSZ); + if(!spaces) + return NULL; per_thread=(union per_thread_data *) (spaces+ THREAD_CONTROL_STACK_SIZE+ BINDING_STACK_SIZE+ ALIEN_STACK_SIZE); - th=&per_thread->thread; if(all_threads) { memcpy(per_thread,arch_os_get_current_thread(), dynamic_values_bytes); @@ -129,6 +133,7 @@ struct thread * create_thread_struct(lispobj initial_function) { #endif } + th=&per_thread->thread; th->control_stack_start = spaces; th->binding_stack_start= (lispobj*)((void*)th->control_stack_start+THREAD_CONTROL_STACK_SIZE); @@ -138,7 +143,7 @@ struct thread * create_thread_struct(lispobj initial_function) { th->binding_stack_pointer=th->binding_stack_start; th->this=th; th->pid=0; - th->state=STATE_STOPPED; + th->state=STATE_STARTING; #ifdef LISP_FEATURE_STACK_GROWS_DOWNWARD_NOT_UPWARD th->alien_stack_pointer=((void *)th->alien_stack_start + ALIEN_STACK_SIZE-N_WORD_BYTES); @@ -179,7 +184,8 @@ struct thread * create_thread_struct(lispobj initial_function) { bind_variable(INTERRUPT_PENDING, NIL,th); bind_variable(INTERRUPTS_ENABLED,T,th); - th->interrupt_data=os_validate(0,(sizeof (struct interrupt_data))); + th->interrupt_data = + os_validate(0,(sizeof (struct interrupt_data))); if(all_threads) memcpy(th->interrupt_data, arch_os_get_current_thread()->interrupt_data, @@ -190,12 +196,6 @@ struct thread * create_thread_struct(lispobj initial_function) { th->unbound_marker=initial_function; return th; - cleanup: - /* if(th && th->tls_cookie>=0) os_free_tls_pointer(th); */ - if(spaces) os_invalidate(spaces, - THREAD_CONTROL_STACK_SIZE+BINDING_STACK_SIZE+ - ALIEN_STACK_SIZE+dynamic_values_bytes); - return 0; } void link_thread(struct thread *th,pid_t kid_pid) @@ -212,10 +212,10 @@ void link_thread(struct thread *th,pid_t kid_pid) * to ensure that we don't have >1 thread with pid=0 on the list at once */ protect_control_stack_guard_page(th->pid,1); + th->pid=kid_pid; /* child will not start until this is set */ release_spinlock(&all_threads_lock); sigprocmask(SIG_SETMASK,&oldset,0); - th->pid=kid_pid; /* child will not start until this is set */ } void create_initial_thread(lispobj initial_function) { @@ -229,19 +229,39 @@ void create_initial_thread(lispobj initial_function) { #ifdef LISP_FEATURE_SB_THREAD pid_t create_thread(lispobj initial_function) { - struct thread *th=create_thread_struct(initial_function); + struct thread *th; pid_t kid_pid=0; + if(linux_no_threads_p) return 0; + th=create_thread_struct(initial_function); if(th==0) return 0; +#ifdef QSHOW_SIGNALS + SHOW("create_thread:waiting on lock"); +#endif + get_spinlock(&thread_start_lock,arch_os_get_current_thread()->pid); +#ifdef QSHOW_SIGNALS + SHOW("create_thread:got lock"); +#endif kid_pid=clone(new_thread_trampoline, (((void*)th->control_stack_start)+ - THREAD_CONTROL_STACK_SIZE-4), + THREAD_CONTROL_STACK_SIZE-16), CLONE_FILES|SIG_THREAD_EXIT|CLONE_VM,th); if(kid_pid>0) { link_thread(th,kid_pid); + /* wait here until our thread is started: see new_thread_trampoline */ + while(th->state==STATE_STARTING) sched_yield(); + /* it's started and initialized, it's safe to gc */ + release_spinlock(&thread_start_lock); +#ifdef QSHOW_SIGNALS + SHOW("create_thread:released lock"); +#endif return th->pid; } else { + release_spinlock(&thread_start_lock); +#ifdef QSHOW_SIGNALS + SHOW("create_thread:released lock(failure)"); +#endif os_invalidate((os_vm_address_t) th->control_stack_start, ((sizeof (lispobj)) * (th->control_stack_end-th->control_stack_start)) + @@ -252,32 +272,6 @@ pid_t create_thread(lispobj initial_function) { } #endif -/* unused */ -void destroy_thread (struct thread *th) -{ - /* precondition: the unix task has already been killed and exited. - * This is called by the parent or some other thread */ -#ifdef LISP_FEATURE_GENCGC - gc_alloc_update_page_tables(0, &th->alloc_region); -#endif - get_spinlock(&all_threads_lock,th->pid); - th->unbound_marker=0; /* for debugging */ - if(th==all_threads) - all_threads=th->next; - else { - struct thread *th1=all_threads; - while(th1 && th1->next!=th) th1=th1->next; - if(th1) th1->next=th->next; /* unlink */ - } - release_spinlock(&all_threads_lock); - if(th && th->tls_cookie>=0) arch_os_thread_cleanup(th); - os_invalidate((os_vm_address_t) th->control_stack_start, - ((sizeof (lispobj)) - * (th->control_stack_end-th->control_stack_start)) + - BINDING_STACK_SIZE+ALIEN_STACK_SIZE+dynamic_values_bytes+ - 32*SIGSTKSZ); -} - struct thread *find_thread_by_pid(pid_t pid) { struct thread *th; @@ -331,32 +325,6 @@ void reap_dead_threads() } } -/* These are not needed unless #+SB-THREAD, and since sigwaitinfo() - * doesn't seem to be easily available everywhere (OpenBSD...) it's - * more trouble than it's worth to compile it when not needed. */ -void block_sigcont(void) -{ - /* don't allow ourselves to receive SIGCONT while we're in the - * "ambiguous" state of being on the queue but not actually stopped. - */ - sigset_t newset; - sigemptyset(&newset); - sigaddset(&newset,SIG_DEQUEUE); - sigprocmask(SIG_BLOCK, &newset, 0); -} - -void unblock_sigcont_and_sleep(void) -{ - sigset_t set; - sigemptyset(&set); - sigaddset(&set,SIG_DEQUEUE); - do { - errno=0; - sigwaitinfo(&set,0); - }while(errno==EINTR); - sigprocmask(SIG_UNBLOCK,&set,0); -} - int interrupt_thread(pid_t pid, lispobj function) { union sigval sigval; @@ -377,50 +345,80 @@ int signal_thread_to_dequeue (pid_t pid) /* stopping the world is a two-stage process. From this thread we signal * all the others with SIG_STOP_FOR_GC. The handler for this signal does * the usual pseudo-atomic checks (we don't want to stop a thread while - * it's in the middle of allocation) then kills _itself_ with SIGSTOP. + * it's in the middle of allocation) then waits for another SIG_STOP_FOR_GC. */ void gc_stop_the_world() { - /* stop all other threads by sending them SIG_STOP_FOR_GC */ +#ifdef QSHOW_SIGNALS + SHOW("gc_stop_the_world:begin"); +#endif struct thread *p,*th=arch_os_get_current_thread(); - pid_t old_pid; - int finished; - do { - finished=1; - for(p=all_threads,old_pid=p->pid; p; p=p->next) { - if(p==th) continue; - if(p->state==STATE_RUNNING) { - p->state=STATE_STOPPING; - if(kill(p->pid,SIG_STOP_FOR_GC)==-1) { - /* we can't kill the process; assume because it - * died already (and its parent is dead so never - * saw the SIGCHLD) */ - p->state=STATE_DEAD; - } - } - if((p->state!=STATE_STOPPED) && - (p->state!=STATE_DEAD)) { - finished=0; - } - } - if(old_pid!=all_threads->pid) { - finished=0; - } - } while(!finished); + /* keep threads from starting while the world is stopped. */ + get_spinlock(&thread_start_lock,th->pid); +#ifdef QSHOW_SIGNALS + SHOW("gc_stop_the_world:locked"); +#endif + /* stop all other threads by sending them SIG_STOP_FOR_GC */ + for(p=all_threads; p; p=p->next) { + if((p!=th) && (p->pid!=0) && (p->state==STATE_RUNNING)) { + p->state=STATE_STOPPING; + if(kill(p->pid,SIG_STOP_FOR_GC)==-1) { + /* we can't kill the process; assume because it + * died already (and its parent is dead so never + * saw the SIGCHLD) */ + p->state=STATE_DEAD; + } + } + } +#ifdef QSHOW_SIGNALS + SHOW("gc_stop_the_world:signals sent"); +#endif + /* wait for the running threads to stop */ + for(p=all_threads;p;) { + if((p==th) || (p->pid==0) || (p->state==STATE_STARTING) || + (p->state==STATE_DEAD) || (p->state==STATE_STOPPED)) { + p=p->next; + } + } +#ifdef QSHOW_SIGNALS + SHOW("gc_stop_the_world:end"); +#endif } void gc_start_the_world() { struct thread *p,*th=arch_os_get_current_thread(); /* 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_lock, but it won't have been stopped so won't need - * restarting */ + * 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 + * restarting; there can be threads just starting from before + * gc_stop_the_world, though */ +#ifdef QSHOW_SIGNALS + SHOW("gc_start_the_world:begin"); +#endif for(p=all_threads;p;p=p->next) { - if((p==th) || (p->state==STATE_DEAD)) continue; - p->state=STATE_RUNNING; - kill(p->pid,SIG_STOP_FOR_GC); + if((p!=th) && (p->pid!=0) && (p->state!=STATE_STARTING) && + (p->state!=STATE_DEAD)) { + if(p->state!=STATE_STOPPED) { + lose("gc_start_the_world: wrong thread state is %ld\n", + fixnum_value(p->state)); + } + kill(p->pid,SIG_STOP_FOR_GC); + } } + /* we must wait for all threads to leave stopped state else we + * risk signal accumulation and lose any meaning of + * thread->state */ + for(p=all_threads;p;) { + gc_assert(p->state!=STATE_STOPPING); + if((p==th) || (p->pid==0) || (p->state!=STATE_STOPPED)) { + p=p->next; + } + } + release_spinlock(&thread_start_lock); +#ifdef QSHOW_SIGNALS + SHOW("gc_start_the_world:end"); +#endif } #endif