Some time ago, I wrote code to support native threads instead of lwp for SYSV 
operating systems, the way I wrote the code all available threading models 
were compiled into wine and the actual one to be used was selected by setting 
an environment variable. (Currently WINE_THREADMODE). On OSes that support 
multiple threading methods, IE Solaris and probably the BSDs this allows for 
easy development and testing of thread support. It also allows the simulation 
of no thread support (Not that that seems to be very useful).

Originally when I wrote this Solaris 9 was having some problems with crashing 
in mutexes if native threads were used, but LWPs weren't perfect either, in 
particular lwps aren't necessarily safe to use with some libraries  (There is 
a warning about this in the Man pages for the _lwp calls, For example. this 
caused problems with alarm/usleep calls used at the time in winmm). These 
days however in Solaris 10 the native thread code works quite well.

Anyway the fallout was that non winmm code worked best using native threads on 
certain Solaris MUs  and winmm dependent programs worked best using LWPs and 
having the Threading model selectable made sense. This is still the case for 
Solaris 8/9 users.

I can still see situations where it would be advantageous to be able to select 
the threading model at runtime and I'd like to leave this support in (Solaris 
9 for example). This possibility has received a luke warm reception before 
but I thought I'd re-raise the possibility now while I'm doing the putback 
from my latest patch kit.

Just as a final point Solaris no longer supports _lwp_create calls, without 
this patch wine is unable to run on Solaris versions > Solaris 9


Bob

Threading patch attached
Index: loader/kthread.c
===================================================================
RCS file: /home/wine/wine/loader/kthread.c,v
retrieving revision 1.11
diff -u -3 -p -r1.11 kthread.c
--- loader/kthread.c	2 Dec 2004 18:19:25 -0000	1.11
+++ loader/kthread.c	6 May 2005 21:22:11 -0000
@@ -68,6 +68,16 @@ struct _pthread_cleanup_buffer;
 #include <sched.h>
 #endif
 
+#ifdef HAVE_THREAD_H
+
+/* Fixme: Wine creates a namespace problem since it has it's own thread.h,
++for the time being explicitly load the system header"
+*/
+
+#include <errno.h>
+#include </usr/include/thread.h>
+#endif
+
 #include "wine/library.h"
 #include "wine/pthread.h"
 
@@ -83,6 +93,31 @@ static struct wine_pthread_functions fun
 #define MAX_KEYS 16 /* libc6 doesn't use that many, but... */
 #define MAX_TSD  16
 
+#define THREADS_SYSV 1
+#define THREADS_LWP 2
+#define THREADS_PTHREAD 3
+#define THREADS_CLONE 4
+#define THREADS_RFORK 5
+#define THREADS_NONE 6
+
+#if defined (HAVE_NPTL) || defined (HAVE_PTHREAD)
+#define THREADS_DEFAULT THREADS_PTHREAD
+#elif defined( HAVE_CLONE)
+#define THREADS_DEFAULT THREADS_CLONE
+#elif defined (HAVE_RFORK)
+#define THREADS_DEFAULT THREADS_RFORK
+#elif defined (HAVE_THR)
+#define THREADS_DEFAULT THREADS_SYSV
+#elif defined (HAVE__LWP_CREATE)
+#define THREADS_DEFAULT THREADS_LWP
+#else
+#define THREADS_DEFAULT THREADS_NONE
+#endif
+
+#define TRACE printf
+#define ERR printf
+
+int threadmode =0;
 struct pthread_descr_struct
 {
     char               dummy[2048];
@@ -123,7 +158,7 @@ int *__errno_location(void)             
 }
 int *__error(void)     { return __errno_location(); }  /* FreeBSD */
 int *__errno(void)     { return __errno_location(); }  /* NetBSD */
