From 80e34dbb663a874efd11baaab826fca5bd7a0c31 Mon Sep 17 00:00:00 2001 From: Alexandre Julliard Date: Wed, 3 Sep 2003 00:26:08 +0000 Subject: [PATCH] Moved the implementation of the pthread wrappers to dlls/kernel. --- configure | 130 ++++++++- configure.ac | 4 + dlls/kernel/Makefile.in | 1 + dlls/kernel/pthread.c | 355 +++++++++++++++++++++++++ dlls/ntdll/sysdeps.c | 8 +- include/config.h.in | 6 + include/wine/pthread.h | 64 +++++ libs/wine/loader.c | 2 + scheduler/process.c | 6 - scheduler/pthread.c | 567 +++++++++++++++------------------------- 10 files changed, 767 insertions(+), 376 deletions(-) create mode 100644 dlls/kernel/pthread.c create mode 100644 include/wine/pthread.h diff --git a/configure b/configure index b328bd41487..f67b913ce9d 100755 --- a/configure +++ b/configure @@ -1128,7 +1128,7 @@ _ASBOX echo "$as_me: caught signal $ac_signal" echo "$as_me: exit $exit_status" } >&5 - rm -f core core.* *.core && + rm -f core *.core && rm -rf conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 @@ -6080,7 +6080,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_header_stdc=no fi -rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi fi @@ -9962,6 +9962,122 @@ _ACEOF fi +echo "$as_me:$LINENO: checking for pthread_rwlock_t" >&5 +echo $ECHO_N "checking for pthread_rwlock_t... $ECHO_C" >&6 +if test "${ac_cv_type_pthread_rwlock_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _GNU_SOURCE +#include + +int +main () +{ +if ((pthread_rwlock_t *) 0) + return 0; +if (sizeof (pthread_rwlock_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pthread_rwlock_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pthread_rwlock_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pthread_rwlock_t" >&5 +echo "${ECHO_T}$ac_cv_type_pthread_rwlock_t" >&6 +if test $ac_cv_type_pthread_rwlock_t = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_PTHREAD_RWLOCK_T 1 +_ACEOF + + +fi +echo "$as_me:$LINENO: checking for pthread_rwlockattr_t" >&5 +echo $ECHO_N "checking for pthread_rwlockattr_t... $ECHO_C" >&6 +if test "${ac_cv_type_pthread_rwlockattr_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +#line $LINENO "configure" +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#define _GNU_SOURCE +#include + +int +main () +{ +if ((pthread_rwlockattr_t *) 0) + return 0; +if (sizeof (pthread_rwlockattr_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pthread_rwlockattr_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pthread_rwlockattr_t=no +fi +rm -f conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pthread_rwlockattr_t" >&5 +echo "${ECHO_T}$ac_cv_type_pthread_rwlockattr_t" >&6 +if test $ac_cv_type_pthread_rwlockattr_t = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE_PTHREAD_RWLOCKATTR_T 1 +_ACEOF + + +fi + + @@ -10841,7 +10957,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_mmap64_works="no" fi -rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_mmap64_works" >&5 @@ -10911,7 +11027,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_c_gcc_strength_bug="yes" fi -rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_c_gcc_strength_bug" >&5 @@ -12135,7 +12251,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_ld_reloc_exec="no" fi -rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi CFLAGS="$saved_CFLAGS" fi @@ -13437,7 +13553,7 @@ sed 's/^/| /' conftest.$ac_ext >&5 ( exit $ac_status ) ac_cv_c_stack_direction=-1 fi -rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5 @@ -15177,7 +15293,7 @@ echo "$as_me: error: cannot compute sizeof (long long), 77 See \`config.log' for more details." >&2;} { (exit 1); exit 1; }; } fi -rm -f core core.* *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext fi fi rm -f conftest.val diff --git a/configure.ac b/configure.ac index 7fee56e7d40..05a06df6d21 100644 --- a/configure.ac +++ b/configure.ac @@ -520,6 +520,10 @@ then AC_DEFINE(HAVE___VA_COPY, 1, [Define if we have __va_copy]) fi +dnl **** Check for pthread_rwlock_t **** +AC_CHECK_TYPES([pthread_rwlock_t, pthread_rwlockattr_t],,,[#define _GNU_SOURCE +#include ]) + dnl **** Check for Open Sound System **** AC_CHECK_HEADERS(sys/soundcard.h machine/soundcard.h soundcard.h, break) diff --git a/dlls/kernel/Makefile.in b/dlls/kernel/Makefile.in index 7abb1698524..291ca19d46d 100644 --- a/dlls/kernel/Makefile.in +++ b/dlls/kernel/Makefile.in @@ -39,6 +39,7 @@ C_SRCS = \ powermgnt.c \ process.c \ profile.c \ + pthread.c \ relay16.c \ resource.c \ resource16.c \ diff --git a/dlls/kernel/pthread.c b/dlls/kernel/pthread.c new file mode 100644 index 00000000000..0e8b2aa55c7 --- /dev/null +++ b/dlls/kernel/pthread.c @@ -0,0 +1,355 @@ +/* + * pthread emulation for re-entrant libcs + * + * Copyright 1999 Ove Kåven + * Copyright 2003 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" +#include "wine/port.h" + +#define _GNU_SOURCE /* we may need to override some GNU extensions */ + +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +# include +#endif +#include +#include +#if HAVE_SYS_SOCKET_H +# include +#endif +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#include "winbase.h" +#include "thread.h" +#include "winternl.h" +#include "wine/pthread.h" + +#define P_OUTPUT(stuff) write(2,stuff,strlen(stuff)) + +static const struct wine_pthread_functions functions; + +DECL_GLOBAL_CONSTRUCTOR(pthread_init) { wine_pthread_init_process( &functions ); } + +static inline int init_done(void) { return GetProcessHeap() != 0; } + +/* NOTE: This is a truly extremely incredibly ugly hack! + * But it does seem to work... */ + +/* assume that pthread_mutex_t has room for at least one pointer, + * and hope that the users of pthread_mutex_t considers it opaque + * (never checks what's in it) + * also: assume that static initializer sets pointer to NULL + */ +typedef struct +{ +#ifdef __GLIBC__ + int reserved; +#endif + CRITICAL_SECTION *critsect; +} *wine_mutex; + +/* see wine_mutex above for comments */ +typedef struct { + RTL_RWLOCK *lock; +} *wine_rwlock; + +struct pthread_thread_init +{ + void* (*start_routine)(void*); + void* arg; +}; + +static DWORD CALLBACK pthread_thread_start(LPVOID data) +{ + struct pthread_thread_init init = *(struct pthread_thread_init*)data; + HeapFree(GetProcessHeap(),0,data); + return (DWORD)init.start_routine(init.arg); +} + +static int wine_pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* + (*start_routine)(void *), void* arg) +{ + HANDLE hThread; + struct pthread_thread_init* idata = HeapAlloc(GetProcessHeap(), 0, sizeof(struct pthread_thread_init)); + + idata->start_routine = start_routine; + idata->arg = arg; + hThread = CreateThread( NULL, 0, pthread_thread_start, idata, 0, (LPDWORD)thread); + + if(hThread) + CloseHandle(hThread); + else + { + HeapFree(GetProcessHeap(),0,idata); /* free idata struct on failure */ + return EAGAIN; + } + + return 0; +} + +static int wine_pthread_cancel(pthread_t thread) +{ + HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)thread); + + if(!TerminateThread(hThread, 0)) + { + CloseHandle(hThread); + return EINVAL; /* return error */ + } + + CloseHandle(hThread); + + return 0; /* return success */ +} + +static int wine_pthread_join(pthread_t thread, void **value_ptr) +{ + HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)thread); + + WaitForSingleObject(hThread, INFINITE); + if(!GetExitCodeThread(hThread, (LPDWORD)value_ptr)) + { + CloseHandle(hThread); + return EINVAL; /* FIXME: make this more correctly match */ + } /* windows errors */ + + CloseHandle(hThread); + return 0; +} + +/*FIXME: not sure what to do with this one... */ +static int wine_pthread_detach(pthread_t thread) +{ + P_OUTPUT("FIXME:pthread_detach\n"); + return 0; +} + +/***** MUTEXES *****/ + +static int wine_pthread_mutex_init(pthread_mutex_t *mutex, + const pthread_mutexattr_t *mutexattr) +{ + /* glibc has a tendency to initialize mutexes very often, even + in situations where they are not really used later on. + + As for us, initializing a mutex is very expensive, we postpone + the real initialization until the time the mutex is first used. */ + + ((wine_mutex)mutex)->critsect = NULL; + return 0; +} + +static void mutex_real_init( pthread_mutex_t *mutex ) +{ + CRITICAL_SECTION *critsect = HeapAlloc(GetProcessHeap(), 0, sizeof(CRITICAL_SECTION)); + RtlInitializeCriticalSection(critsect); + + if (InterlockedCompareExchangePointer((void**)&(((wine_mutex)mutex)->critsect),critsect,NULL) != NULL) { + /* too late, some other thread already did it */ + RtlDeleteCriticalSection(critsect); + HeapFree(GetProcessHeap(), 0, critsect); + } +} + +static int wine_pthread_mutex_lock(pthread_mutex_t *mutex) +{ + if (!init_done()) return 0; + if (!((wine_mutex)mutex)->critsect) + mutex_real_init( mutex ); + + RtlEnterCriticalSection(((wine_mutex)mutex)->critsect); + return 0; +} + +static int wine_pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + if (!init_done()) return 0; + if (!((wine_mutex)mutex)->critsect) + mutex_real_init( mutex ); + + if (!RtlTryEnterCriticalSection(((wine_mutex)mutex)->critsect)) { + errno = EBUSY; + return -1; + } + return 0; +} + +static int wine_pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + if (!((wine_mutex)mutex)->critsect) return 0; + RtlLeaveCriticalSection(((wine_mutex)mutex)->critsect); + return 0; +} + +static int wine_pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + if (!((wine_mutex)mutex)->critsect) return 0; + if (((wine_mutex)mutex)->critsect->RecursionCount) { +#if 0 /* there seems to be a bug in libc6 that makes this a bad idea */ + return EBUSY; +#else + while (((wine_mutex)mutex)->critsect->RecursionCount) + RtlLeaveCriticalSection(((wine_mutex)mutex)->critsect); +#endif + } + RtlDeleteCriticalSection(((wine_mutex)mutex)->critsect); + HeapFree(GetProcessHeap(), 0, ((wine_mutex)mutex)->critsect); + ((wine_mutex)mutex)->critsect = NULL; + return 0; +} + +/***** READ-WRITE LOCKS *****/ + +static void rwlock_real_init(pthread_rwlock_t *rwlock) +{ + RTL_RWLOCK *lock = HeapAlloc(GetProcessHeap(), 0, sizeof(RTL_RWLOCK)); + RtlInitializeResource(lock); + + if (InterlockedCompareExchangePointer((void**)&(((wine_rwlock)rwlock)->lock),lock,NULL) != NULL) { + /* too late, some other thread already did it */ + RtlDeleteResource(lock); + HeapFree(GetProcessHeap(), 0, lock); + } +} + +static int wine_pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *rwlock_attr) +{ + ((wine_rwlock)rwlock)->lock = NULL; + return 0; +} + +static int wine_pthread_rwlock_destroy(pthread_rwlock_t *rwlock) +{ + if (!((wine_rwlock)rwlock)->lock) return 0; + RtlDeleteResource(((wine_rwlock)rwlock)->lock); + HeapFree(GetProcessHeap(), 0, ((wine_rwlock)rwlock)->lock); + return 0; +} + +static int wine_pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) +{ + if (!init_done()) return 0; + if (!((wine_rwlock)rwlock)->lock) + rwlock_real_init( rwlock ); + + while(TRUE) + if (RtlAcquireResourceShared(((wine_rwlock)rwlock)->lock, TRUE)) + return 0; +} + +static int wine_pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) +{ + if (!init_done()) return 0; + if (!((wine_rwlock)rwlock)->lock) + rwlock_real_init( rwlock ); + + if (!RtlAcquireResourceShared(((wine_rwlock)rwlock)->lock, FALSE)) { + errno = EBUSY; + return -1; + } + return 0; +} + +static int wine_pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) +{ + if (!init_done()) return 0; + if (!((wine_rwlock)rwlock)->lock) + rwlock_real_init( rwlock ); + + while(TRUE) + if (RtlAcquireResourceExclusive(((wine_rwlock)rwlock)->lock, TRUE)) + return 0; +} + +static int wine_pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) +{ + if (!init_done()) return 0; + if (!((wine_rwlock)rwlock)->lock) + rwlock_real_init( rwlock ); + + if (!RtlAcquireResourceExclusive(((wine_rwlock)rwlock)->lock, FALSE)) { + errno = EBUSY; + return -1; + } + return 0; +} + +static int wine_pthread_rwlock_unlock(pthread_rwlock_t *rwlock) +{ + if (!((wine_rwlock)rwlock)->lock) return 0; + RtlReleaseResource( ((wine_rwlock)rwlock)->lock ); + return 0; +} + +/***** MISC *****/ + +static pthread_t wine_pthread_self(void) +{ + return (pthread_t)GetCurrentThreadId(); +} + +static int wine_pthread_equal(pthread_t thread1, pthread_t thread2) +{ + return (DWORD)thread1 == (DWORD)thread2; +} + +static void wine_pthread_exit(void *retval, char *currentframe) +{ + ExitThread((DWORD)retval); +} + +static void *wine_get_thread_data(void) +{ + return NtCurrentTeb()->pthread_data; +} + +static void wine_set_thread_data( void *data ) +{ + NtCurrentTeb()->pthread_data = data; +} + +static const struct wine_pthread_functions functions = +{ + wine_get_thread_data, /* ptr_get_thread_data */ + wine_set_thread_data, /* ptr_set_thread_data */ + wine_pthread_self, /* ptr_pthread_self */ + wine_pthread_equal, /* ptr_pthread_equal */ + wine_pthread_create, /* ptr_pthread_create */ + wine_pthread_cancel, /* ptr_pthread_cancel */ + wine_pthread_join, /* ptr_pthread_join */ + wine_pthread_detach, /* ptr_pthread_detach */ + wine_pthread_exit, /* ptr_pthread_exit */ + wine_pthread_mutex_init, /* ptr_pthread_mutex_init */ + wine_pthread_mutex_lock, /* ptr_pthread_mutex_lock */ + wine_pthread_mutex_trylock, /* ptr_pthread_mutex_trylock */ + wine_pthread_mutex_unlock, /* ptr_pthread_mutex_unlock */ + wine_pthread_mutex_destroy, /* ptr_pthread_mutex_destroy */ + wine_pthread_rwlock_init, /* ptr_pthread_rwlock_init */ + wine_pthread_rwlock_destroy, /* ptr_pthread_rwlock_destroy */ + wine_pthread_rwlock_rdlock, /* ptr_pthread_rwlock_rdlock */ + wine_pthread_rwlock_tryrdlock, /* ptr_pthread_rwlock_tryrdlock */ + wine_pthread_rwlock_wrlock, /* ptr_pthread_rwlock_wrlock */ + wine_pthread_rwlock_trywrlock, /* ptr_pthread_rwlock_trywrlock */ + wine_pthread_rwlock_unlock /* ptr_pthread_rwlock_unlock */ +}; diff --git a/dlls/ntdll/sysdeps.c b/dlls/ntdll/sysdeps.c index 3c1971507c3..d0ed6dab1cd 100644 --- a/dlls/ntdll/sysdeps.c +++ b/dlls/ntdll/sysdeps.c @@ -45,11 +45,8 @@ #include #endif -#ifdef HAVE_NPTL -#include -#endif - #include "thread.h" +#include "wine/pthread.h" #include "wine/server.h" #include "winbase.h" #include "wine/library.h" @@ -70,7 +67,6 @@ struct thread_cleanup_info static char temp_stacks[NB_TEMP_STACKS][TEMP_STACK_SIZE]; static LONG next_temp_stack; /* next temp stack to use */ -extern void PTHREAD_init_thread(void); /*********************************************************************** * SYSDEPS_SetCurThread @@ -102,7 +98,7 @@ void SYSDEPS_SetCurThread( TEB *teb ) #ifdef HAVE_NPTL teb->pthread_data = (void *)pthread_self(); #else - PTHREAD_init_thread(); + wine_pthread_init_thread(); #endif } diff --git a/include/config.h.in b/include/config.h.in index 3fb7b2de0e7..e655c14bd92 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -395,6 +395,12 @@ /* Define to 1 if you have the `pread' function. */ #undef HAVE_PREAD +/* Define to 1 if the system has the type `pthread_rwlockattr_t'. */ +#undef HAVE_PTHREAD_RWLOCKATTR_T + +/* Define to 1 if the system has the type `pthread_rwlock_t'. */ +#undef HAVE_PTHREAD_RWLOCK_T + /* Define to 1 if you have the header file. */ #undef HAVE_PTY_H diff --git a/include/wine/pthread.h b/include/wine/pthread.h new file mode 100644 index 00000000000..1f7ad1fdcd7 --- /dev/null +++ b/include/wine/pthread.h @@ -0,0 +1,64 @@ +/* + * Definitions for Wine pthread emulation + * + * Copyright 2003 Alexandre Julliard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __WINE_WINE_PTHREAD_H +#define __WINE_WINE_PTHREAD_H + +#define _GNU_SOURCE +#include + +#ifndef HAVE_PTHREAD_RWLOCK_T +typedef void *pthread_rwlock_t; +#endif +#ifndef HAVE_PTHREAD_RWLOCKATTR_T +typedef void *pthread_rwlockattr_t; +#endif + +struct wine_pthread_functions +{ + void * (*ptr_get_thread_data)(void); + void (*ptr_set_thread_data)(void *data); + pthread_t (*ptr_pthread_self)(void); + int (*ptr_pthread_equal)(pthread_t thread1, pthread_t thread2); + int (*ptr_pthread_create)(pthread_t* thread, const pthread_attr_t* attr, + void* (*start_routine)(void *), void* arg); + int (*ptr_pthread_cancel)(pthread_t thread); + int (*ptr_pthread_join)(pthread_t thread, void **value_ptr); + int (*ptr_pthread_detach)(pthread_t thread); + void (*ptr_pthread_exit)(void *retval, char *currentframe); + int (*ptr_pthread_mutex_init)(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); + int (*ptr_pthread_mutex_lock)(pthread_mutex_t *mutex); + int (*ptr_pthread_mutex_trylock)(pthread_mutex_t *mutex); + int (*ptr_pthread_mutex_unlock)(pthread_mutex_t *mutex); + int (*ptr_pthread_mutex_destroy)(pthread_mutex_t *mutex); + int (*ptr_pthread_rwlock_init)(pthread_rwlock_t *rwlock, + const pthread_rwlockattr_t *rwlock_attr); + int (*ptr_pthread_rwlock_destroy)(pthread_rwlock_t *rwlock); + int (*ptr_pthread_rwlock_rdlock)(pthread_rwlock_t *rwlock); + int (*ptr_pthread_rwlock_tryrdlock)(pthread_rwlock_t *rwlock); + int (*ptr_pthread_rwlock_wrlock)(pthread_rwlock_t *rwlock); + int (*ptr_pthread_rwlock_trywrlock)(pthread_rwlock_t *rwlock); + int (*ptr_pthread_rwlock_unlock)(pthread_rwlock_t *rwlock); +}; + +extern void wine_pthread_init_process( const struct wine_pthread_functions *functions ); +extern void wine_pthread_init_thread(void); + +#endif /* __WINE_WINE_PTHREAD_H */ diff --git a/libs/wine/loader.c b/libs/wine/loader.c index 93a1beb28f3..14220321117 100644 --- a/libs/wine/loader.c +++ b/libs/wine/loader.c @@ -420,6 +420,8 @@ void wine_init( int argc, char *argv[], char *error, int error_size ) void (*init_func)(int, char **); if (!(ntdll = dlopen_dll( "ntdll.dll", error, error_size, 0, &file_exists ))) return; + /* make sure kernel32 is loaded too */ + if (!dlopen_dll( "kernel32.dll", error, error_size, 0, &file_exists )) return; if (!(init_func = wine_dlsym( ntdll, "__wine_process_init", error, error_size ))) return; init_func( argc, argv ); } diff --git a/scheduler/process.c b/scheduler/process.c index d593f0686de..bec3d7158ec 100644 --- a/scheduler/process.c +++ b/scheduler/process.c @@ -117,9 +117,6 @@ static unsigned int server_startticks; int main_create_flags = 0; -/* scheduler/pthread.c */ -extern void PTHREAD_init_done(void); - /* dlls/ntdll/env.c */ extern BOOL init_user_process_pmts( size_t, char*, size_t ); extern BOOL build_command_line( char **argv ); @@ -359,9 +356,6 @@ static BOOL process_init( char *argv[] ) process_pmts.hStdError = console_handle_map(process_pmts.hStdError); } - /* Now we can use the pthreads routines */ - PTHREAD_init_done(); - /* Copy the parent environment */ if (!init_user_process_pmts( info_size, main_exe_name, sizeof(main_exe_name) )) return FALSE; diff --git a/scheduler/pthread.c b/scheduler/pthread.c index cd2b8eaa2e3..687ba84a420 100644 --- a/scheduler/pthread.c +++ b/scheduler/pthread.c @@ -5,6 +5,7 @@ * that want pthreads use Wine's own threading instead... * * Copyright 1999 Ove Kåven + * Copyright 2003 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,11 +29,10 @@ struct _pthread_cleanup_buffer; -#define _GNU_SOURCE /* we may need to override some GNU extensions */ - #include #include #include +#include #include #ifdef HAVE_UNISTD_H # include @@ -55,49 +55,62 @@ struct _pthread_cleanup_buffer; #include #endif -#include "winbase.h" -#include "thread.h" -#include "winternl.h" +#include "wine/pthread.h" -/* default errno before threading is initialized */ -static int *default_errno_location(void) +#define P_OUTPUT(stuff) write(2,stuff,strlen(stuff)) + +#define PSTR(str) __ASM_NAME(#str) + +static struct wine_pthread_functions funcs; + +/* thread descriptor */ + +#define FIRST_KEY 0 +#define MAX_KEYS 16 /* libc6 doesn't use that many, but... */ +#define MAX_TSD 16 + +struct pthread_descr_struct { - static int static_errno; - return &static_errno; + char dummy[2048]; + int thread_errno; + int thread_h_errno; + int cancel_state; + int cancel_type; + struct __res_state res_state; + const void *key_data[MAX_KEYS]; /* for normal pthread keys */ + const void *tsd_data[MAX_TSD]; /* for libc internal tsd variables */ +}; + +typedef struct pthread_descr_struct *pthread_descr; + +static struct pthread_descr_struct initial_descr; + +pthread_descr __pthread_thread_self(void) +{ + struct pthread_descr_struct *descr; + if (!funcs.ptr_get_thread_data) return &initial_descr; + descr = funcs.ptr_get_thread_data(); + if (!descr) return &initial_descr; + return descr; } -/* default h_errno before threading is initialized */ -static int *default_h_errno_location(void) -{ - static int static_h_errno; - return &static_h_errno; -} - -/* errno once threading is working */ -static int *thread_errno_location(void) -{ - return &NtCurrentTeb()->thread_errno; -} - -/* h_errno once threading is working */ -static int *thread_h_errno_location(void) -{ - return &NtCurrentTeb()->thread_h_errno; -} - -static int* (*errno_location_ptr)(void) = default_errno_location; -static int* (*h_errno_location_ptr)(void) = default_h_errno_location; +static int (*libc_uselocale)(int set); +static int *libc_multiple_threads; /*********************************************************************** * __errno_location/__error/__errno/___errno/__thr_errno * * Get the per-thread errno location. */ -int *__errno_location(void) { return errno_location_ptr(); } /* Linux */ -int *__error(void) { return errno_location_ptr(); } /* FreeBSD */ -int *__errno(void) { return errno_location_ptr(); } /* NetBSD */ -int *___errno(void) { return errno_location_ptr(); } /* Solaris */ -int *__thr_errno(void) { return errno_location_ptr(); } /* UnixWare */ +int *__errno_location(void) /* Linux */ +{ + pthread_descr descr = __pthread_thread_self(); + return &descr->thread_errno; +} +int *__error(void) { return __errno_location(); } /* FreeBSD */ +int *__errno(void) { return __errno_location(); } /* NetBSD */ +int *___errno(void) { return __errno_location(); } /* Solaris */ +int *__thr_errno(void) { return __errno_location(); } /* UnixWare */ /*********************************************************************** * __h_errno_location @@ -106,7 +119,66 @@ int *__thr_errno(void) { return errno_location_ptr(); } /* UnixWare */ */ int *__h_errno_location(void) { - return h_errno_location_ptr(); + pthread_descr descr = __pthread_thread_self(); + return &descr->thread_h_errno; +} + + +static inline void writejump( const char *symbol, void *dest ) +{ +#if defined(__GLIBC__) && defined(__i386__) + unsigned char *addr = dlsym( RTLD_NEXT, symbol ); + + if (!addr) return; + + /* write a relative jump at the function address */ + mprotect((void*)((unsigned int)addr & ~(getpagesize()-1)), 5, PROT_READ|PROT_EXEC|PROT_WRITE); + addr[0] = 0xe9; + *(int *)(addr+1) = (unsigned char *)dest - (addr + 5); + mprotect((void*)((unsigned int)addr & ~(getpagesize()-1)), 5, PROT_READ|PROT_EXEC); + +# ifdef HAVE_VALGRIND_MEMCHECK_H + VALGRIND_DISCARD_TRANSLATIONS( addr, 5 ); +# endif +#endif /* __GLIBC__ && __i386__ */ +} + +/*********************************************************************** + * wine_pthread_init_process + * + * Initialization for a newly created process. + */ +void wine_pthread_init_process( const struct wine_pthread_functions *functions ) +{ + memcpy( &funcs, functions, sizeof(funcs) ); + funcs.ptr_set_thread_data( &initial_descr ); + initial_descr.cancel_state = PTHREAD_CANCEL_ENABLE; + initial_descr.cancel_type = PTHREAD_CANCEL_ASYNCHRONOUS; + writejump( "__errno_location", __errno_location ); + writejump( "__h_errno_location", __h_errno_location ); + writejump( "__res_state", __res_state ); + if (libc_uselocale) libc_uselocale( -1 /*LC_GLOBAL_LOCALE*/ ); +} + + +/*********************************************************************** + * wine_pthread_init_thread + * + * Initialization for a newly created thread. + */ +void wine_pthread_init_thread(void) +{ + if (funcs.ptr_set_thread_data) + { + struct pthread_descr_struct *descr = calloc( 1, sizeof(*descr) ); + + funcs.ptr_set_thread_data( descr ); + descr->cancel_state = PTHREAD_CANCEL_ENABLE; + descr->cancel_type = PTHREAD_CANCEL_ASYNCHRONOUS; + if (libc_multiple_threads) *libc_multiple_threads = 1; + if (libc_uselocale) libc_uselocale( -1 /*LC_GLOBAL_LOCALE*/ ); + } + /* else it's the first thread, init will be done in wine_pthread_init_process */ } @@ -123,45 +195,14 @@ int *__h_errno_location(void) #endif #include -#include - -#define P_OUTPUT(stuff) write(2,stuff,strlen(stuff)) - -#define PSTR(str) __ASM_NAME(#str) /* adapt as necessary (a construct like this is used in glibc sources) */ #define strong_alias(orig, alias) \ asm(".globl " PSTR(alias) "\n" \ "\t.set " PSTR(alias) "," PSTR(orig)) -/* thread descriptor */ - -#define FIRST_KEY 0 -#define MAX_KEYS 16 /* libc6 doesn't use that many, but... */ -#define MAX_TSD 16 - struct fork_block; -struct pthread_descr_struct -{ - char dummy[2048]; - struct __res_state res_state; - const void *key_data[MAX_KEYS]; /* for normal pthread keys */ - const void *tsd_data[MAX_TSD]; /* for libc internal tsd variables */ -}; - -typedef struct pthread_descr_struct *pthread_descr; - -static struct pthread_descr_struct initial_descr; - -pthread_descr __pthread_thread_self(void) -{ - struct pthread_descr_struct *descr = NtCurrentTeb()->pthread_data; - if (!descr) return &initial_descr; - return descr; -} -strong_alias(__pthread_thread_self, pthread_thread_self); - /* pthread functions redirection */ struct pthread_functions @@ -210,19 +251,13 @@ struct pthread_functions int (*ptr_pthread_raise) (int sig); }; -static struct pthread_functions wine_pthread_functions; -static int init_done; - static pid_t (*libc_fork)(void); static int (*libc_sigaction)(int signum, const struct sigaction *act, struct sigaction *oldact); -static int (*libc_uselocale)(int set); static int *(*libc_pthread_init)( const struct pthread_functions *funcs ); -static int *libc_multiple_threads; -void PTHREAD_init_done(void) -{ - init_done = 1; -} +static struct pthread_functions libc_pthread_functions; + +strong_alias(__pthread_thread_self, pthread_thread_self); struct __res_state *__res_state(void) { @@ -230,164 +265,40 @@ struct __res_state *__res_state(void) return &descr->res_state; } -static inline void writejump( const char *symbol, void *dest ) -{ -#if defined(__GLIBC__) && defined(__i386__) - unsigned char *addr = dlsym( RTLD_NEXT, symbol ); - - if (!addr) return; - - /* write a relative jump at the function address */ - mprotect((void*)((unsigned int)addr & ~(getpagesize()-1)), 5, PROT_READ|PROT_EXEC|PROT_WRITE); - addr[0] = 0xe9; - *(int *)(addr+1) = (unsigned char *)dest - (addr + 5); - mprotect((void*)((unsigned int)addr & ~(getpagesize()-1)), 5, PROT_READ|PROT_EXEC); - -# ifdef HAVE_VALGRIND_MEMCHECK_H - VALGRIND_DISCARD_TRANSLATIONS( addr, 5 ); -# endif -#endif /* __GLIBC__ && __i386__ */ -} - -/*********************************************************************** - * PTHREAD_init_thread - * - * Initialization for a newly created thread. - */ -void PTHREAD_init_thread(void) -{ - static int first = 1; - - if (first) - { - first = 0; - NtCurrentTeb()->pthread_data = &initial_descr; - errno_location_ptr = thread_errno_location; - h_errno_location_ptr = thread_h_errno_location; - libc_fork = dlsym( RTLD_NEXT, "fork" ); - libc_sigaction = dlsym( RTLD_NEXT, "sigaction" ); - libc_uselocale = dlsym( RTLD_NEXT, "uselocale" ); - libc_pthread_init = dlsym( RTLD_NEXT, "__libc_pthread_init" ); - if (libc_pthread_init) libc_multiple_threads = libc_pthread_init( &wine_pthread_functions ); - writejump( "__errno_location", thread_errno_location ); - writejump( "__h_errno_location", thread_h_errno_location ); - writejump( "__res_state", __res_state ); - } - else - { - struct pthread_descr_struct *descr = calloc( 1, sizeof(*descr) ); - NtCurrentTeb()->pthread_data = descr; - if (libc_multiple_threads) *libc_multiple_threads = 1; - } - if (libc_uselocale) libc_uselocale( -1 /*LC_GLOBAL_LOCALE*/ ); -} - /* redefine this to prevent libpthread from overriding our function pointers */ int *__libc_pthread_init( const struct pthread_functions *funcs ) { return libc_multiple_threads; } -/* NOTE: This is a truly extremely incredibly ugly hack! - * But it does seem to work... */ - -/* assume that pthread_mutex_t has room for at least one pointer, - * and hope that the users of pthread_mutex_t considers it opaque - * (never checks what's in it) - * also: assume that static initializer sets pointer to NULL - */ -typedef struct -{ -#ifdef __GLIBC__ - int reserved; -#endif - CRITICAL_SECTION *critsect; -} *wine_mutex; - -/* see wine_mutex above for comments */ -typedef struct { - RTL_RWLOCK *lock; -} *wine_rwlock; - typedef struct _wine_cleanup { void (*routine)(void *); void *arg; } *wine_cleanup; -void __pthread_initialize(void) -{ -} - -struct pthread_thread_init { - void* (*start_routine)(void*); - void* arg; -}; - -static DWORD CALLBACK pthread_thread_start(LPVOID data) -{ - struct pthread_thread_init init = *(struct pthread_thread_init*)data; - HeapFree(GetProcessHeap(),0,data); - return (DWORD)init.start_routine(init.arg); -} - int pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start_routine)(void *), void* arg) { - HANDLE hThread; - struct pthread_thread_init* idata = HeapAlloc(GetProcessHeap(), 0, - sizeof(struct pthread_thread_init)); - - idata->start_routine = start_routine; - idata->arg = arg; - hThread = CreateThread( NULL, 0, pthread_thread_start, idata, 0, - (LPDWORD)thread); - - if(hThread) - CloseHandle(hThread); - else - { - HeapFree(GetProcessHeap(),0,idata); /* free idata struct on failure */ - return EAGAIN; - } - - return 0; + assert( funcs.ptr_pthread_create ); + return funcs.ptr_pthread_create( thread, attr, start_routine, arg ); } int pthread_cancel(pthread_t thread) { - HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)thread); - - if(!TerminateThread(hThread, 0)) - { - CloseHandle(hThread); - return EINVAL; /* return error */ - } - - CloseHandle(hThread); - - return 0; /* return success */ + assert( funcs.ptr_pthread_cancel ); + return funcs.ptr_pthread_cancel( thread ); } int pthread_join(pthread_t thread, void **value_ptr) { - HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, (DWORD)thread); - - WaitForSingleObject(hThread, INFINITE); - if(!GetExitCodeThread(hThread, (LPDWORD)value_ptr)) - { - CloseHandle(hThread); - return EINVAL; /* FIXME: make this more correctly match */ - } /* windows errors */ - - CloseHandle(hThread); - return 0; + assert( funcs.ptr_pthread_join ); + return funcs.ptr_pthread_join( thread, value_ptr ); } -/*FIXME: not sure what to do with this one... */ int pthread_detach(pthread_t thread) { - P_OUTPUT("FIXME:pthread_detach\n"); - return 0; + assert( funcs.ptr_pthread_detach ); + return funcs.ptr_pthread_detach( thread ); } /* FIXME: we have no equivalents in win32 for the policys */ @@ -413,15 +324,21 @@ int pthread_attr_setschedparam(pthread_attr_t *attr, return 0; /* return success */ } +/* FIXME */ +int pthread_attr_setstack(pthread_attr_t *attr, void *addr, size_t size) +{ + return 0; /* return success */ +} + int __pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) { - static pthread_once_t the_once = PTHREAD_ONCE_INIT; - LONG once_now; + static pthread_once_t the_once = PTHREAD_ONCE_INIT; + long once_now; - memcpy(&once_now,&the_once,sizeof(once_now)); - if (InterlockedCompareExchange((LONG*)once_control, once_now+1, once_now) == once_now) - (*init_routine)(); - return 0; + memcpy(&once_now,&the_once,sizeof(once_now)); + if (interlocked_cmpxchg((long*)once_control, once_now+1, once_now) == once_now) + (*init_routine)(); + return 0; } strong_alias(__pthread_once, pthread_once); @@ -435,14 +352,7 @@ strong_alias(__pthread_kill_other_threads_np, pthread_kill_other_threads_np); #define MAX_ATFORK 8 /* libc doesn't need that many anyway */ -static CRITICAL_SECTION atfork_section; -static CRITICAL_SECTION_DEBUG critsect_debug = -{ - 0, 0, &atfork_section, - { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList }, - 0, 0, { 0, (DWORD)(__FILE__ ": atfork_section") } -}; -static CRITICAL_SECTION atfork_section = { &critsect_debug, -1, 0, 0, 0, 0 }; +static pthread_mutex_t atfork_mutex = PTHREAD_MUTEX_INITIALIZER; typedef void (*atfork_handler)(); static atfork_handler atfork_prepare[MAX_ATFORK]; @@ -450,17 +360,15 @@ static atfork_handler atfork_parent[MAX_ATFORK]; static atfork_handler atfork_child[MAX_ATFORK]; static int atfork_count; -int __pthread_atfork(void (*prepare)(void), - void (*parent)(void), - void (*child)(void)) +int __pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) { - if (init_done) RtlEnterCriticalSection( &atfork_section ); + pthread_mutex_lock( &atfork_mutex ); assert( atfork_count < MAX_ATFORK ); atfork_prepare[atfork_count] = prepare; atfork_parent[atfork_count] = parent; atfork_child[atfork_count] = child; atfork_count++; - if (init_done) RtlLeaveCriticalSection( &atfork_section ); + pthread_mutex_unlock( &atfork_mutex ); return 0; } strong_alias(__pthread_atfork, pthread_atfork); @@ -475,18 +383,18 @@ pid_t __fork(void) libc_fork = dlsym( RTLD_NEXT, "fork" ); assert( libc_fork ); } - RtlEnterCriticalSection( &atfork_section ); + pthread_mutex_lock( &atfork_mutex ); /* prepare handlers are called in reverse insertion order */ for (i = atfork_count - 1; i >= 0; i--) if (atfork_prepare[i]) atfork_prepare[i](); if (!(pid = libc_fork())) { - RtlInitializeCriticalSection( &atfork_section ); + pthread_mutex_init( &atfork_mutex, NULL ); for (i = 0; i < atfork_count; i++) if (atfork_child[i]) atfork_child[i](); } else { for (i = 0; i < atfork_count; i++) if (atfork_parent[i]) atfork_parent[i](); - RtlLeaveCriticalSection( &atfork_section ); + pthread_mutex_unlock( &atfork_mutex ); } return pid; } @@ -494,80 +402,38 @@ strong_alias(__fork, fork); /***** MUTEXES *****/ -int __pthread_mutex_init(pthread_mutex_t *mutex, - const pthread_mutexattr_t *mutexattr) +int __pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr) { - /* glibc has a tendency to initialize mutexes very often, even - in situations where they are not really used later on. - - As for us, initializing a mutex is very expensive, we postpone - the real initialization until the time the mutex is first used. */ - - ((wine_mutex)mutex)->critsect = NULL; - return 0; + if (!funcs.ptr_pthread_mutex_init) return 0; + return funcs.ptr_pthread_mutex_init( mutex, mutexattr ); } strong_alias(__pthread_mutex_init, pthread_mutex_init); -static void mutex_real_init( pthread_mutex_t *mutex ) -{ - CRITICAL_SECTION *critsect = HeapAlloc(GetProcessHeap(), 0, sizeof(CRITICAL_SECTION)); - RtlInitializeCriticalSection(critsect); - - if (InterlockedCompareExchangePointer((void**)&(((wine_mutex)mutex)->critsect),critsect,NULL) != NULL) { - /* too late, some other thread already did it */ - RtlDeleteCriticalSection(critsect); - HeapFree(GetProcessHeap(), 0, critsect); - } -} - int __pthread_mutex_lock(pthread_mutex_t *mutex) { - if (!init_done) return 0; - if (!((wine_mutex)mutex)->critsect) - mutex_real_init( mutex ); - - RtlEnterCriticalSection(((wine_mutex)mutex)->critsect); - return 0; + if (!funcs.ptr_pthread_mutex_lock) return 0; + return funcs.ptr_pthread_mutex_lock( mutex ); } strong_alias(__pthread_mutex_lock, pthread_mutex_lock); int __pthread_mutex_trylock(pthread_mutex_t *mutex) { - if (!init_done) return 0; - if (!((wine_mutex)mutex)->critsect) - mutex_real_init( mutex ); - - if (!RtlTryEnterCriticalSection(((wine_mutex)mutex)->critsect)) { - errno = EBUSY; - return -1; - } - return 0; + if (!funcs.ptr_pthread_mutex_trylock) return 0; + return funcs.ptr_pthread_mutex_trylock( mutex ); } strong_alias(__pthread_mutex_trylock, pthread_mutex_trylock); int __pthread_mutex_unlock(pthread_mutex_t *mutex) { - if (!((wine_mutex)mutex)->critsect) return 0; - RtlLeaveCriticalSection(((wine_mutex)mutex)->critsect); - return 0; + if (!funcs.ptr_pthread_mutex_unlock) return 0; + return funcs.ptr_pthread_mutex_unlock( mutex ); } strong_alias(__pthread_mutex_unlock, pthread_mutex_unlock); int __pthread_mutex_destroy(pthread_mutex_t *mutex) { - if (!((wine_mutex)mutex)->critsect) return 0; - if (((wine_mutex)mutex)->critsect->RecursionCount) { -#if 0 /* there seems to be a bug in libc6 that makes this a bad idea */ - return EBUSY; -#else - while (((wine_mutex)mutex)->critsect->RecursionCount) - RtlLeaveCriticalSection(((wine_mutex)mutex)->critsect); -#endif - } - RtlDeleteCriticalSection(((wine_mutex)mutex)->critsect); - HeapFree(GetProcessHeap(), 0, ((wine_mutex)mutex)->critsect); - ((wine_mutex)mutex)->critsect = NULL; - return 0; + if (!funcs.ptr_pthread_mutex_destroy) return 0; + return funcs.ptr_pthread_mutex_destroy( mutex ); } strong_alias(__pthread_mutex_destroy, pthread_mutex_destroy); @@ -618,9 +484,9 @@ strong_alias(__pthread_mutexattr_gettype, pthread_mutexattr_gettype); int __pthread_key_create(pthread_key_t *key, void (*destr_function)(void *)) { - static LONG keycnt = FIRST_KEY; - *key = InterlockedExchangeAdd(&keycnt, 1); - return 0; + static long keycnt = FIRST_KEY; + *key = interlocked_xchg_add(&keycnt, 1); + return 0; } strong_alias(__pthread_key_create, pthread_key_create); @@ -645,26 +511,27 @@ void *__pthread_getspecific(pthread_key_t key) } strong_alias(__pthread_getspecific, pthread_getspecific); -/* these are not exported, they are only used in the pthread_functions structure */ - static int pthread_internal_tsd_set( int key, const void *pointer ) { pthread_descr descr = __pthread_thread_self(); descr->tsd_data[key] = pointer; return 0; } +int (*__libc_internal_tsd_set)(int, const void *) = pthread_internal_tsd_set; static void *pthread_internal_tsd_get( int key ) { pthread_descr descr = __pthread_thread_self(); return (void *)descr->tsd_data[key]; } +void* (*__libc_internal_tsd_get)(int) = pthread_internal_tsd_get; static void ** __attribute__((const)) pthread_internal_tsd_address( int key ) { pthread_descr descr = __pthread_thread_self(); return (void **)&descr->tsd_data[key]; } +void** (*__libc_internal_tsd_address)(int) = pthread_internal_tsd_address; /***** "EXCEPTION" FRAMES *****/ /* not implemented right now */ @@ -753,94 +620,54 @@ int pthread_condattr_destroy(pthread_condattr_t *attr) return 0; } -#if (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 2) /***** READ-WRITE LOCKS *****/ -static void rwlock_real_init(pthread_rwlock_t *rwlock) -{ - RTL_RWLOCK *lock = HeapAlloc(GetProcessHeap(), 0, sizeof(RTL_RWLOCK)); - RtlInitializeResource(lock); - - if (InterlockedCompareExchangePointer((void**)&(((wine_rwlock)rwlock)->lock),lock,NULL) != NULL) { - /* too late, some other thread already did it */ - RtlDeleteResource(lock); - HeapFree(GetProcessHeap(), 0, lock); - } -} - int __pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *rwlock_attr) { - ((wine_rwlock)rwlock)->lock = NULL; - return 0; + assert( funcs.ptr_pthread_rwlock_init ); + return funcs.ptr_pthread_rwlock_init( rwlock, rwlock_attr ); } strong_alias(__pthread_rwlock_init, pthread_rwlock_init); int __pthread_rwlock_destroy(pthread_rwlock_t *rwlock) { - if (!((wine_rwlock)rwlock)->lock) return 0; - RtlDeleteResource(((wine_rwlock)rwlock)->lock); - HeapFree(GetProcessHeap(), 0, ((wine_rwlock)rwlock)->lock); - return 0; + assert( funcs.ptr_pthread_rwlock_destroy ); + return funcs.ptr_pthread_rwlock_destroy( rwlock ); } strong_alias(__pthread_rwlock_destroy, pthread_rwlock_destroy); int __pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) { - if (!init_done) return 0; - if (!((wine_rwlock)rwlock)->lock) - rwlock_real_init( rwlock ); - - while(TRUE) - if (RtlAcquireResourceShared(((wine_rwlock)rwlock)->lock, TRUE)) - return 0; + if (!funcs.ptr_pthread_rwlock_rdlock) return 0; + return funcs.ptr_pthread_rwlock_rdlock( rwlock ); } strong_alias(__pthread_rwlock_rdlock, pthread_rwlock_rdlock); int __pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) { - if (!init_done) return 0; - if (!((wine_rwlock)rwlock)->lock) - rwlock_real_init( rwlock ); - - if (!RtlAcquireResourceShared(((wine_rwlock)rwlock)->lock, FALSE)) { - errno = EBUSY; - return -1; - } - return 0; + assert( funcs.ptr_pthread_rwlock_tryrdlock ); + return funcs.ptr_pthread_rwlock_tryrdlock( rwlock ); } strong_alias(__pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock); int __pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) { - if (!init_done) return 0; - if (!((wine_rwlock)rwlock)->lock) - rwlock_real_init( rwlock ); - - while(TRUE) - if (RtlAcquireResourceExclusive(((wine_rwlock)rwlock)->lock, TRUE)) - return 0; + assert( funcs.ptr_pthread_rwlock_wrlock ); + return funcs.ptr_pthread_rwlock_wrlock( rwlock ); } strong_alias(__pthread_rwlock_wrlock, pthread_rwlock_wrlock); int __pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) { - if (!init_done) return 0; - if (!((wine_rwlock)rwlock)->lock) - rwlock_real_init( rwlock ); - - if (!RtlAcquireResourceExclusive(((wine_rwlock)rwlock)->lock, FALSE)) { - errno = EBUSY; - return -1; - } - return 0; + assert( funcs.ptr_pthread_rwlock_trywrlock ); + return funcs.ptr_pthread_rwlock_trywrlock( rwlock ); } strong_alias(__pthread_rwlock_trywrlock, pthread_rwlock_trywrlock); int __pthread_rwlock_unlock(pthread_rwlock_t *rwlock) { - if (!((wine_rwlock)rwlock)->lock) return 0; - RtlReleaseResource( ((wine_rwlock)rwlock)->lock ); - return 0; + assert( funcs.ptr_pthread_rwlock_unlock ); + return funcs.ptr_pthread_rwlock_unlock( rwlock ); } strong_alias(__pthread_rwlock_unlock, pthread_rwlock_unlock); @@ -868,24 +695,25 @@ int pthread_rwlockattr_setkind_np(pthread_rwlockattr_t *attr, int pref) { return 0; } -#endif /* glibc 2.2 */ /***** MISC *****/ pthread_t pthread_self(void) { - return (pthread_t)GetCurrentThreadId(); + assert( funcs.ptr_pthread_self ); + return funcs.ptr_pthread_self(); } int pthread_equal(pthread_t thread1, pthread_t thread2) { - return (DWORD)thread1 == (DWORD)thread2; + assert( funcs.ptr_pthread_equal ); + return funcs.ptr_pthread_equal( thread1, thread2 ); } void __pthread_do_exit(void *retval, char *currentframe) { - /* FIXME: pthread cleanup */ - ExitThread((DWORD)retval); + assert( funcs.ptr_pthread_exit ); + return funcs.ptr_pthread_exit( retval, currentframe ); } void __pthread_exit(void *retval) @@ -894,10 +722,20 @@ void __pthread_exit(void *retval) } strong_alias(__pthread_exit, pthread_exit); +int pthread_setcancelstate(int state, int *oldstate) +{ + pthread_descr descr = __pthread_thread_self(); + if (oldstate) *oldstate = descr->cancel_state; + descr->cancel_state = state; + return 0; +} + int pthread_setcanceltype(int type, int *oldtype) { - if (oldtype) *oldtype = PTHREAD_CANCEL_ASYNCHRONOUS; - return 0; + pthread_descr descr = __pthread_thread_self(); + if (oldtype) *oldtype = descr->cancel_type; + descr->cancel_type = type; + return 0; } /***** ANTI-OVERRIDES *****/ @@ -913,7 +751,23 @@ int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) return libc_sigaction(signum, act, oldact); } -static struct pthread_functions wine_pthread_functions = +void __pthread_initialize(void) +{ + static int done; + + if (!done) + { + done = 1; + libc_fork = dlsym( RTLD_NEXT, "fork" ); + libc_sigaction = dlsym( RTLD_NEXT, "sigaction" ); + libc_uselocale = dlsym( RTLD_NEXT, "uselocale" ); + libc_pthread_init = dlsym( RTLD_NEXT, "__libc_pthread_init" ); + if (libc_pthread_init) libc_multiple_threads = libc_pthread_init( &libc_pthread_functions ); + } +} +DECL_GLOBAL_CONSTRUCTOR(init) { __pthread_initialize(); } + +static struct pthread_functions libc_pthread_functions = { NULL, /* ptr_pthread_fork */ NULL, /* FIXME */ /* ptr_pthread_attr_destroy */ @@ -946,7 +800,7 @@ static struct pthread_functions wine_pthread_functions = __pthread_mutex_trylock, /* ptr_pthread_mutex_trylock */ __pthread_mutex_unlock, /* ptr_pthread_mutex_unlock */ pthread_self, /* ptr_pthread_self */ - NULL, /* FIXME */ /* ptr_pthread_setcancelstate */ + pthread_setcancelstate, /* ptr_pthread_setcancelstate */ pthread_setcanceltype, /* ptr_pthread_setcanceltype */ __pthread_do_exit, /* ptr_pthread_do_exit */ __pthread_cleanup_upto, /* ptr_pthread_cleanup_upto */ @@ -959,16 +813,15 @@ static struct pthread_functions wine_pthread_functions = NULL /* ptr_pthread_raise */ }; -#else /* __GLIBC__ || __FREEBSD__ */ - -void PTHREAD_init_thread(void) { } -void PTHREAD_init_done(void) { } - #endif /* __GLIBC__ || __FREEBSD__ */ #else /* HAVE_NPTL */ -void PTHREAD_init_done(void) +void wine_pthread_init_process( const struct wine_pthread_functions *functions ) +{ +} + +void wine_pthread_init_thread(void) { }