+ /* interrupt_thread_handler locks this spinlock with blockables
+ * blocked (it does so for the sake of
+ * arrange_return_to_lisp_function), so we must also block them or
+ * else SIG_STOP_FOR_GC and all_threads_lock will find a way to
+ * deadlock. */
+ sigaddset_blockable(&newset);
+ thread_sigmask(SIG_BLOCK, &newset, &oldset);
+ if (th == arch_os_get_current_thread())
+ lose("cannot interrupt current thread");
+ get_spinlock(&th->interrupt_fun_lock,
+ (long)arch_os_get_current_thread());
+ ((struct cons *)native_pointer(c))->cdr=th->interrupt_fun;
+ th->interrupt_fun=c;
+ release_spinlock(&th->interrupt_fun_lock);
+ thread_sigmask(SIG_SETMASK,&oldset,0);
+ /* Called from lisp with the thread object as a parameter. Thus,
+ * the object cannot be garbage collected and consequently reaped
+ * and joined. Because it's not joined, kill should work (even if
+ * the thread has died/exited). */
+ {
+ int status=kill_thread_safely(th->os_thread,SIG_INTERRUPT_THREAD);
+ if (status==0) {
+ return 0;
+ } else if (status==ESRCH) {
+ /* This thread has exited. */
+ th->interrupt_fun=NIL;
+ errno=ESRCH;
+ return -1;
+ } else {
+ lose("cannot send SIG_INTERRUPT_THREAD to thread=%lu: %d, %s",
+ th->os_thread,status,strerror(status));
+ }
+ }
+}
+
+/* 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 waits for another SIG_STOP_FOR_GC.
+ */
+
+/* To avoid deadlocks when gc stops the world all clients of each
+ * mutex must enable or disable SIG_STOP_FOR_GC for the duration of
+ * holding the lock, but they must agree on which. */
+void gc_stop_the_world()
+{
+ struct thread *p,*th=arch_os_get_current_thread();
+ int status;
+ FSHOW_SIGNAL((stderr,"/gc_stop_the_world:waiting on lock, thread=%lu\n",
+ th->os_thread));
+ /* keep threads from starting while the world is stopped. */
+ pthread_mutex_lock(&all_threads_lock); \
+ FSHOW_SIGNAL((stderr,"/gc_stop_the_world:got lock, thread=%lu\n",
+ th->os_thread));
+ /* stop all other threads by sending them SIG_STOP_FOR_GC */
+ for(p=all_threads; p; p=p->next) {
+ while(p->state==STATE_STARTING) sched_yield();
+ if((p!=th) && (p->state==STATE_RUNNING)) {
+ FSHOW_SIGNAL((stderr,"/gc_stop_the_world: suspending %lu\n",
+ p->os_thread));
+ status=kill_thread_safely(p->os_thread,SIG_STOP_FOR_GC);
+ if (status==ESRCH) {
+ /* This thread has exited. */
+ gc_assert(p->state==STATE_DEAD);
+ } else if (status) {
+ lose("cannot send suspend thread=%lu: %d, %s",
+ p->os_thread,status,strerror(status));
+ }
+ }
+ }
+ FSHOW_SIGNAL((stderr,"/gc_stop_the_world:signals sent\n"));
+ /* wait for the running threads to stop or finish */
+ for(p=all_threads;p;) {
+ gc_assert(p->os_thread!=0);
+ gc_assert(p->state!=STATE_STARTING);
+ if((p==th) || (p->state==STATE_SUSPENDED) ||
+ (p->state==STATE_DEAD)) {
+ p=p->next;
+ } else {
+ sched_yield();
+ }
+ }
+ FSHOW_SIGNAL((stderr,"/gc_stop_the_world:end\n"));