Make thread suspension really work.
authorDavid Xu <davidxu@dragonflybsd.org>
Sun, 19 Mar 2006 13:07:12 +0000 (13:07 +0000)
committerDavid Xu <davidxu@dragonflybsd.org>
Sun, 19 Mar 2006 13:07:12 +0000 (13:07 +0000)
lib/libthread_xu/thread/thr_create.c
lib/libthread_xu/thread/thr_private.h
lib/libthread_xu/thread/thr_resume_np.c
lib/libthread_xu/thread/thr_sig.c
lib/libthread_xu/thread/thr_suspend_np.c

index 1f8e4a8..6ee82da 100644 (file)
@@ -32,7 +32,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/lib/libpthread/thread/thr_create.c,v 1.58 2004/10/23 23:28:36 davidxu Exp $
- * $DragonFly: src/lib/libthread_xu/thread/thr_create.c,v 1.6 2006/03/12 11:28:06 davidxu Exp $
+ * $DragonFly: src/lib/libthread_xu/thread/thr_create.c,v 1.7 2006/03/19 13:07:12 davidxu Exp $
  */
 #include <errno.h>
 #include <stdlib.h>
@@ -198,6 +198,10 @@ _pthread_create(pthread_t * thread, const pthread_attr_t * attr,
                        THR_THREAD_LOCK(curthread, new_thread);
                new_thread->state = PS_DEAD;
                new_thread->terminated = 1;
+               if (new_thread->flags & THR_FLAGS_NEED_SUSPEND) {
+                       new_thread->cycle++;
+                       _thr_umtx_wake(&new_thread->cycle, INT_MAX);
+               }
                THR_THREAD_UNLOCK(curthread, new_thread);
                THREAD_LIST_LOCK(curthread);
                _thread_active_threads--;
@@ -241,12 +245,12 @@ thread_start(void *arg)
        start_arg->started = 1;
        _thr_umtx_wake(&start_arg->started, 1);
 
-       THR_LOCK(curthread);
-       THR_UNLOCK(curthread);
-
        /* Thread was created with all signals blocked, unblock them. */
        __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, NULL);
 
+       THR_LOCK(curthread);
+       THR_UNLOCK(curthread);
+
        if (curthread->flags & THR_FLAGS_NEED_SUSPEND)
                _thr_suspend_check(curthread);
 
index 19633ec..c4b24d2 100644 (file)
@@ -32,7 +32,7 @@
  * Private thread definitions for the uthread kernel.
  *
  * $FreeBSD: src/lib/libpthread/thread/thr_private.h,v 1.120 2004/11/01 10:49:34 davidxu Exp $
- * $DragonFly: src/lib/libthread_xu/thread/thr_private.h,v 1.8 2006/03/12 11:28:06 davidxu Exp $
+ * $DragonFly: src/lib/libthread_xu/thread/thr_private.h,v 1.9 2006/03/19 13:07:12 davidxu Exp $
  */
 
 #ifndef _THR_PRIVATE_H
@@ -370,6 +370,12 @@ struct pthread {
        /* How many low level locks the thread held. */
        int                     locklevel;
 
+       /*
+        * Set to non-zero when this thread has entered a critical
+        * region.  We allow for recursive entries into critical regions.
+        */
+       int                     critical_count;
+
        /* Signal blocked counter. */
        int                     sigblock;
 
@@ -514,6 +520,10 @@ struct pthread {
        td_event_msg_t          event_buf;
 };
 
+#define        THR_IN_CRITICAL(thrd)                           \
+       (((thrd)->locklevel > 0) ||                     \
+       ((thrd)->critical_count > 0))
+
 #define THR_UMTX_TRYLOCK(thrd, lck)                    \
        _thr_umtx_trylock((lck), (thrd)->tid)
 
@@ -532,14 +542,22 @@ do {                                                      \
        _thr_umtx_lock(lck, (thrd)->tid);               \
 } while (0)
 
-#define        THR_LOCK_RELEASE(thrd, lck)                     \
+#ifdef _PTHREADS_INVARIANTS
+#define        THR_ASSERT_LOCKLEVEL(thrd)                      \
 do {                                                   \
-       if ((thrd)->locklevel > 0) {                    \
-               _thr_umtx_unlock((lck), (thrd)->tid);   \
-               (thrd)->locklevel--;                    \
-       } else {                                        \
+       if (__predict_false((thrd)->locklevel <= 0))    \
                _thr_assert_lock_level();               \
-       }                                               \
+} while (0)
+#else
+#define THR_ASSERT_LOCKLEVEL(thrd)
+#endif
+
+#define        THR_LOCK_RELEASE(thrd, lck)                     \
+do {                                                   \
+       THR_ASSERT_LOCKLEVEL(thrd);                     \
+       _thr_umtx_unlock((lck), (thrd)->tid);           \
+       (thrd)->locklevel--;                            \
+       _thr_ast(thrd);                                 \
 } while (0)
 
 #define        THR_LOCK(curthrd)               THR_LOCK_ACQUIRE(curthrd, &(curthrd)->lock)
@@ -748,6 +766,7 @@ void        _thr_link(struct pthread *curthread, struct pthread *thread);
 void   _thr_unlink(struct pthread *curthread, struct pthread *thread);
 void   _thr_suspend_check(struct pthread *curthread);
 void   _thr_assert_lock_level() __dead2;
+void   _thr_ast(struct pthread *);
 int    _thr_get_tid(void);
 void   _thr_report_creation(struct pthread *curthread,
                           struct pthread *newthread);
index 77a39cd..de69e08 100644 (file)
@@ -30,7 +30,7 @@
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/lib/libpthread/thread/thr_resume_np.c,v 1.18 2003/07/23 02:11:07 deischen Exp $
- * $DragonFly: src/lib/libthread_xu/thread/thr_resume_np.c,v 1.2 2005/03/29 19:26:20 joerg Exp $
+ * $DragonFly: src/lib/libthread_xu/thread/thr_resume_np.c,v 1.3 2006/03/19 13:07:12 davidxu Exp $
  */
 
 #include <machine/tls.h>
@@ -90,5 +90,4 @@ resume_common(struct pthread *thread)
        thread->flags &= ~THR_FLAGS_NEED_SUSPEND;
        thread->cycle++;
        _thr_umtx_wake(&thread->cycle, 1);
-       _thr_send_sig(thread, SIGCANCEL);
 }
