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 ); }