From 222a76c37c89ee37eebecd53dd32fd481245e6fa Mon Sep 17 00:00:00 2001 From: rpj Date: Thu, 25 Oct 2001 13:24:58 +0000 Subject: * barrier.c: Move _LONG and _LPLONG defines into implement.h; rename to PTW32_INTERLOCKED_LONG and PTW32_INTERLOCKED_LPLONG respectively. * spin.c: Likewise; ptw32_interlocked_compare_exchange used in place of InterlockedCompareExchange directly. * global.c (ptw32_interlocked_compare_exchange): Add prototype for this new routine pointer to be used when InterlockedCompareExchange isn't supported by Windows. * nonportable.c (pthread_win32_process_attach_np): Check for support of InterlockedCompareExchange in kernel32 and assign its address to ptw32_interlocked_compare_exchange if it exists, or our own ix86 specific implementation ptw32_InterlockedCompareExchange. *private.c (ptw32_InterlockedCompareExchange): An implementation of InterlockedCompareExchange() which is specific to ix86; written directly in assembler for either MSVC or GNU C; needed because Windows 95 doesn't support InterlockedCompareExchange(). * sched.c (sched_get_priority_min): Extend to return THREAD_PRIORITY_IDLE. (sched_get_priority_max): Extend to return THREAD_PRIORITY_CRITICAL. --- ChangeLog | 25 ++++++++++++++++++++ Nmakefile.tests | 2 ++ barrier.c | 18 ++++----------- global.c | 8 +++++++ implement.h | 18 +++++++++++++++ nonportable.c | 47 ++++++++++++++++++++++++++++++++++++++ private.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ sched.c | 14 ++++++++---- spin.c | 35 ++++++++++++---------------- tests/GNUmakefile | 3 ++- tests/Makefile | 3 ++- 11 files changed, 200 insertions(+), 41 deletions(-) diff --git a/ChangeLog b/ChangeLog index c4c90fd..489aa84 100644 --- a/ChangeLog +++ b/ChangeLog @@ -7,6 +7,31 @@ because STACK_MIN is 0 and stacksize is of type size_t (or unsigned int). +2001-10-17 Ross Johnson + + * barrier.c: Move _LONG and _LPLONG defines into + implement.h; rename to PTW32_INTERLOCKED_LONG and + PTW32_INTERLOCKED_LPLONG respectively. + * spin.c: Likewise; ptw32_interlocked_compare_exchange used + in place of InterlockedCompareExchange directly. + * global.c (ptw32_interlocked_compare_exchange): Add + prototype for this new routine pointer to be used when + InterlockedCompareExchange isn't supported by Windows. + * nonportable.c (pthread_win32_process_attach_np): Check for + support of InterlockedCompareExchange in kernel32 and assign its + address to ptw32_interlocked_compare_exchange if it exists, or + our own ix86 specific implementation ptw32_InterlockedCompareExchange. + *private.c (ptw32_InterlockedCompareExchange): An + implementation of InterlockedCompareExchange() which is + specific to ix86; written directly in assembler for either + MSVC or GNU C; needed because Windows 95 doesn't support + InterlockedCompareExchange(). + + * sched.c (sched_get_priority_min): Extend to return + THREAD_PRIORITY_IDLE. + (sched_get_priority_max): Extend to return + THREAD_PRIORITY_CRITICAL. + 2001-10-15 Ross Johnson * spin.c (pthread_spin_lock): PTHREAD_SPINLOCK_INITIALIZER diff --git a/Nmakefile.tests b/Nmakefile.tests index 109a995..a99b1db 100644 --- a/Nmakefile.tests +++ b/Nmakefile.tests @@ -95,6 +95,7 @@ benchtest1:: benchtest1.c benchtest2:: benchtest2.c benchtest3:: benchtest3.c benchtest4:: benchtest4.c +benchtest5:: benchtest5.c loadfree: :test: mutex1 :test: loadfree @@ -161,6 +162,7 @@ benchtest1 :test: mutex3 benchtest2 :test: benchtest1 benchtest3 :test: benchtest2 benchtest4 :test: benchtest3 +benchtest5 :test: benchtest4 exception1 :test: cancel4 exception2 :test: exception1 exception3 :test: exception2 diff --git a/barrier.c b/barrier.c index 3cfa66f..e989095 100644 --- a/barrier.c +++ b/barrier.c @@ -27,14 +27,6 @@ #include "implement.h" -#ifdef __MINGW32__ -#define _LONG long -#define _LPLONG long* -#else -#define _LONG PVOID -#define _LPLONG PVOID* -#endif - int pthread_barrier_init(pthread_barrier_t * barrier, const pthread_barrierattr_t * attr, @@ -169,14 +161,14 @@ pthread_barrier_wait(pthread_barrier_t *barrier) /* * The first thread across will be the PTHREAD_BARRIER_SERIAL_THREAD. - * It also sets up the alternate semaphore as the next barrier. + * This also sets up the alternate semaphore as the next barrier. */ if (0 == result) { - result = ((_LONG) step == - InterlockedCompareExchange((_LPLONG) &(b->iStep), - (_LONG) (1L - step), - (_LONG) step) + result = ((PTW32_INTERLOCKED_LONG) step == + ptw32_interlocked_compare_exchange((PTW32_INTERLOCKED_LPLONG) &(b->iStep), + (PTW32_INTERLOCKED_LONG) (1L - step), + (PTW32_INTERLOCKED_LONG) step) ? PTHREAD_BARRIER_SERIAL_THREAD : 0); } diff --git a/global.c b/global.c index 92ea8da..3a1c635 100644 --- a/global.c +++ b/global.c @@ -34,6 +34,14 @@ pthread_key_t ptw32_cleanupKey = NULL; int ptw32_concurrency = 0; +/* + * Function pointer to InterlockedCompareExchange if it exists; otherwise NULL + */ +PTW32_INTERLOCKED_LONG +(WINAPI *ptw32_interlocked_compare_exchange)(PTW32_INTERLOCKED_LPLONG, + PTW32_INTERLOCKED_LONG, + PTW32_INTERLOCKED_LONG) = NULL; + /* * Global lock for testing internal state of PTHREAD_MUTEX_INITIALIZER * created mutexes. diff --git a/implement.h b/implement.h index 5c1d7ec..dfb2dba 100644 --- a/implement.h +++ b/implement.h @@ -45,6 +45,14 @@ #define INLINE #endif +#ifdef __MINGW32__ +#define PTW32_INTERLOCKED_LONG long +#define PTW32_INTERLOCKED_LPLONG long* +#else +#define PTW32_INTERLOCKED_LONG PVOID +#define PTW32_INTERLOCKED_LPLONG PVOID* +#endif + typedef enum { /* * This enumeration represents the state of the thread; @@ -410,6 +418,16 @@ extern "C" { * ===================== * ===================== */ +PTW32_INTERLOCKED_LONG +(WINAPI *ptw32_interlocked_compare_exchange)(PTW32_INTERLOCKED_LPLONG, + PTW32_INTERLOCKED_LONG, + PTW32_INTERLOCKED_LONG); + +PTW32_INTERLOCKED_LONG +ptw32_InterlockedCompareExchange(PTW32_INTERLOCKED_LPLONG ptr, + PTW32_INTERLOCKED_LONG value, + PTW32_INTERLOCKED_LONG comparand); + int ptw32_processInitialize (void); void ptw32_processTerminate (void); diff --git a/nonportable.c b/nonportable.c index 5fcb1b8..de9dc07 100644 --- a/nonportable.c +++ b/nonportable.c @@ -179,6 +179,11 @@ pthread_getprocessors_np(int * count) } +/* + * Handle to kernel32.dll + */ +static HINSTANCE ptw32_h_kernel32; + BOOL pthread_win32_process_attach_np () { @@ -189,6 +194,43 @@ pthread_win32_process_attach_np () pthread_count++; #endif + /* + * Load KERNEL32 and try to get address of InterlockedCompareExchange + */ + ptw32_h_kernel32 = LoadLibrary(TEXT("KERNEL32.DLL")); + + ptw32_interlocked_compare_exchange = + (PTW32_INTERLOCKED_LONG (PT_STDCALL *)(PTW32_INTERLOCKED_LPLONG, PTW32_INTERLOCKED_LONG, PTW32_INTERLOCKED_LONG)) +#if defined(NEED_UNICODE_CONSTS) + GetProcAddress(ptw32_h_kernel32, + (const TCHAR *)TEXT("InterlockedCompareExchange")); +#else + GetProcAddress(ptw32_h_kernel32, + (LPCSTR) "InterlockedCompareExchange"); +#endif + + if (ptw32_interlocked_compare_exchange == NULL) + { + ptw32_interlocked_compare_exchange = &ptw32_InterlockedCompareExchange; + + /* + * If InterlockedCompareExchange is not being used, then free + * the kernel32.dll handle now, rather than leaving it until + * DLL_PROCESS_DETACH. + * + * Note: this is not a pedantic exercise in freeing unused + * resources! It is a work-around for a bug in Windows 95 + * (see microsoft knowledge base article, Q187684) which + * does Bad Things when FreeLibrary is called within + * the DLL_PROCESS_DETACH code, in certain situations. + * Since w95 just happens to be a platform which does not + * provide InterlockedCompareExchange, the bug will be + * effortlessly avoided. + */ + (void) FreeLibrary(ptw32_h_kernel32); + ptw32_h_kernel32 = 0; + } + return result; } @@ -214,6 +256,11 @@ pthread_win32_process_detach_np () * The DLL is being unmapped into the process's address space */ ptw32_processTerminate (); + + if (ptw32_h_kernel32) + { + (void) FreeLibrary(ptw32_h_kernel32); + } } return TRUE; diff --git a/private.c b/private.c index a332385..f97d9f4 100644 --- a/private.c +++ b/private.c @@ -1020,3 +1020,71 @@ ptw32_pop_cleanup_all(int execute) while( NULL != ptw32_pop_cleanup(execute) ) { } } + + +/* + * ptw32_InterlockedCompareExchange -- + * + * Needed because W95 doesn't support InterlockedCompareExchange. + * It is only used when running the dll on W95. Other versions of + * Windows use the Win32 supported version, which may be running on + * different processor types. + * + * This can't be inlined because we need to know it's address so that + * we can call it through a pointer. + */ +PTW32_INTERLOCKED_LONG +ptw32_InterlockedCompareExchange(PTW32_INTERLOCKED_LPLONG ptr, + PTW32_INTERLOCKED_LONG value, + PTW32_INTERLOCKED_LONG comparand) +{ + PTW32_INTERLOCKED_LONG result; + +#if defined(_M_IX86) || defined(_X86_) + +#if defined(__MSVCRT__) + + _asm { + PUSH ecx + PUSH edx + MOV ecx,dword ptr [ptr] ; Load ECX with plTarget + MOV edx,dword ptr [value] ; Load EDX with lValue + MOV eax,dword ptr [comparand] + LOCK CMPXCHG dword ptr [ecx],edx ; if (EAX == [ECX]), + ; [ECX] = EDX + ; else + ; EAX = [ECX] + MOV dword ptr [result], eax + POP edx + POP ecx + } + +#elif defined(__GNUC__) + + __asm__ + ( + "lock\n\t" + "cmpxchgl %3,(%0)" /* if (EAX == [ptr]), */ + /* [ptr] = value */ + /* else */ + /* EAX = [ptr] */ + :"=r" (ptr), "=a" (result) + :"0" (ptr), "q" (value), "a" (comparand) + : "memory" ); + +#endif + +#else + + /* + * If we get to here then we should be running on a Win95 system but either + * running on something other than an X86 processor, or a compiler other + * than MSVC or GCC. Pthreads-win32 doesn't support that platform (yet). + */ + + result = 0; + +#endif + + return result; +} diff --git a/sched.c b/sched.c index bc8aca0..8ef502f 100644 --- a/sched.c +++ b/sched.c @@ -155,6 +155,8 @@ int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param) { + int prio; + /* Validate the thread id. */ if (thread == NULL || thread->threadH == 0) { @@ -173,15 +175,17 @@ pthread_setschedparam(pthread_t thread, int policy, return ENOTSUP; } + prio = param->sched_priority; + /* Validate priority level. */ - if (param->sched_priority < sched_get_priority_min(policy) || - param->sched_priority > sched_get_priority_max(policy)) + if (prio < sched_get_priority_min(policy) || + prio > sched_get_priority_max(policy)) { return EINVAL; } /* This is practically guaranteed to return TRUE. */ - (void) SetThreadPriority(thread->threadH, param->sched_priority); + (void) SetThreadPriority(thread->threadH, prio); return 0; } @@ -253,7 +257,7 @@ sched_get_priority_max(int policy) return sched_Max(THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL); #else /* This is independent of scheduling policy in Win32. */ - return sched_Max(THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_HIGHEST); + return sched_Max(THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL) #endif } @@ -271,7 +275,7 @@ sched_get_priority_min(int policy) return sched_Min(THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL); #else /* This is independent of scheduling policy in Win32. */ - return sched_Min(THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_HIGHEST); + return sched_Min(THREAD_PRIORITY_IDLE, THREAD_PRIORITY_TIME_CRITICAL); #endif } diff --git a/spin.c b/spin.c index b35b9f0..09c5031 100644 --- a/spin.c +++ b/spin.c @@ -26,13 +26,6 @@ #include "pthread.h" #include "implement.h" -#ifdef __MINGW32__ -#define _LONG long -#define _LPLONG long* -#else -#define _LONG PVOID -#define _LPLONG PVOID* -#endif static INLINE int ptw32_spinlock_check_need_init(pthread_spinlock_t *lock) @@ -177,10 +170,10 @@ pthread_spin_destroy(pthread_spinlock_t *lock) return pthread_mutex_destroy(&(s->u.mutex)); } - if ( (_LONG) PTW32_SPIN_UNLOCKED == - InterlockedCompareExchange((_LPLONG) &(s->interlock), - (_LONG) PTW32_OBJECT_INVALID, - (_LONG) PTW32_SPIN_UNLOCKED)) + if ( (PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED == + ptw32_interlocked_compare_exchange((PTW32_INTERLOCKED_LPLONG) &(s->interlock), + (PTW32_INTERLOCKED_LONG) PTW32_OBJECT_INVALID, + (PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED)) { return 0; } @@ -243,10 +236,10 @@ pthread_spin_lock(pthread_spinlock_t *lock) s = *lock; - while ( (_LONG) PTW32_SPIN_LOCKED == - InterlockedCompareExchange((_LPLONG) &(s->interlock), - (_LONG) PTW32_SPIN_LOCKED, - (_LONG) PTW32_SPIN_UNLOCKED) ) + while ( (PTW32_INTERLOCKED_LONG) PTW32_SPIN_LOCKED == + ptw32_interlocked_compare_exchange((PTW32_INTERLOCKED_LPLONG) &(s->interlock), + (PTW32_INTERLOCKED_LONG) PTW32_SPIN_LOCKED, + (PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED) ) {} if (s->interlock == PTW32_SPIN_LOCKED) @@ -271,9 +264,9 @@ pthread_spin_unlock(pthread_spinlock_t *lock) return EPERM; } - switch ((long) InterlockedCompareExchange((_LPLONG) &(s->interlock), - (_LONG) PTW32_SPIN_UNLOCKED, - (_LONG) PTW32_SPIN_LOCKED )) + switch ((long) ptw32_interlocked_compare_exchange((PTW32_INTERLOCKED_LPLONG) &(s->interlock), + (PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED, + (PTW32_INTERLOCKED_LONG) PTW32_SPIN_LOCKED )) { case PTW32_SPIN_LOCKED: return 0; case PTW32_SPIN_UNLOCKED: return EPERM; @@ -298,9 +291,9 @@ pthread_spin_trylock(pthread_spinlock_t *lock) } } - switch ((long) InterlockedCompareExchange((_LPLONG) &(s->interlock), - (_LONG) PTW32_SPIN_LOCKED, - (_LONG) PTW32_SPIN_UNLOCKED )) + switch ((long) ptw32_interlocked_compare_exchange((PTW32_INTERLOCKED_LPLONG) &(s->interlock), + (PTW32_INTERLOCKED_LONG) PTW32_SPIN_LOCKED, + (PTW32_INTERLOCKED_LONG) PTW32_SPIN_UNLOCKED )) { case PTW32_SPIN_UNLOCKED: return 0; case PTW32_SPIN_LOCKED: return EBUSY; diff --git a/tests/GNUmakefile b/tests/GNUmakefile index d62e45c..8e06c68 100644 --- a/tests/GNUmakefile +++ b/tests/GNUmakefile @@ -53,7 +53,7 @@ TESTS = loadfree \ exception1 exception2 exception3 BENCHTESTS = \ - benchtest1 benchtest2 benchtest3 benchtest4 + benchtest1 benchtest2 benchtest3 benchtest4 benchtest5 PASSES = $(TESTS:%=%.pass) BENCHRESULTS = $(BENCHTESTS:%=%.bench) @@ -91,6 +91,7 @@ benchtest1.bench: benchtest2.bench: benchtest3.bench: benchtest4.bench: +benchtest5.bench: barrier1.pass: barrier2.pass: barrier1.pass diff --git a/tests/Makefile b/tests/Makefile index dba30d3..1a7b6bf 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -62,7 +62,7 @@ PASSES= loadfree.pass \ exception1.pass exception2.pass exception3.pass BENCHRESULTS = \ - benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench + benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench all: @ $(ECHO) Run one of the following command lines: @@ -147,6 +147,7 @@ benchtest1.bench: benchtest2.bench: benchtest3.bench: benchtest4.bench: +benchtest5.bench: barrier1.pass: barrier2.pass: barrier1.pass barrier3.pass: barrier2.pass -- cgit v1.2.3