index 48cf5e3..4c57e04 100644 (file)
@@ -29,7 +29,7 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $DragonFly: src/lib/libthread_xu/thread/thr_sig.c,v 1.4 2005/03/29 19:26:20 joerg Exp $
+ * $DragonFly: src/lib/libthread_xu/thread/thr_sig.c,v 1.5 2006/03/19 13:07:12 davidxu Exp $
  */
 
 #include <sys/param.h>
@@ -60,34 +60,60 @@ sigcancel_handler(int sig, siginfo_t *info, ucontext_t *ucp)
 
        if (curthread->cancelflags & THR_CANCEL_AT_POINT)
                pthread_testcancel();
-       if (curthread->flags & THR_FLAGS_NEED_SUSPEND) {
-               __sys_sigprocmask(SIG_SETMASK, &ucp->uc_sigmask, NULL);
-               _thr_suspend_check(curthread);
+       _thr_ast(curthread);
+}
+
+void
+_thr_ast(struct pthread *curthread)
+{
+       if (!THR_IN_CRITICAL(curthread)) {
+               if (__predict_false((curthread->flags &
+                   (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
+                       == THR_FLAGS_NEED_SUSPEND))
+                       _thr_suspend_check(curthread);
        }
 }
 
 void
 _thr_suspend_check(struct pthread *curthread)
 {
-       long cycle;
+       umtx_t cycle;
 
-       /* Async suspend. */
+       /*
+        * Blocks SIGCANCEL which other threads must send.
+        */
        _thr_signal_block(curthread);
-       THR_LOCK(curthread);
-       if ((curthread->flags & (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED))
-               == THR_FLAGS_NEED_SUSPEND) {
+
+       /*
+        * Increase critical_count, here we don't use THR_LOCK/UNLOCK
+        * because we are leaf code, we don't want to recursively call
+        * ourself.
+        */
+       curthread->critical_count++;
+       THR_UMTX_LOCK(curthread, &(curthread)->lock);
+       while ((curthread->flags & (THR_FLAGS_NEED_SUSPEND |
+               THR_FLAGS_SUSPENDED)) == THR_FLAGS_NEED_SUSPEND) {
+               curthread->cycle++;
+               cycle = curthread->cycle;
+
+               /* Wake the thread suspending us. */
+               _thr_umtx_wake(&curthread->cycle, INT_MAX);
+
+               /*
+                * if we are from pthread_exit, we don't want to
+                * suspend, just go and die.
+                */
+               if (curthread->state == PS_DEAD)
+                       break;
                curthread->flags |= THR_FLAGS_SUSPENDED;
-               while (curthread->flags & THR_FLAGS_NEED_SUSPEND) {
-                       cycle = curthread->cycle;
-                       THR_UNLOCK(curthread);
-                       _thr_signal_unblock(curthread);
-                       _thr_umtx_wait(&curthread->cycle, cycle, NULL, 0);
-                       _thr_signal_block(curthread);
-                       THR_LOCK(curthread);
-               }
+               THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
+               _thr_umtx_wait(&curthread->cycle, cycle, NULL, 0);
+               THR_UMTX_LOCK(curthread, &(curthread)->lock);
                curthread->flags &= ~THR_FLAGS_SUSPENDED;
        }
-       THR_UNLOCK(curthread);
+       THR_UMTX_UNLOCK(curthread, &(curthread)->lock);
+       curthread->critical_count--;
+
        _thr_signal_unblock(curthread);
 }
 
index a8779de..db70433 100644 (file)
  * SUCH DAMAGE.
  *
  * $FreeBSD: src/lib/libpthread/thread/thr_suspend_np.c,v 1.19 2003/05/04 16:17:01 deischen Exp $
- * $DragonFly: src/lib/libthread_xu/thread/thr_suspend_np.c,v 1.2 2005/03/29 19:26:20 joerg Exp $
+ * $DragonFly: src/lib/libthread_xu/thread/thr_suspend_np.c,v 1.3 2006/03/19 13:07:12 davidxu Exp $
  */
 
-#include <machine/tls.h>
 #include <errno.h>
 #include <pthread.h>
+
 #include "thr_private.h"
 
-static void suspend_common(struct pthread *thread);
+static int suspend_common(struct pthread *, struct pthread *,
+               int);
 
 __weak_reference(_pthread_suspend_np, pthread_suspend_np);
 __weak_reference(_pthread_suspend_all_np, pthread_suspend_all_np);
@@ -59,7 +60,7 @@ _pthread_suspend_np(pthread_t thread)
            == 0) {
                /* Lock the threads scheduling queue: */
                THR_THREAD_LOCK(curthread, thread);
-               suspend_common(thread);
+               suspend_common(curthread, thread, 1);
                /* Unlock the threads scheduling queue: */
                THR_THREAD_UNLOCK(curthread, thread);
 
@@ -72,29 +73,71 @@ _pthread_suspend_np(pthread_t thread)
 void
 _pthread_suspend_all_np(void)
 {
-       struct pthread  *curthread = tls_get_curthread();
-       struct pthread  *thread;
+       struct pthread *curthread = tls_get_curthread();
+       struct pthread *thread;
+       int ret;
 
-       /* Take the thread list lock: */
        THREAD_LIST_LOCK(curthread);
 
        TAILQ_FOREACH(thread, &_thread_list, tle) {
                if (thread != curthread) {
                        THR_THREAD_LOCK(curthread, thread);
-                       suspend_common(thread);
+                       if (thread->state != PS_DEAD &&
+                          !(thread->flags & THR_FLAGS_SUSPENDED))
+                           thread->flags |= THR_FLAGS_NEED_SUSPEND;
+                       THR_THREAD_UNLOCK(curthread, thread);
+               }
+       }
+       /* thr_kill(-1, SIGCANCEL); */
+
+restart:
+       TAILQ_FOREACH(thread, &_thread_list, tle) {
+               if (thread != curthread) {
+                       /* First try to suspend the thread without waiting */
+                       THR_THREAD_LOCK(curthread, thread);
+                       ret = suspend_common(curthread, thread, 0);
+                       if (ret == 0) {
+                               /* Can not suspend, try to wait */
+                               thread->refcount++;
+                               THREAD_LIST_UNLOCK(curthread);
+                               suspend_common(curthread, thread, 1);
+                               THR_THREAD_UNLOCK(curthread, thread);
+                               THREAD_LIST_LOCK(curthread);
+                               _thr_ref_delete_unlocked(curthread, thread);
+                               /*
+                                * Because we were blocked, things may have
+                                * been changed, we have to restart the
+                                * process.
+                                */
+                               goto restart;
+                       }
                        THR_THREAD_UNLOCK(curthread, thread);
                }
        }
 
-       /* Release the thread list lock: */
        THREAD_LIST_UNLOCK(curthread);
 }
 
-static void
-suspend_common(struct pthread *thread)
+static int
+suspend_common(struct pthread *curthread, struct pthread *thread,
+       int waitok)
 {
-       if (thread->state != PS_DEAD) {
+       umtx_t tmp;
+
+       while (thread->state != PS_DEAD &&
+             !(thread->flags & THR_FLAGS_SUSPENDED)) {
                thread->flags |= THR_FLAGS_NEED_SUSPEND;
+               tmp = thread->cycle;
+               THR_THREAD_UNLOCK(curthread, thread);
                _thr_send_sig(thread, SIGCANCEL);
+               if (waitok) {
+                       _thr_umtx_wait(&thread->cycle, tmp, NULL, 0);
+                       THR_THREAD_LOCK(curthread, thread);
+               } else {
+                       THR_THREAD_LOCK(curthread, thread);
+                       return (0);
+               }
        }
+
+       return (1);
 }