-int *___errno(void)    { return __errno_location(); }  /* Solaris */
+//int *___errno(void)    { return __errno_location(); }  /* Solaris */
 int *__thr_errno(void) { return __errno_location(); }  /* UnixWare */
 
 /***********************************************************************
@@ -179,6 +214,44 @@ inline static char *get_temp_stack(void)
     return temp_stacks[next % NB_TEMP_STACKS] + TEMP_STACK_SIZE;
 }
 
+#ifdef HAVE_THR
+/*
+ * THR library specific, store thread specific data
+*/
+#define THREAD_GET_DATA 0
+#define THREAD_SET_DATA 1
+void * solaris_thread_data(int op, void *data)
+{
+       static mutex_t keylock; /* static ensures only one copy of keylock */
+       static thread_key_t key;
+       static int once_per_keyname = 0;
+       void *tsd = NULL;
+
+       if (!once_per_keyname) {
+            mutex_lock(&keylock);
+            if (!once_per_keyname)  {
+                  thr_keycreate(&key, free);
+                  once_per_keyname++;
+       }
+            mutex_unlock(&keylock);
+       }
+       
+       thr_getspecific(key, &tsd);
+       if(op==THREAD_GET_DATA) return(tsd);
+       if (tsd == NULL) {
+            thr_setspecific(key, data);
+              }
+	      else
+	      ERR("Thread private data already set !\n");
+        thr_getspecific(key, &tsd);
+	return((void *) tsd);
+}  
+     /* end thread_specific_data */
+#endif
+ 
+     
+
+
 
 /***********************************************************************
  *           cleanup_thread
@@ -192,9 +265,18 @@ static void cleanup_thread( void *ptr )
     wine_ldt_free_fs( info.teb_sel );
     munmap( info.stack_base, info.stack_size );
     munmap( info.teb_base, info.teb_size );
-#ifdef HAVE__LWP_CREATE
-    _lwp_exit();
+#ifdef HAVE_THR
+    if (threadmode==THREADS_SYSV) {
+       thr_exit((void * ) &info.exit_status);
+    }
 #endif
+#ifdef  HAVE__LWP_CREATE
+    if(threadmode==THREADS_LWP)
+    {
+     _lwp_exit();
+    }
+#endif
+if((threadmode!=THREADS_SYSV) && (threadmode!=THREADS_LWP))
     _exit( info.exit_status );
 }
 
@@ -235,7 +317,9 @@ void wine_pthread_init_thread( struct wi
     }
     descr->cancel_state = PTHREAD_CANCEL_ENABLE;
     descr->cancel_type  = PTHREAD_CANCEL_ASYNCHRONOUS;
+#ifndef sun
     if (libc_uselocale) libc_uselocale( -1 /*LC_GLOBAL_LOCALE*/ );
+#endif
 }
 
 
