Bugzilla bug 214411: implement automatic attaching and detaching of Solaris NSPR_4_4_BASE
authorwtc%netscape.com
Thu, 11 Sep 2003 00:29:37 +0000
changeset 2898 7309a2ba2269a0b17cb2fcc2fd0467e8cc9df84b
parent 2895 12baccbc31e3f491af67c9b346153d6c3eee1fb2
child 2899 f8ec1c644ed73eedcfce2b37e303a2da8ef96c41
child 2901 29a3b31a34281372e8dedd62af01d31149c867dd
push idunknown
push userunknown
push dateunknown
bugs214411
Bugzilla bug 214411: implement automatic attaching and detaching of Solaris native threads. The patch contains contribution by Gerard Roos <gerard.roos@adnovum.ch>. Modified Files: _solaris.h primpl.h solaris.c pruthr.c foreign.c provider.c
pr/include/md/_solaris.h
pr/include/private/primpl.h
pr/src/md/unix/solaris.c
pr/src/threads/combined/pruthr.c
pr/tests/foreign.c
pr/tests/provider.c
--- a/pr/include/md/_solaris.h
+++ b/pr/include/md/_solaris.h
@@ -216,24 +216,26 @@ NSPR_API(PRStatus) _MD_CreateThread(PRTh
 extern struct PRLock *_pr_schedLock;
 
 /*
 ** Thread Local Storage 
 */
 
 #define THREAD_KEY_T thread_key_t
 
+extern struct PRThread *_pr_attached_thread_tls();
 extern struct PRThread *_pr_current_thread_tls();
 extern struct _PRCPU *_pr_current_cpu_tls();
 extern struct PRThread *_pr_last_thread_tls();
 
 extern THREAD_KEY_T threadid_key;
 extern THREAD_KEY_T cpuid_key;
 extern THREAD_KEY_T last_thread_key;
 
+#define _MD_GET_ATTACHED_THREAD() _pr_attached_thread_tls()
 #define _MD_CURRENT_THREAD() _pr_current_thread_tls()
 #define _MD_CURRENT_CPU() _pr_current_cpu_tls()
 #define _MD_LAST_THREAD() _pr_last_thread_tls()
 	
 #define _MD_SET_CURRENT_THREAD(newval) 			\
 	PR_BEGIN_MACRO					\
 	thr_setspecific(threadid_key, (void *)newval);	\
 	PR_END_MACRO
--- a/pr/include/private/primpl.h
+++ b/pr/include/private/primpl.h
@@ -299,17 +299,18 @@ typedef struct _PRInterruptTable {
     const char *name;
     PRUintn missed_bit;
     void (*handler)(void);
 } _PRInterruptTable;
 
 #define _PR_CPU_PTR(_qp) \
     ((_PRCPU*) ((char*) (_qp) - offsetof(_PRCPU,links)))
 
-#if !defined(IRIX) && !defined(WIN32) && !defined(XP_OS2)
+#if !defined(IRIX) && !defined(WIN32) && !defined(XP_OS2) \
+        && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY))
 #define _MD_GET_ATTACHED_THREAD()        (_PR_MD_CURRENT_THREAD())
 #endif
 
 #ifdef _PR_LOCAL_THREADS_ONLY 
 
 NSPR_API(struct _PRCPU *)              _pr_currentCPU;
 NSPR_API(PRThread *)                   _pr_currentThread;
 NSPR_API(PRThread *)                   _pr_lastThread;
--- a/pr/src/md/unix/solaris.c
+++ b/pr/src/md/unix/solaris.c
@@ -178,19 +178,35 @@ extern int syscall();  /* not declared i
 static sigset_t old_mask;	/* store away original gc thread sigmask */
 static PRIntn gcprio;		/* store away original gc thread priority */
 
 THREAD_KEY_T threadid_key;
 THREAD_KEY_T cpuid_key;
 THREAD_KEY_T last_thread_key;
 static sigset_t set, oldset;
 
