This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Mask thread signal handling fix on Win32
[perl5.git] / ext / threads / threads.xs
index 65588b4..7abd037 100755 (executable)
@@ -12,7 +12,7 @@
 #ifdef HAS_PPPORT_H
 #  define NEED_PL_signals
 #  define NEED_newRV_noinc
-#  define NEED_sv_2pv_nolen
+#  define NEED_sv_2pv_flags
 #  include "ppport.h"
 #  include "threads.h"
 #endif
@@ -45,12 +45,15 @@ typedef perl_os_thread pthread_t;
 #endif
 
 /* Values for 'state' member */
-#define PERL_ITHR_DETACHED              1
-#define PERL_ITHR_JOINED                2
-#define PERL_ITHR_FINISHED              4
-#define PERL_ITHR_THREAD_EXIT_ONLY      8
-#define PERL_ITHR_NONVIABLE             16
-#define PERL_ITHR_DESTROYED             32
+#define PERL_ITHR_DETACHED           1 /* Thread has been detached */
+#define PERL_ITHR_JOINED             2 /* Thread has been joined */
+#define PERL_ITHR_FINISHED           4 /* Thread has finished execution */
+#define PERL_ITHR_THREAD_EXIT_ONLY   8 /* exit() only exits current thread */
+#define PERL_ITHR_NONVIABLE         16 /* Thread creation failed */
+#define PERL_ITHR_DIED              32 /* Thread finished by dying */
+
+#define PERL_ITHR_UNCALLABLE  (PERL_ITHR_DETACHED|PERL_ITHR_JOINED)
+
 
 typedef struct _ithread {
     struct _ithread *next;      /* Next thread in the list */
@@ -58,7 +61,7 @@ typedef struct _ithread {
     PerlInterpreter *interp;    /* The threads interpreter */
     UV tid;                     /* Threads module's thread id */
     perl_mutex mutex;           /* Mutex for updating things in this struct */
-    int count;                  /* How many SVs have a reference to us */
+    int count;                  /* Reference count. See S_ithread_create. */
     int state;                  /* Detached, joined, finished, etc. */
     int gimme;                  /* Context of create */
     SV *init_function;          /* Code to run */
@@ -70,6 +73,11 @@ typedef struct _ithread {
     pthread_t thr;              /* OS's handle for the thread */
 #endif
     IV stack_size;
+    SV *err;                    /* Error from abnormally terminated thread */
+    char *err_class;            /* Error object's classname if applicable */
+#ifndef WIN32
+    sigset_t initial_sigmask;   /* Thread wakes up with signals blocked */
+#endif
 } ithread;
 
 
@@ -97,6 +105,7 @@ typedef struct {
     IV joinable_threads;
     IV running_threads;
     IV detached_threads;
+    IV total_threads;
     IV default_stack_size;
     IV page_size;
 } my_pool_t;
@@ -108,6 +117,45 @@ typedef struct {
 
 #define MY_POOL (*my_poolp)
 
+#ifndef WIN32
+/* Block most signals for calling thread, setting the old signal mask to
+ * oldmask, if it is not NULL */
+STATIC int
+S_block_most_signals(sigset_t *oldmask)
+{
+    sigset_t newmask;
+
+    sigfillset(&newmask);
+    /* Don't block certain "important" signals (stolen from mg.c) */
+#ifdef SIGILL
+    sigdelset(&newmask, SIGILL);
+#endif
+#ifdef SIGBUS
+    sigdelset(&newmask, SIGBUS);
+#endif
+#ifdef SIGSEGV
+    sigdelset(&newmask, SIGSEGV);
+#endif
+
+#if defined(VMS)
+    /* no per-thread blocking available */
+    return sigprocmask(SIG_BLOCK, &newmask, oldmask);
+#else
+    return pthread_sigmask(SIG_BLOCK, &newmask, oldmask);
+#endif /* WIN32 */
+}
+
+/* Set the signal mask for this thread to newmask */
+STATIC int
+S_set_sigmask(sigset_t *newmask)
+{
+#if defined(VMS)
+    return sigprocmask(SIG_SETMASK, newmask, NULL);
+#else
+    return pthread_sigmask(SIG_SETMASK, newmask, NULL);
+#endif /* WIN32 */
+}
+#endif
 
 /* Used by Perl interpreter for thread context switching */
 STATIC void
@@ -127,18 +175,30 @@ S_ithread_get(pTHX)
 
 /* Free any data (such as the Perl interpreter) attached to an ithread
  * structure.  This is a bit like undef on SVs, where the SV isn't freed,
- * but the PVX is.  Must be called with thread->mutex already held.
+ * but the PVX is.  Must be called with thread->mutex already locked.  Also,
+ * must be called with MY_POOL.create_destruct_mutex unlocked as destruction
+ * of the interpreter can lead to recursive destruction calls that could
+ * lead to a deadlock on that mutex.
  */
 STATIC void
 S_ithread_clear(pTHX_ ithread *thread)
 {
     PerlInterpreter *interp;
+    sigset_t origmask;
 
     assert(((thread->state & PERL_ITHR_FINISHED) &&
-            (thread->state & (PERL_ITHR_DETACHED|PERL_ITHR_JOINED)))
+            (thread->state & PERL_ITHR_UNCALLABLE))
                 ||
            (thread->state & PERL_ITHR_NONVIABLE));
 
+#ifndef WIN32
+    /* We temporarily set the interpreter context to the interpreter being
+     * destroyed.  It's in no condition to handle signals while it's being
+     * taken apart.
+     */
+    S_block_most_signals(&origmask);
+#endif
+
     interp = thread->interp;
     if (interp) {
         dTHXa(interp);
@@ -149,44 +209,45 @@ S_ithread_clear(pTHX_ ithread *thread)
         SvREFCNT_dec(thread->params);
         thread->params = Nullsv;
 
+        if (thread->err) {
+            SvREFCNT_dec(thread->err);
+            thread->err = Nullsv;
+        }
+
         perl_destruct(interp);
         perl_free(interp);
         thread->interp = NULL;
     }
 
     PERL_SET_CONTEXT(aTHX);
+#ifndef WIN32
+    S_set_sigmask(&origmask);
+#endif
 }
 
 
-/* Free an ithread structure and any attached data if its count == 0 */
+/* Decrement the refcount of an ithread, and if it reaches zero, free it.
+ * Must be called with the mutex held.
+ * On return, mutex is released (or destroyed).
+ */
 STATIC void
-S_ithread_destruct(pTHX_ ithread *thread)
+S_ithread_free(pTHX_ ithread *thread)
 {
-    int destroy = 0;
 #ifdef WIN32
     HANDLE handle;
 #endif
     dMY_POOL;
 
-    /* Determine if thread can be destroyed now */
-    MUTEX_LOCK(&thread->mutex);
-    if (thread->count != 0) {
-        destroy = 0;
-    } else if (thread->state & PERL_ITHR_DESTROYED) {
-        destroy = 0;
-    } else if (thread->state & PERL_ITHR_NONVIABLE) {
-        thread->state |= PERL_ITHR_DESTROYED;
-        destroy = 1;
-    } else if (! (thread->state & PERL_ITHR_FINISHED)) {
-        destroy = 0;
-    } else if (! (thread->state & (PERL_ITHR_DETACHED|PERL_ITHR_JOINED))) {
-        destroy = 0;
-    } else {
-        thread->state |= PERL_ITHR_DESTROYED;
-        destroy = 1;
+    if (! (thread->state & PERL_ITHR_NONVIABLE)) {
+        assert(thread->count > 0);
+        if (--thread->count > 0) {
+            MUTEX_UNLOCK(&thread->mutex);
+            return;
+        }
+        assert((thread->state & PERL_ITHR_FINISHED) &&
+               (thread->state & PERL_ITHR_UNCALLABLE));
     }
     MUTEX_UNLOCK(&thread->mutex);
-    if (! destroy) return;
 
     /* Main thread (0) is immortal and should never get here */
     assert(thread->tid != 0);
@@ -217,11 +278,26 @@ S_ithread_destruct(pTHX_ ithread *thread)
     }
 #endif
 
-    /* Call PerlMemShared_free() in the context of the "first" interpreter
-     * per http://www.nntp.perl.org/group/perl.perl5.porters/110772
-     */
-    aTHX = MY_POOL.main_thread.interp;
     PerlMemShared_free(thread);
+
+    /* total_threads >= 1 is used to veto cleanup by the main thread,
+     * should it happen to exit while other threads still exist.
+     * Decrement this as the very last thing in the thread's existence.
+     * Otherwise, MY_POOL and global state such as PL_op_mutex may get
+     * freed while we're still using it.
+     */
+    MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
+    MY_POOL.total_threads--;
+    MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
+}
+
+
+static void
+S_ithread_count_inc(pTHX_ ithread *thread)
+{
+    MUTEX_LOCK(&thread->mutex);
+    thread->count++;
+    MUTEX_UNLOCK(&thread->mutex);
 }
 
 
@@ -229,14 +305,15 @@ S_ithread_destruct(pTHX_ ithread *thread)
 STATIC int
 S_exit_warning(pTHX)
 {
-    int veto_cleanup;
+    int veto_cleanup, warn;
     dMY_POOL;
 
     MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
-    veto_cleanup = (MY_POOL.running_threads || MY_POOL.joinable_threads);
+    veto_cleanup = (MY_POOL.total_threads > 0);
+    warn         = (MY_POOL.running_threads || MY_POOL.joinable_threads);
     MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
 
-    if (veto_cleanup) {
+    if (warn) {
         if (ckWARN_d(WARN_THREADS)) {
             Perl_warn(aTHX_ "Perl exited with active threads:\n\t%"
                             IVdf " running and unjoined\n\t%"
@@ -251,7 +328,10 @@ S_exit_warning(pTHX)
     return (veto_cleanup);
 }
 
-/* Called on exit from main thread */
+
+/* Called from perl_destruct() in each thread.  If it's the main thread,
+ * stop it from freeing everything if there are other threads still running.
+ */
 int
 Perl_ithread_hook(pTHX)
 {
@@ -275,24 +355,15 @@ int
 ithread_mg_free(pTHX_ SV *sv, MAGIC *mg)
 {
     ithread *thread = (ithread *)mg->mg_ptr;
-
     MUTEX_LOCK(&thread->mutex);
-    thread->count--;
-    MUTEX_UNLOCK(&thread->mutex);
-
-    /* Try to clean up thread */
-    S_ithread_destruct(aTHX_ thread);
-
+    S_ithread_free(aTHX_ thread);   /* Releases MUTEX */
     return (0);
 }
 
 int
 ithread_mg_dup(pTHX_ MAGIC *mg, CLONE_PARAMS *param)
 {
-    ithread *thread = (ithread *)mg->mg_ptr;
-    MUTEX_LOCK(&thread->mutex);
-    thread->count++;
-    MUTEX_UNLOCK(&thread->mutex);
+    S_ithread_count_inc(aTHX_ (ithread *)mg->mg_ptr);
     return (0);
 }
 
@@ -339,7 +410,7 @@ S_good_stack_size(pTHX_ IV stack_size)
 #  endif
         if ((long)MY_POOL.page_size < 0) {
             if (errno) {
-                SV * const error = get_sv("@", FALSE);
+                SV * const error = get_sv("@", 0);
                 (void)SvUPGRADE(error, SVt_PV);
                 Perl_croak(aTHX_ "PANIC: sysconf: %s", SvPV_nolen(error));
             } else {
@@ -381,8 +452,9 @@ S_ithread_run(void * arg)
     ithread *thread = (ithread *)arg;
     int jmp_rc = 0;
     I32 oldscope;
-    int exit_app = 0;
+    int exit_app = 0;   /* Thread terminated using 'exit' */
     int exit_code = 0;
+    int died = 0;       /* Thread terminated abnormally */
 
     dJMPENV;
 
@@ -397,6 +469,13 @@ S_ithread_run(void * arg)
     PERL_SET_CONTEXT(thread->interp);
     S_ithread_set(aTHX_ thread);
 
+#ifndef WIN32
+    /* Thread starts with most signals blocked - restore the signal mask from
+     * the ithread struct.
+     */
+    S_set_sigmask(&thread->initial_sigmask);
+#endif
+
     PL_perl_destruct_level = 2;
 
     {
@@ -430,11 +509,19 @@ S_ithread_run(void * arg)
         }
         JMPENV_POP;
 
+#ifndef WIN32
+        /* The interpreter is finished, so this thread can stop receiving
+         * signals.  This way, our signal handler doesn't get called in the
+         * middle of our parent thread calling perl_destruct()...
+         */
+        S_block_most_signals(NULL);
+#endif
+
         /* Remove args from stack and put back in params array */
         SPAGAIN;
         for (ii=len-1; ii >= 0; ii--) {
             SV *sv = POPs;
-            if (jmp_rc == 0) {
+            if (jmp_rc == 0 && (thread->gimme & G_WANT) != G_VOID) {
                 av_store(params, ii, SvREFCNT_inc(sv));
             }
         }
@@ -442,22 +529,34 @@ S_ithread_run(void * arg)
         FREETMPS;
         LEAVE;
 
-        /* Check for failure */
-        if (SvTRUE(ERRSV) && ckWARN_d(WARN_THREADS)) {
-            oldscope = PL_scopestack_ix;
-            JMPENV_PUSH(jmp_rc);
-            if (jmp_rc == 0) {
-                /* Warn that thread died */
-                Perl_warn(aTHX_ "Thread %" UVuf " terminated abnormally: %" SVf, thread->tid, ERRSV);
-            } else if (jmp_rc == 2) {
-                /* Warn handler exited */
-                exit_app = 1;
-                exit_code = STATUS_CURRENT;
-                while (PL_scopestack_ix > oldscope) {
-                    LEAVE;
+        /* Check for abnormal termination */
+        if (SvTRUE(ERRSV)) {
+            died = PERL_ITHR_DIED;
+            thread->err = newSVsv(ERRSV);
+            /* If ERRSV is an object, remember the classname and then
+             * rebless into 'main' so it will survive 'cloning'
+             */
+            if (sv_isobject(thread->err)) {
+                thread->err_class = HvNAME(SvSTASH(SvRV(thread->err)));
+                sv_bless(thread->err, gv_stashpv("main", 0));
+            }
+
+            if (ckWARN_d(WARN_THREADS)) {
+                oldscope = PL_scopestack_ix;
+                JMPENV_PUSH(jmp_rc);
+                if (jmp_rc == 0) {
+                    /* Warn that thread died */
+                    Perl_warn(aTHX_ "Thread %" UVuf " terminated abnormally: %" SVf, thread->tid, ERRSV);
+                } else if (jmp_rc == 2) {
+                    /* Warn handler exited */
+                    exit_app = 1;
+                    exit_code = STATUS_CURRENT;
+                    while (PL_scopestack_ix > oldscope) {
+                        LEAVE;
+                    }
                 }
+                JMPENV_POP;
             }
-            JMPENV_POP;
         }
 
         /* Release function ref */
@@ -470,7 +569,7 @@ S_ithread_run(void * arg)
     MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
     MUTEX_LOCK(&thread->mutex);
     /* Mark as finished */
-    thread->state |= PERL_ITHR_FINISHED;
+    thread->state |= (PERL_ITHR_FINISHED | died);
     /* Clear exit flag if required */
     if (thread->state & PERL_ITHR_THREAD_EXIT_ONLY) {
         exit_app = 0;
@@ -505,8 +604,14 @@ S_ithread_run(void * arg)
         my_exit(exit_code);
     }
 
-    /* Try to clean up thread */
-    S_ithread_destruct(aTHX_ thread);
+    /* At this point, the interpreter may have been freed, so call
+     * free in the the context of of the 'main' interpreter which
+     * can't have been freed due to the veto_cleanup mechanism.
+     */
+    aTHX = MY_POOL.main_thread.interp;
+
+    MUTEX_LOCK(&thread->mutex);
+    S_ithread_free(aTHX_ thread);   /* Releases MUTEX */
 
 #ifdef WIN32
     return ((DWORD)0);
@@ -524,12 +629,8 @@ S_ithread_to_SV(pTHX_ SV *obj, ithread *thread, char *classname, bool inc)
     SV *sv;
     MAGIC *mg;
 
-    /* If incrementing thread ref count, then call within mutex lock */
-    if (inc) {
-        MUTEX_LOCK(&thread->mutex);
-        thread->count++;
-        MUTEX_UNLOCK(&thread->mutex);
-    }
+    if (inc)
+        S_ithread_count_inc(aTHX_ thread);
 
     if (! obj) {
         obj = newSV(0);
@@ -597,11 +698,17 @@ S_ithread_create(
     thread->prev = MY_POOL.main_thread.prev;
     MY_POOL.main_thread.prev = thread;
     thread->prev->next = thread;
-
-    /* Set count to 1 immediately in case thread exits before
-     * we return to caller!
+    MY_POOL.total_threads++;
+
+    /* 1 ref to be held by the local var 'thread' in S_ithread_run().
+     * 1 ref to be held by the threads object that we assume we will
+     *      be embedded in upon our return.
+     * 1 ref to be the responsibility of join/detach, so we don't get
+     *      freed until join/detach, even if no thread objects remain.
+     *      This allows the following to work:
+     *          { threads->create(sub{...}); } threads->object(1)->join;
      */
-    thread->count = 1;
+    thread->count = 3;
 
     /* Block new thread until ->create() call finishes */
     MUTEX_INIT(&thread->mutex);
@@ -622,6 +729,27 @@ S_ithread_create(
     PL_srand_called = FALSE;   /* Set it to false so we can detect if it gets
                                   set during the clone */
 
+#ifndef WIN32
+    /* perl_clone() will leave us the new interpreter's context.  This poses
+     * two problems for our signal handler.  First, it sets the new context
+     * before the new interpreter struct is fully initialized, so our signal
+     * handler might find bogus data in the interpreter struct it gets.
+     * Second, even if the interpreter is initialized before a signal comes in,
+     * we would like to avoid that interpreter receiving notifications for
+     * signals (especially when they ought to be for the one running in this
+     * thread), until it is running in its own thread.  Another problem is that
+     * the new thread will not have set the context until some time after it
+     * has started, so it won't be safe for our signal handler to run until
+     * that time.
+     *
+     * So we block most signals here, so the new thread will inherit the signal
+     * mask, and unblock them right after the thread creation.  The original
+     * mask is saved in the thread struct so that the new thread can restore
+     * the original mask.
+     */
+    S_block_most_signals(&thread->initial_sigmask);
+#endif
+
 #ifdef WIN32
     thread->interp = perl_clone(aTHX, CLONEf_KEEP_PTR_TABLE | CLONEf_CLONE_HOST);
 #else
@@ -650,10 +778,8 @@ S_ithread_create(
             thread->init_function = newSV(0);
             sv_copypv(thread->init_function, init_function);
         } else {
-            thread->init_function = sv_dup(init_function, &clone_param);
-            if (SvREFCNT(thread->init_function) == 0) {
-                SvREFCNT_inc_void(thread->init_function);
-            }
+            thread->init_function =
+               SvREFCNT_inc(sv_dup(init_function, &clone_param));
         }
 
         thread->params = sv_dup(params, &clone_param);
@@ -738,6 +864,13 @@ S_ithread_create(
 #  endif
         }
 
+#ifndef WIN32
+    /* Now it's safe to accept signals, since we're in our own interpreter's
+     * context and we have created the thread.
+     */
+    S_set_sigmask(&thread->initial_sigmask);
+#endif
+
 #  ifdef _POSIX_THREAD_ATTR_STACKSIZE
         /* Try to get thread's actual stack size */
         {
@@ -765,7 +898,7 @@ S_ithread_create(
         MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
         sv_2mortal(params);
         thread->state |= PERL_ITHR_NONVIABLE;
-        S_ithread_destruct(aTHX_ thread);
+        S_ithread_free(aTHX_ thread);   /* Releases MUTEX */
 #ifndef WIN32
         if (ckWARN_d(WARN_THREADS)) {
             if (rc_stack_size) {
@@ -810,7 +943,7 @@ ithread_create(...)
     CODE:
         if ((items >= 2) && SvROK(ST(1)) && SvTYPE(SvRV(ST(1)))==SVt_PVHV) {
             if (--items < 2) {
-                Perl_croak(aTHX_ "Usage: threads->create(\\%specs, function, ...)");
+                Perl_croak(aTHX_ "Usage: threads->create(\\%%specs, function, ...)");
             }
             specs = (HV*)SvRV(ST(1));
             idx = 1;
@@ -826,13 +959,15 @@ ithread_create(...)
             /* $thr->create() */
             classname = HvNAME(SvSTASH(SvRV(ST(0))));
             thread = INT2PTR(ithread *, SvIV(SvRV(ST(0))));
+            MUTEX_LOCK(&thread->mutex);
             stack_size = thread->stack_size;
             exit_opt = thread->state & PERL_ITHR_THREAD_EXIT_ONLY;
+            MUTEX_UNLOCK(&thread->mutex);
         } else {
             /* threads->create() */
             classname = (char *)SvPV_nolen(ST(0));
             stack_size = MY_POOL.default_stack_size;
-            thread_exit_only = get_sv("threads::thread_exit_only", TRUE);
+            thread_exit_only = get_sv("threads::thread_exit_only", GV_ADD);
             exit_opt = (SvTRUE(thread_exit_only))
                                     ? PERL_ITHR_THREAD_EXIT_ONLY : 0;
         }
@@ -856,6 +991,8 @@ ithread_create(...)
                 switch (*str) {
                     case 'a':
                     case 'A':
+                    case 'l':
+                    case 'L':
                         context = G_ARRAY;
                         break;
                     case 's':
@@ -873,6 +1010,10 @@ ithread_create(...)
                 if (SvTRUE(*hv_fetch(specs, "array", 5, 0))) {
                     context = G_ARRAY;
                 }
+            } else if (hv_exists(specs, "list", 4)) {
+                if (SvTRUE(*hv_fetch(specs, "list", 4, 0))) {
+                    context = G_ARRAY;
+                }
             } else if (hv_exists(specs, "scalar", 6)) {
                 if (SvTRUE(*hv_fetch(specs, "scalar", 6, 0))) {
                     context = G_SCALAR;
@@ -931,6 +1072,7 @@ ithread_list(...)
         int list_context;
         IV count = 0;
         int want_running = 0;
+        int state;
         dMY_POOL;
     PPCODE:
         /* Class method only */
@@ -953,19 +1095,23 @@ ithread_list(...)
              thread != &MY_POOL.main_thread;
              thread = thread->next)
         {
+            MUTEX_LOCK(&thread->mutex);
+            state = thread->state;
+            MUTEX_UNLOCK(&thread->mutex);
+
             /* Ignore detached or joined threads */
-            if (thread->state & (PERL_ITHR_DETACHED|PERL_ITHR_JOINED)) {
+            if (state & PERL_ITHR_UNCALLABLE) {
                 continue;
             }
 
             /* Filter per parameter */
             if (items > 1) {
                 if (want_running) {
-                    if (thread->state & PERL_ITHR_FINISHED) {
+                    if (state & PERL_ITHR_FINISHED) {
                         continue;   /* Not running */
                     }
                 } else {
-                    if (! (thread->state & PERL_ITHR_FINISHED)) {
+                    if (! (state & PERL_ITHR_FINISHED)) {
                         continue;   /* Still running - not joinable yet */
                     }
                 }
@@ -1017,13 +1163,13 @@ void
 ithread_join(...)
     PREINIT:
         ithread *thread;
+        ithread *current_thread;
         int join_err;
-        AV *params;
+        AV *params = NULL;
         int len;
         int ii;
-#ifdef WIN32
-        DWORD waitcode;
-#else
+#ifndef WIN32
+        int rc_join;
         void *retval;
 #endif
         dMY_POOL;
@@ -1033,41 +1179,56 @@ ithread_join(...)
             Perl_croak(aTHX_ "Usage: $thr->join()");
         }
 
-        /* Check if the thread is joinable */
+        /* Check if the thread is joinable and not ourselves */
         thread = S_SV_to_ithread(aTHX_ ST(0));
-        join_err = (thread->state & (PERL_ITHR_DETACHED|PERL_ITHR_JOINED));
-        if (join_err) {
-            if (join_err & PERL_ITHR_DETACHED) {
-                Perl_croak(aTHX_ "Cannot join a detached thread");
-            } else {
-                Perl_croak(aTHX_ "Thread already joined");
-            }
+        current_thread = S_ithread_get(aTHX);
+
+        MUTEX_LOCK(&thread->mutex);
+        if ((join_err = (thread->state & PERL_ITHR_UNCALLABLE))) {
+            MUTEX_UNLOCK(&thread->mutex);
+            Perl_croak(aTHX_ (join_err & PERL_ITHR_DETACHED)
+                                ? "Cannot join a detached thread"
+                                : "Thread already joined");
+        } else if (thread->tid == current_thread->tid) {
+            MUTEX_UNLOCK(&thread->mutex);
+            Perl_croak(aTHX_ "Cannot join self");
         }
 
+        /* Mark as joined */
+        thread->state |= PERL_ITHR_JOINED;
+        MUTEX_UNLOCK(&thread->mutex);
+
+        MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
+        MY_POOL.joinable_threads--;
+        MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
+
         /* Join the thread */
 #ifdef WIN32
-        waitcode = WaitForSingleObject(thread->handle, INFINITE);
+        if (WaitForSingleObject(thread->handle, INFINITE) != WAIT_OBJECT_0) {
+            /* Timeout/abandonment unexpected here; check $^E */
+            Perl_croak(aTHX_ "PANIC: underlying join failed");
+        };
 #else
-        pthread_join(thread->thr, &retval);
+        if ((rc_join = pthread_join(thread->thr, &retval)) != 0) {
+            /* In progress/deadlock/unknown unexpected here; check $! */
+            errno = rc_join;
+            Perl_croak(aTHX_ "PANIC: underlying join failed");
+        };
 #endif
 
         MUTEX_LOCK(&thread->mutex);
-        /* Mark as joined */
-        thread->state |= PERL_ITHR_JOINED;
-
         /* Get the return value from the call_sv */
-        {
+        /* Objects do not survive this process - FIXME */
+        if ((thread->gimme & G_WANT) != G_VOID) {
             AV *params_copy;
             PerlInterpreter *other_perl;
             CLONE_PARAMS clone_params;
-            ithread *current_thread;
 
             params_copy = (AV *)SvRV(thread->params);
             other_perl = thread->interp;
             clone_params.stashes = newAV();
             clone_params.flags = CLONEf_JOIN_IN;
             PL_ptr_table = ptr_table_new();
-            current_thread = S_ithread_get(aTHX);
             S_ithread_set(aTHX_ thread);
             /* Ensure 'meaningful' addresses retain their meaning */
             ptr_table_store(PL_ptr_table, &other_perl->Isv_undef, &PL_sv_undef);
@@ -1081,15 +1242,11 @@ ithread_join(...)
             PL_ptr_table = NULL;
         }
 
-        /* We are finished with the thread */
-        S_ithread_clear(aTHX_ thread);
-        MUTEX_UNLOCK(&thread->mutex);
-
-        MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
-        if (! (thread->state & PERL_ITHR_DETACHED)) {
-            MY_POOL.joinable_threads--;
+        /* If thread didn't die, then we can free its interpreter */
+        if (! (thread->state & PERL_ITHR_DIED)) {
+            S_ithread_clear(aTHX_ thread);
         }
-        MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
+        S_ithread_free(aTHX_ thread);   /* Releases MUTEX */
 
         /* If no return values, then just return */
         if (! params) {
@@ -1123,37 +1280,43 @@ ithread_detach(...)
     CODE:
         PERL_UNUSED_VAR(items);
 
-        /* Check if the thread is detachable */
-        thread = S_SV_to_ithread(aTHX_ ST(0));
-        if ((detach_err = (thread->state & (PERL_ITHR_DETACHED|PERL_ITHR_JOINED)))) {
-            if (detach_err & PERL_ITHR_DETACHED) {
-                Perl_croak(aTHX_ "Thread already detached");
-            } else {
-                Perl_croak(aTHX_ "Cannot detach a joined thread");
-            }
-        }
-
         /* Detach the thread */
+        thread = S_SV_to_ithread(aTHX_ ST(0));
         MUTEX_LOCK(&MY_POOL.create_destruct_mutex);
         MUTEX_LOCK(&thread->mutex);
-        thread->state |= PERL_ITHR_DETACHED;
+        if (! (detach_err = (thread->state & PERL_ITHR_UNCALLABLE))) {
+            /* Thread is detachable */
+            thread->state |= PERL_ITHR_DETACHED;
 #ifdef WIN32
-        /* Windows has no 'detach thread' function */
+            /* Windows has no 'detach thread' function */
 #else
-        PERL_THREAD_DETACH(thread->thr);
+            PERL_THREAD_DETACH(thread->thr);
 #endif
-
-        if (thread->state & PERL_ITHR_FINISHED) {
-            MY_POOL.joinable_threads--;
-        } else {
-            MY_POOL.running_threads--;
-            MY_POOL.detached_threads++;
+            if (thread->state & PERL_ITHR_FINISHED) {
+                MY_POOL.joinable_threads--;
+            } else {
+                MY_POOL.running_threads--;
+                MY_POOL.detached_threads++;
+            }
         }
         MUTEX_UNLOCK(&thread->mutex);
         MUTEX_UNLOCK(&MY_POOL.create_destruct_mutex);
 
-        /* Try to cleanup thread */
-        S_ithread_destruct(aTHX_ thread);
+        if (detach_err) {
+            Perl_croak(aTHX_ (detach_err & PERL_ITHR_DETACHED)
+                                ? "Thread already detached"
+                                : "Cannot detach a joined thread");
+        }
+
+        /* If thread is finished and didn't die,
+         * then we can free its interpreter */
+        MUTEX_LOCK(&thread->mutex);
+        if ((thread->state & PERL_ITHR_FINISHED) &&
+            ! (thread->state & PERL_ITHR_DIED))
+        {
+            S_ithread_clear(aTHX_ thread);
+        }
+        S_ithread_free(aTHX_ thread);   /* Releases MUTEX */
 
 
 void
@@ -1236,6 +1399,7 @@ ithread_object(...)
         char *classname;
         UV tid;
         ithread *thread;
+        int state;
         int have_obj = 0;
         dMY_POOL;
     CODE:
@@ -1261,7 +1425,10 @@ ithread_object(...)
             /* Look for TID */
             if (thread->tid == tid) {
                 /* Ignore if detached or joined */
-                if (! (thread->state & (PERL_ITHR_DETACHED|PERL_ITHR_JOINED))) {
+                MUTEX_LOCK(&thread->mutex);
+                state = thread->state;
+                MUTEX_UNLOCK(&thread->mutex);
+                if (! (state & PERL_ITHR_UNCALLABLE)) {
                     /* Put object on stack */
                     ST(0) = sv_2mortal(S_ithread_to_SV(aTHX_ Nullsv, thread, classname, TRUE));
                     have_obj = 1;
@@ -1323,6 +1490,9 @@ ithread_set_stack_size(...)
         if (sv_isobject(ST(0))) {
             Perl_croak(aTHX_ "Cannot change stack size of an existing thread");
         }
+        if (! looks_like_number(ST(1))) {
+            Perl_croak(aTHX_ "Stack size must be numeric");
+        }
 
         old_size = MY_POOL.default_stack_size;
         MY_POOL.default_stack_size = S_good_stack_size(aTHX_ SvIV(ST(1)));
@@ -1341,7 +1511,9 @@ ithread_is_running(...)
         }
 
         thread = INT2PTR(ithread *, SvIV(SvRV(ST(0))));
+        MUTEX_LOCK(&thread->mutex);
         ST(0) = (thread->state & PERL_ITHR_FINISHED) ? &PL_sv_no : &PL_sv_yes;
+        MUTEX_UNLOCK(&thread->mutex);
         /* XSRETURN(1); - implied */
 
 
@@ -1352,7 +1524,9 @@ ithread_is_detached(...)
     CODE:
         PERL_UNUSED_VAR(items);
         thread = S_SV_to_ithread(aTHX_ ST(0));
+        MUTEX_LOCK(&thread->mutex);
         ST(0) = (thread->state & PERL_ITHR_DETACHED) ? &PL_sv_yes : &PL_sv_no;
+        MUTEX_UNLOCK(&thread->mutex);
         /* XSRETURN(1); - implied */
 
 
@@ -1369,7 +1543,7 @@ ithread_is_joinable(...)
         thread = INT2PTR(ithread *, SvIV(SvRV(ST(0))));
         MUTEX_LOCK(&thread->mutex);
         ST(0) = ((thread->state & PERL_ITHR_FINISHED) &&
-                 ! (thread->state & (PERL_ITHR_DETACHED|PERL_ITHR_JOINED)))
+                 ! (thread->state & PERL_ITHR_UNCALLABLE))
             ? &PL_sv_yes : &PL_sv_no;
         MUTEX_UNLOCK(&thread->mutex);
         /* XSRETURN(1); - implied */
@@ -1382,9 +1556,9 @@ ithread_wantarray(...)
     CODE:
         PERL_UNUSED_VAR(items);
         thread = S_SV_to_ithread(aTHX_ ST(0));
-        ST(0) = (thread->gimme & G_ARRAY) ? &PL_sv_yes :
-                (thread->gimme & G_VOID)  ? &PL_sv_undef
-                           /* G_SCALAR */ : &PL_sv_no;
+        ST(0) = ((thread->gimme & G_WANT) == G_ARRAY) ? &PL_sv_yes :
+                ((thread->gimme & G_WANT) == G_VOID)  ? &PL_sv_undef
+                                       /* G_SCALAR */ : &PL_sv_no;
         /* XSRETURN(1); - implied */
 
 
@@ -1405,6 +1579,59 @@ ithread_set_thread_exit_only(...)
         }
         MUTEX_UNLOCK(&thread->mutex);
 
+
+void
+ithread_error(...)
+    PREINIT:
+        ithread *thread;
+        SV *err = NULL;
+    CODE:
+        /* Object method only */
+        if ((items != 1) || ! sv_isobject(ST(0))) {
+            Perl_croak(aTHX_ "Usage: $thr->err()");
+        }
+
+        thread = INT2PTR(ithread *, SvIV(SvRV(ST(0))));
+        MUTEX_LOCK(&thread->mutex);
+
+        /* If thread died, then clone the error into the calling thread */
+        if (thread->state & PERL_ITHR_DIED) {
+            PerlInterpreter *other_perl;
+            CLONE_PARAMS clone_params;
+            ithread *current_thread;
+
+            other_perl = thread->interp;
+            clone_params.stashes = newAV();
+            clone_params.flags = CLONEf_JOIN_IN;
+            PL_ptr_table = ptr_table_new();
+            current_thread = S_ithread_get(aTHX);
+            S_ithread_set(aTHX_ thread);
+            /* Ensure 'meaningful' addresses retain their meaning */
+            ptr_table_store(PL_ptr_table, &other_perl->Isv_undef, &PL_sv_undef);
+            ptr_table_store(PL_ptr_table, &other_perl->Isv_no, &PL_sv_no);
+            ptr_table_store(PL_ptr_table, &other_perl->Isv_yes, &PL_sv_yes);
+            err = sv_dup(thread->err, &clone_params);
+            S_ithread_set(aTHX_ current_thread);
+            SvREFCNT_dec(clone_params.stashes);
+            SvREFCNT_inc_void(err);
+            /* If error was an object, bless it into the correct class */
+            if (thread->err_class) {
+                sv_bless(err, gv_stashpv(thread->err_class, 1));
+            }
+            ptr_table_free(PL_ptr_table);
+            PL_ptr_table = NULL;
+        }
+
+        MUTEX_UNLOCK(&thread->mutex);
+
+        if (! err) {
+            XSRETURN_UNDEF;
+        }
+
+        ST(0) = sv_2mortal(err);
+        /* XSRETURN(1); - implied */
+
+
 #endif /* USE_ITHREADS */