@@ -244,6 +328,48 @@ void wine_pthread_init_thread( struct wi
  */
 int wine_pthread_create_thread( struct wine_pthread_thread_info *info )
 {
+    
+    /*   Runtime Threadmode support, initially set shared threadmode
+     *  
+     *  Implementation note, 
+      * we determine the threadmode from the environment variable WINE_THREADMODE
+     */
+    char *env;
+
+ TRACE("Starting New Thread stack base = %lx size =%d\n",info->stack_base, (char *)info->stack_size);
+ if(!threadmode)
+    {
+     	env=getenv("WINE_THREADMODE");
+	threadmode=THREADS_DEFAULT;
+
+     	if(env)
+	{
+#ifdef HAVE_THR
+     		if(!strcasecmp(env,"SYSV") ) threadmode=THREADS_SYSV;
+#endif
+#ifdef HAVE__LWP_CREATE
+     		if(!strcasecmp(env,"LWP") ) threadmode=THREADS_LWP;
+#endif
+     		if(!strcasecmp(env,"PTHREAD"))  threadmode=THREADS_PTHREAD;
+#ifdef HAVE_RFORK
+     		if(!strcasecmp(env,"RFORK") ) threadmode=THREADS_RFORK;
+#endif
+#ifdef HAVE_CLONE
+     		if(!strcasecmp(env,"CLONE") ) threadmode=THREADS_CLONE;
+#endif
+      		if(!strcasecmp(env,"NONE") ) threadmode=THREADS_NONE;
+
+TRACE("Setting Threading mode to %d (%s)\n",threadmode,env);
+     	}//env
+  } //! threaadmode
+
+
+if (threadmode == THREADS_NONE)
+{   	TRACE("Threads disabled - Returning an error");
+	return -1;
+ }
+    
+
     if (!info->stack_base)
     {
         info->stack_base = wine_anon_mmap( NULL, info->stack_size,
@@ -251,11 +377,17 @@ int wine_pthread_create_thread( struct w
         if (info->stack_base == (void *)-1) return -1;
     }
 #ifdef HAVE_CLONE
-    if (clone( (int (*)(void *))info->entry, (char *)info->stack_base + info->stack_size,
+    if(threadmode==THREADS_CLONE)
+    {
+      if (clone( (int (*)(void *))info->entry, (char *)info->stack_base + info->stack_size,
                CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, info ) < 0)
         return -1;
     return 0;
-#elif defined(HAVE_RFORK)
+    }
+#endif
+
+#ifdef HAVE_RFORK
+    if(threadmode==THREADS_RFORK)
     {
         void **sp = (void **)((char *)info->stack_base + info->stack_size);
         *--sp = info;
@@ -276,15 +408,78 @@ int wine_pthread_create_thread( struct w
             : "eax", "edx");
         return 0;
     }
-#elif defined(HAVE__LWP_CREATE)
-    {
+
+#endif
+#ifdef HAVE_THR
+ 	if(threadmode==THREADS_SYSV)
+	{
+ 		thread_t tid;
+		int err;
+
+		TRACE("Starting New Thread Via thr_create\n");
+ 	err=thr_create(info->stack_base, ( int )info->stack_size,  (void * (*) (void*)) info->entry, info,THR_BOUND|THR_DETACHED,&tid) ;
+ 		if(err)
+ 		{
+   			switch(err)
+   			{
+      			case EAGAIN:
+        			ERR("Thread creation failed EAGAIN - System is out of threads or lwps\n");
+        			break;
+      			case EINVAL:
+        			ERR("Thread creation failed EINVAL - The stack_base argument is not NULL and stack_size  is  less  than the value returned by thr_min_stack,  or the stack_base argument is NULL and  stack_size  is   not   0  and  is  less  than  the  value  returned  by          thr_min_stack\n");
+        			break;
+      			case ENOMEM:
+        			ERR("Thread creation failed ENOMEM - here is not enough memory to allocate the stack for the thread\n");
+        			break;
+       			case -1:
+        			ERR("Thread creation failed (-1) - Application is not linked with threading libraryn");
+        			break;
+      			default:
+        			ERR("Thread creation failed Unknown %d - Possible mmap failure check mmap results\n",err);
+        			break;
+   			}
+    			return -1;
+ 		}
+		else
+    			TRACE("Started New Thread Via thr_create\n");
+    		return 0;
+	} /* threadmode*/
+#endif
+
+#ifdef HAVE__LWP_CREATE
+  if(threadmode==THREADS_LWP)
+  {
+      int err;
+
         ucontext_t context;
         _lwp_makecontext( &context, (void(*)(void *))info->entry, info,
                           NULL, info->stack_base, info->stack_size );
-        if ( _lwp_create( &context, 0, NULL ) )
-            return -1;
-        return 0;
-    }
+        if ( (err=_lwp_create( &context, 0, NULL )) )
+	{
+
+  		switch (err)
+		{
+      		case EFAULT:
+        		ERR("lwp_create: Either the context parameter or the new_lwp  parameter  point to invalid addresses.");
+        		break;
+      		case EAGAIN:
+        		ERR("lwp_create: Resources depleted, probably too many lwps");
+        		break;
+      		case EINVAL:
+        		ERR("lwp_create: FLAGS argument invalid\n");
+        		break;
+       		case -1:
+        		ERR("Thread creation failed\n");
+        		break;
+      		default:
+        		ERR("Thread creation failed Unknown %d \n",err);
+        		break;
+   		}
+           return -1;
+        } /* if err*/
+	return 0;
+    } /* Threadmode = THREADS_LWP */
+
 #endif
     return -1;
 }
@@ -305,9 +500,19 @@ void wine_pthread_init_current_teb( stru
     wine_ldt_set_limit( &fs_entry, info->teb_size - 1 );
     wine_ldt_set_flags( &fs_entry, WINE_LDT_FLAGS_DATA|WINE_LDT_FLAGS_32BIT );
     wine_ldt_init_fs( info->teb_sel, &fs_entry );
-#elif defined(HAVE__LWP_CREATE)
-    /* On non-i386 Solaris, we use the LWP private pointer */
-    _lwp_setprivate( info->teb_base );
+
+#elif defined(HAVE__LWP_CREATE) || defined (HAVE_THR)
+
+#ifdef HAVE__LWP_CREATE
+   /* On non-i386 Solaris, we use the LWP private pointer */
+ if(threadmode==THREADS_LWP)    ret = _lwp_setprivate(info->teb_base);
+#endif
+
+#ifdef HAVE_THR
+   /* On non-i386 Solaris, we can also use the threads private pointer */
+   if(threadmode==THREADS_SYSV) ret=solaris_thread_data(THREAD_SET_DATA,info->teb_base);
+#endif
+
 #elif defined(__powerpc__)
     /* On PowerPC, the current TEB is in the gpr13 register */
 # ifdef __APPLE__
@@ -324,8 +529,21 @@ void wine_pthread_init_current_teb( stru
 
     /* set pid and tid */
     info->pid = getpid();
-#ifdef HAVE__LWP_SELF
-    info->tid = _lwp_self();
+
+
+
+#if defined ( HAVE__LWP_SELF ) || defined (HAVE_THR)
+ 	info->tid = -1;
+#if defined ( HAVE__LWP_SELF )
+	if(threadmode==THREADS_LWP)   info->tid = _lwp_self();
+#endif
+
+#if defined (HAVE_THR)
+	if(threadmode==THREADS_SYSV)  info->tid=thr_self();
+#endif
+    
+
+
 #else
     info->tid = -1;
 #endif
@@ -341,8 +559,18 @@ void *wine_pthread_get_current_teb(void)
 
 #ifdef __i386__
     __asm__( ".byte 0x64\n\tmovl 0x18,%0" : "=r" (ret) );
-#elif defined(HAVE__LWP_CREATE)
-    ret = _lwp_getprivate();
+#elif defined(HAVE__LWP_CREATE) || defined (HAVE_THR)
+
+#ifdef HAVE__LWP_CREATE
+if(threadmode==THREADS_LWP)    ret = _lwp_getprivate();
+#endif
+
+#ifdef HAVE_THR
+ if(threadmode==THREADS_SYSV) ret=solaris_thread_data(THREAD_GET_DATA,NULL);
+#endif
+   
+    
+    
 #elif defined(__powerpc__)
 # ifdef __APPLE__
     __asm__( "mr %0,r13" : "=r" (ret) );
@@ -379,9 +607,20 @@ void wine_pthread_exit_thread( struct wi
  */
 void wine_pthread_abort_thread( int status )
 {
-#ifdef HAVE__LWP_CREATE
-    _lwp_exit();
+
+#ifdef HAVE_THR
+if (threadmode==THREADS_SYSV) {
+    thr_exit((void * ) &status);
+}
+#endif
+#ifdef  HAVE__LWP_CREATE
+  if(threadmode==THREADS_LWP)
+  {
+     _lwp_exit();
+ }
 #endif
+
+
     _exit( status );
 }
 

Reply via email to