+static void
+threadid_key_destructor(void *value)
+{
+    PRThread *me = (PRThread *)value;
+    PR_ASSERT((me != NULL) && (me->flags & _PR_ATTACHED));
+    /*
+     * The Solaris thread library sets the thread specific
+     * data (the current thread) to NULL before invoking
+     * the destructor.  We need to restore it to prevent the
+     * _PR_MD_CURRENT_THREAD() call in _PRI_DetachThread()
+     * from attaching the thread again.
+     */
+    _PR_MD_SET_CURRENT_THREAD(me);
+    _PRI_DetachThread();
+}
+
 void _MD_EarlyInit(void)
 {
-    THR_KEYCREATE(&threadid_key, NULL);
+    THR_KEYCREATE(&threadid_key, threadid_key_destructor);
     THR_KEYCREATE(&cpuid_key, NULL);
     THR_KEYCREATE(&last_thread_key, NULL);
     sigemptyset(&set);
     sigaddset(&set, SIGALRM);
 }
 
 PRStatus _MD_CreateThread(PRThread *thread, 
 					void (*start)(void *), 
@@ -332,24 +348,39 @@ void _MD_lock(struct _MDLock *md_lock)
 	PR_ASSERT(lock->owner == NULL);
         return;
     }
 #endif /* DEBUG */
 
     mutex_lock(&md_lock->lock);
 }
 
-PRThread *_pr_current_thread_tls()
+PRThread *_pr_attached_thread_tls()
 {
     PRThread *ret;
 
     thr_getspecific(threadid_key, (void **)&ret);
     return ret;
 }
 
+PRThread *_pr_current_thread_tls()
+{
+    PRThread *thread;
+
+    thread = _MD_GET_ATTACHED_THREAD();
+
+    if (NULL == thread) {
+        thread = _PRI_AttachThread(
+            PR_USER_THREAD, PR_PRIORITY_NORMAL, NULL, 0);
+    }
+    PR_ASSERT(thread != NULL);
+
+    return thread;
+}
+
 PRStatus
 _MD_wait(PRThread *thread, PRIntervalTime ticks)
 {
         _MD_WAIT_SEM(&thread->md.waiter_sem);
         return PR_SUCCESS;
 }
 
 PRStatus
--- a/pr/src/threads/combined/pruthr.c
+++ b/pr/src/threads/combined/pruthr.c
@@ -1505,23 +1505,24 @@ PR_IMPLEMENT(PRThread*) PR_AttachThread(
 #pragma unused( type, priority, stack )
 #endif
     return PR_GetCurrentThread();
 }
 
 PR_IMPLEMENT(void) PR_DetachThread(void)
 {
     /*
-     * On IRIX and Windows, foreign threads are detached when
+     * On IRIX, Solaris, and Windows, foreign threads are detached when
      * they terminate.
      */
-#if !defined(IRIX) && !defined(WIN32)
+#if !defined(IRIX) && !defined(WIN32) \
+        && !(defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY))
     PRThread *me;
     if (_pr_initialized) {
-        me = _MD_GET_ATTACHED_THREAD();
+        me = _PR_MD_GET_ATTACHED_THREAD();
         if ((me != NULL) && (me->flags & _PR_ATTACHED))
             _PRI_DetachThread();
     }
 #endif
 }
 
 void _PRI_DetachThread(void)
 {
--- a/pr/tests/foreign.c
+++ b/pr/tests/foreign.c
@@ -58,17 +58,17 @@
 #include "prtypes.h"
 #include "prprf.h"
 #include "plgetopt.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 
 static enum {
-    thread_nspr, thread_pthread, thread_sproc, thread_win32
+    thread_nspr, thread_pthread, thread_uithread, thread_sproc, thread_win32
 } thread_provider;
 
 typedef void (*StartFn)(void*);
 typedef struct StartObject
 {
     StartFn start;
     void *arg;
 } StartObject;
@@ -89,16 +89,28 @@ static void *pthread_start(void *arg)
     StartFn start = ((StartObject*)arg)->start;
     void *data = ((StartObject*)arg)->arg;
     PR_Free(arg);
     start(data);
     return NULL;
 }  /* pthread_start */
 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
 
+#if defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
+#include <thread.h>
+static void *uithread_start(void *arg)
+{
+    StartFn start = ((StartObject*)arg)->start;
+    void *data = ((StartObject*)arg)->arg;
+    PR_Free(arg);
+    start(data);
+    return NULL;
+}  /* uithread_start */
+#endif /* defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY) */
+
 #if defined(IRIX) && !defined(_PR_PTHREADS)
 #include <sys/types.h>
 #include <sys/prctl.h>
 static void sproc_start(void *arg, PRSize size)
 {
     StartObject *so = (StartObject*)arg;
     StartFn start = so->start;
     void *data = so->arg;
@@ -164,16 +176,39 @@ static PRStatus CreateThread(StartFn sta
             return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
         }
 #else
         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
         rv = PR_FAILURE;
         break;
 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
 
+    case thread_uithread:
+#if defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
+        {
+            int rv;
+            thread_t id;
+            long flags;
+            StartObject *start_object;
+            start_object = PR_NEW(StartObject);
+            PR_ASSERT(NULL != start_object);
+            start_object->start = start;
+            start_object->arg = arg;
+
+            flags = THR_DETACHED;
+
+            rv = thr_create(NULL, NULL, uithread_start, start_object, flags, &id);
+            return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+        }
+#else
+        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+        rv = PR_FAILURE;
+        break;
+#endif /* defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY) */
+
     case thread_sproc:
 #if defined(IRIX) && !defined(_PR_PTHREADS)
         {
             PRInt32 pid;
             StartObject *start_object;
             start_object = PR_NEW(StartObject);
             PR_ASSERT(NULL != start_object);
             start_object->start = start;
@@ -330,16 +365,18 @@ PRIntn main(PRIntn argc, char **argv)
 	PRInt32	thread_cnt = DEFAULT_THREAD_COUNT;
 	PLOptStatus os;
 	PLOptState *opt = PL_CreateOptState(argc, argv, "dt:");
 
 #if defined(WIN32)
 	thread_provider = thread_win32;
 #elif defined(_PR_PTHREADS)
 	thread_provider = thread_pthread;
+#elif defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
+	thread_provider = thread_uithread;
 #elif defined(IRIX)
 	thread_provider = thread_sproc;
 #else
     thread_provider = thread_nspr;
 #endif
 
 
 	while (PL_OPT_EOL != (os = PL_GetNextOpt(opt)))
--- a/pr/tests/provider.c
+++ b/pr/tests/provider.c
@@ -122,17 +122,17 @@ typedef enum Verbosity
     TEST_LOG_WARNING,
     TEST_LOG_NOTICE,
     TEST_LOG_INFO,
     TEST_LOG_STATUS,
     TEST_LOG_VERBOSE
 } Verbosity;
 
 static enum {
-    thread_nspr, thread_pthread, thread_sproc, thread_win32
+    thread_nspr, thread_pthread, thread_uithread, thread_sproc, thread_win32
 } thread_provider;
 
 static PRInt32 domain = AF_INET;
 static PRInt32 protocol = 6;  /* TCP */
 static PRFileDesc *debug_out = NULL;
 static PRBool debug_mode = PR_FALSE;
 static PRBool pthread_stats = PR_FALSE;
 static Verbosity verbosity = TEST_LOG_ALWAYS;
@@ -653,16 +653,30 @@ static void *pthread_start(void *arg)
     StartFn start = so->start;
     void *data = so->arg;
     PR_Free(so);
     start(data);
     return NULL;
 }  /* pthread_start */
 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
 
+#if defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
+#include <thread.h>
+
+static void *uithread_start(void *arg)
+{
+    StartObject *so = (StartObject*)arg;
+    StartFn start = so->start;
+    void *data = so->arg;
+    PR_Free(so);
+    start(data);
+    return NULL;
+}  /* uithread_start */
+#endif /* defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY) */
+
 #if defined(IRIX) && !defined(_PR_PTHREADS)
 #include <sys/types.h>
 #include <sys/prctl.h>
 static void sproc_start(void *arg, PRSize size)
 {
     StartObject *so = (StartObject*)arg;
     StartFn start = so->start;
     void *data = so->arg;
@@ -693,16 +707,21 @@ static PRStatus JoinThread(PRThread *thr
     case thread_nspr:
         rv = PR_JoinThread(thread);
         break;
     case thread_pthread:
 #if defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS)
         rv = PR_SUCCESS;
         break;
 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
+    case thread_uithread:
+#if defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
+        rv = PR_SUCCESS;
+        break;
+#endif /* defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY) */
     case thread_win32:
 #if defined(WIN32)
         rv = PR_SUCCESS;
         break;
 #endif
     default:
         rv = PR_FAILURE;
         break;
@@ -754,16 +773,39 @@ static PRStatus NewThread(
             return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
         }
 #else
         PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
         rv = PR_FAILURE;
 #endif /* defined(_PR_PTHREADS) && !defined(_PR_DCETHREADS) */
         break;
 
+    case thread_uithread:
+#if defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
+        {
+            int rv;
+            thread_t id;
+            long flags;
+            StartObject *start_object;
+            start_object = PR_NEW(StartObject);
+            PR_ASSERT(NULL != start_object);
+            start_object->start = start;
+            start_object->arg = arg;
+
+            flags = THR_DETACHED;
+
+            rv = thr_create(NULL, NULL, uithread_start, start_object, flags, &id);
+            return (0 == rv) ? PR_SUCCESS : PR_FAILURE;
+        }
+#else
+        PR_SetError(PR_NOT_IMPLEMENTED_ERROR, 0);
+        rv = PR_FAILURE;
+#endif /* defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY) */
+        break;
+
     case thread_sproc:
 #if defined(IRIX) && !defined(_PR_PTHREADS)
         {
             PRInt32 pid;
             StartObject *start_object;
             start_object = PR_NEW(StartObject);
             PR_ASSERT(NULL != start_object);
             start_object->start = start;
@@ -942,19 +984,25 @@ exit:
 }  /* Worker */
 
 static void PR_CALLBACK Server(void *arg)
 {
     PRStatus rv;
     PRNetAddr serverAddress;
     CSServer_t *server = (CSServer_t*)arg;
     PRThread *me = server->thread = PR_CurrentThread();
+    PRSocketOptionData sockOpt;
 
     server->listener = PR_Socket(domain, SOCK_STREAM, protocol);
 
+    sockOpt.option = PR_SockOpt_Reuseaddr;
+    sockOpt.value.reuse_addr = PR_TRUE;
+    rv = PR_SetSocketOption(server->listener, &sockOpt);
+    TEST_ASSERT(PR_SUCCESS == rv);
+
     memset(&serverAddress, 0, sizeof(serverAddress));
     rv = PR_InitializeNetAddr(PR_IpAddrAny, DEFAULT_PORT, &serverAddress);
 
     rv = PR_Bind(server->listener, &serverAddress);
     TEST_ASSERT(PR_SUCCESS == rv);
 
     rv = PR_Listen(server->listener, server->backlog);
     TEST_ASSERT(PR_SUCCESS == rv);
@@ -1058,17 +1106,17 @@ static void Help(void)
     PR_fprintf(debug_out, "\t-a <n>       threads allowed in accept        (5)\n");
     PR_fprintf(debug_out, "\t-b <n>       backlock for listen              (5)\n");
     PR_fprintf(debug_out, "\t-c <threads> number of clients to create      (1)\n");
     PR_fprintf(debug_out, "\t-w <threads> minimal number of server threads (1)\n");
     PR_fprintf(debug_out, "\t-W <threads> maximum number of server threads (1)\n");
     PR_fprintf(debug_out, "\t-e <seconds> duration of the test in seconds  (10)\n");
     PR_fprintf(debug_out, "\t-s <string>  dsn name of server               (localhost)\n");
     PR_fprintf(debug_out, "\t-G           use GLOBAL threads               (LOCAL)\n");
-    PR_fprintf(debug_out, "\t-T <string>  thread provider ('n' | 'p' | 'w')(n)\n");
+    PR_fprintf(debug_out, "\t-T <string>  thread provider ('n' | 'p' | 'u' | 'w')(n)\n");
     PR_fprintf(debug_out, "\t-X           use XTP as transport             (TCP)\n");
     PR_fprintf(debug_out, "\t-6           Use IPv6                         (IPv4)\n");
     PR_fprintf(debug_out, "\t-v           verbosity (accumulative)         (0)\n");
     PR_fprintf(debug_out, "\t-p           pthread statistics               (FALSE)\n");
     PR_fprintf(debug_out, "\t-d           debug mode                       (FALSE)\n");
     PR_fprintf(debug_out, "\t-h           this message\n");
 }  /* Help */
 
@@ -1110,16 +1158,18 @@ PRIntn main(PRIntn argc, char** argv)
 
     PLOptStatus os;
     PLOptState *opt = PL_CreateOptState(argc, argv, "GX6b:a:c:w:W:e:s:T:vdhp");
 
 #if defined(WIN32)
 	thread_provider = thread_win32;
 #elif defined(_PR_PTHREADS)
 	thread_provider = thread_pthread;
+#elif defined(SOLARIS) && defined(_PR_GLOBAL_THREADS_ONLY)
+	thread_provider = thread_uithread;
 #elif defined(IRIX)
 	thread_provider = thread_sproc;
 #else
     thread_provider = thread_nspr;
 #endif
 
     debug_out = PR_GetSpecialFD(PR_StandardError);
 
@@ -1141,16 +1191,17 @@ PRIntn main(PRIntn argc, char** argv)
             accepting = atoi(opt->value);
             break;
         case 'b':  /* the value for backlock */
             backlog = atoi(opt->value);
             break;
         case 'T':  /* the thread provider */
             if ('n' == *opt->value) thread_provider = thread_nspr;
             else if ('p' == *opt->value) thread_provider = thread_pthread;
+            else if ('u' == *opt->value) thread_provider = thread_uithread;
             else if ('w' == *opt->value) thread_provider = thread_win32;
             else {Help(); return 2; }
             break;
         case 'c':  /* number of client threads */
             clients = atoi(opt->value);
             break;
         case 'w':  /* minimum server worker threads */
             workersMin = atoi(opt->value);
@@ -1369,16 +1420,18 @@ PRIntn main(PRIntn argc, char** argv)
     TEST_LOG(
         cltsrv_log_file, TEST_LOG_ALWAYS, 
         ("main(0x%p): test complete\n", PR_CurrentThread()));
 
 	if (thread_provider == thread_win32)
 		thread_type = "\nWin32 Thread Statistics\n";
 	else if (thread_provider == thread_pthread)
 		thread_type = "\npthread Statistics\n";
+	else if (thread_provider == thread_uithread)
+		thread_type = "\nUnix International (UI) Thread Statistics\n";
 	else if (thread_provider == thread_sproc)
 		thread_type = "\nsproc Statistics\n";
     else {
 		PR_ASSERT(thread_provider == thread_nspr);
 		thread_type = "\nPRThread Statistics\nn";
 	}
 
     PT_FPrintStats(debug_out, thread_type);