diff options
author | rpj <rpj> | 2010-06-20 03:31:18 +0000 |
---|---|---|
committer | rpj <rpj> | 2010-06-20 03:31:18 +0000 |
commit | a3ea0b24409b89bd08c0a2268dbae834724734df (patch) | |
tree | 4629fd085756226f0cee8beba4faf66466ef0410 | |
parent | 135d6f060c5e5232311af77bd0d0f500e861290c (diff) |
See ChangeLogs: preparing for new release.
38 files changed, 474 insertions, 332 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0d650e1..334b945 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -128,3 +128,7 @@ Vladimir Kliatchko vladimir at kliatchko dot com reimplemented pthread_once with the same form as described by A.Terekhov (later version 2); implementation of MCS (Mellor-Crummey/Scott) locks. +Ramiro Polla ramiro.polla at gmail.com + static library auto init/cleanup on application + start/exit via RT hooks (MSC and GCC compilers only). +
\ No newline at end of file @@ -1,105 +1,50 @@ -2009-03-03 Stephan O'Farrill <stephan dot ofarrill at gmail dot com> - - * pthread_attr_getschedpolicy.c: Add "const" to function parameter - in accordance with SUSv3 (POSIX). - * pthread_attr_getinheritsched.c: Likewise. - * pthread_mutexattr_gettype.c: Likewise. - - -2008-06-06 Robert Kindred <RKindred at SwRI dot edu> - - * ptw32_throw.c (ptw32_throw): Remove possible reference to NULL - pointer. (At the same time made the switch block conditionally - included only if exitCode is needed - RPJ.) - * pthread_testcancel.c (pthread_testcancel): Remove duplicate and - misplaced pthread_mutex_unlock(). - -2008-02-21 Sebastian Gottschalk <seppig_relay at gmx dot de> - - * pthread_attr_getdetachstate.c (pthread_attr_getdetachstate): - Remove potential and superfluous null pointer assignment. - -2007-11-22 Ivan Pizhenko <ivanp4 at ua dot fm> - - * pthread.h (gmtime_r): gmtime returns 0 if tm represents a time - prior to 1/1/1970. Notice this to prevent raising an exception. - * pthread.h (localtime_r): Likewise for localtime. - -2007-07-14 Marcel Ruff <mr at marcelruff dot info> - - * errno.c (_errno): Fix test for pthread_self() success. - * need_errno.h: Remove unintentional line wrap from #if line. - -2007-07-14 Mike Romanchuk <mromanchuk at empirix dot com> - - * pthread.h (timespec): Fix tv_sec type. - -2007-01-07 Sinan Kaya <sinan.kaya at siemens dot com> - - * need_errno.h: Fix declaration of _errno - the local version of - _errno() is used, e.g. by WinCE. - -2007-01-06 Ross Johnson <ross.johnson at homemail dot com dot au> - - * ptw32_semwait.c: Add check for invalid sem_t after acquiring the - sem_t state guard mutex and before affecting changes to sema state. - -2007-01-06 Marcel Ruff <mr at marcelruff dot info> - - * error.c: Fix reference to pthread handle exitStatus member for - builds that use NEED_ERRNO (i.e. WINCE). - * context.h: Add support for ARM processor (WinCE). - * mutex.c (process.h): Exclude for WINCE. - * create.c: Likewise. - * exit.c: Likewise. - * implement.h: Likewise. - * pthread_detach.c (signal.h): Exclude for WINCE. - * pthread_join.c: Likewise. - * pthread_kill.c: Likewise. - * pthread_rwlock_init.c (errno.h): Remove - included by pthread.h. - * pthread_rwlock_destroy.c: Likewise. - * pthread_rwlock_rdlock.c: Likewise. - * pthread_rwlock_timedrdlock.c: Likewise. - * pthread_rwlock_timedwrlock.c: Likewise. - * pthread_rwlock_tryrdlock.c: Likewise. - * pthread_rwlock_trywrlock.c: likewise. - * pthread_rwlock_unlock.c: Likewise. - * pthread_rwlock_wrlock.c: Likewise. - * pthread_rwlockattr_destroy.c: Likewise. - * pthread_rwlockattr_getpshared.c: Likewise. - * pthread_rwlockattr_init.c: Likewise. - * pthread_rwlockattr_setpshared.c: Likewise. - -2007-01-06 Romano Paolo Tenca <rotenca at telvia dot it> - - * pthread_cond_destroy.c: Replace sem_wait() with non-cancelable - ptw32_semwait() since pthread_cond_destroy() is not a cancelation - point. - * implement.h (ptw32_spinlock_check_need_init): Add prototype. - * ptw32_MCS_lock.c: Reverse order of includes. - -2007-01-06 Eric Berge <eric dot berge at quantum dot com> - - * pthread_cond_destroy.c: Add LeaveCriticalSection before returning - after errors. - -2007-01-04 Ross Johnson <ross.johnson at homemail dot com dot au> - - * ptw32_InterlockedCompareExchange.c: Conditionally skip for - Win64 as not required. - * pthread_win32_attach_detach_np.c (pthread_win32_process_attach_np): - Test for InterlockedCompareExchange is not required for Win64. - * context.h: New file. Included by pthread_cancel.h and any tests - that need it (e.g. context1.c). - * pthread_cancel.c: Architecture-dependent context macros moved - to context.h. - -2007-01-04 Kip Streithorst <KSTREITH at ball dot com> - - * implement.h (PTW32_INTERLOCKED_COMPARE_EXCHANGE): Add Win64 - support. +2010-06-19 Ross Johnson <ross.johnson at homemail.com.au> + + * ptw32_MCS_lock.c (ptw32_mcs_node_substitute): Fix variable + names to avoid using C++ keyword ("new"). + * implement.h (ptw32_mcs_node_substitute): Likewise. + * pthread_barrier_wait.c: Fix signed/unsigned comparison warning. + +2010-06-18 Ramiro Polla <ramiro.polla at gmail.com > + + * autostatic.c: New file; call pthread_win32_process_*() + libary init/cleanup routines automatically on application start + when statically linked. + * pthread.c (autostatic.c): Included. + * pthread.h (declspec): Remove import/export defines if compiler + is MINGW. + * sched.h (declspec): Likewise. + * semaphore.h (declspec): Likewise. + * need_errno.h (declspec): Likewise. + * Makefile (autostatic.obj): Add for small static builds. + * GNUmakefile (autostatic.o): Likewise. + * NEWS (Version 2.9.0): Add changes. + * README.NONPORTABLE (pthread_win32_process_*): Update + description. + +2010-06-15 Ramiro Polla <ramiro.polla at gmail.com > + + * Makefile: Remove linkage with the winsock library by default. + * GNUmakefile: Likewise. + * pthread_getspecific.c: Likewise by removing calls to WSA + functions. + * config.h (RETAIN_WSALASTERROR): Can be defined if necessary. + +2010-01-26 Ross Johnson <ross.johnson at homemail.com.au> + + * ptw32_MCS_lock.c (ptw32_mcs_node_substitute): New routine + to allow relocating the lock owners thread-local node to somewhere + else, e.g. to global space so that another thread can release the + lock. Used in pthread_barrier_wait. + (ptw32_mcs_lock_try_acquire): New routine. + * pthread_barrier_init: Only one semaphore is used now. + * pthread_barrier_wait: Added an MCS guard lock with the last thread + to leave the barrier releasing the lock. This removes a deadlock bug + observed when there are greater than barrier-count threads + attempting to cross. + * pthread_barrier_destroy: Added an MCS guard lock. -2006-12-20 Ross Johnson <ross.johnson at homemail dot com dot au> +2006-12-20 Ross Johnson <ross.johnson at homemail.com.au> * sem_destroy.c: Fix the race involving invalidation of the sema; fix incorrect return of EBUSY resulting from the mutex trylock diff --git a/GNUmakefile b/GNUmakefile index 6c48fd2..652edb8 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -63,7 +63,8 @@ DOPT = $(CLEANUP) -g -O0 XOPT = RCFLAGS = --include-dir=. -LFLAGS = -lwsock32 +# Uncomment this if config.h defines RETAIN_WSALASTERROR +#LFLAGS = -lwsock32 # ---------------------------------------------------------------------- # The library can be built with some alternative behaviour to @@ -170,6 +171,7 @@ SMALL_STATIC_OBJS = \ pthread_cond_wait.o \ create.o \ dll.o \ + autostatic.o \ errno.o \ pthread_exit.o \ fork.o \ @@ -436,6 +438,8 @@ GCE_LIB = libpthreadGCE$(DLL_VER).a GCED_LIB= libpthreadGCE$(DLL_VERD).a GCE_INLINED_STAMP = pthreadGCE$(DLL_VER).stamp GCED_INLINED_STAMP = pthreadGCE$(DLL_VERD).stamp +GCE_STATIC_STAMP = libpthreadGCE$(DLL_VER).stamp +GCED_STATIC_STAMP = libpthreadGCE$(DLL_VERD).stamp GC_DLL = pthreadGC$(DLL_VER).dll GCD_DLL = pthreadGC$(DLL_VERD).dll @@ -30,6 +30,8 @@ OPTIMD = CFLAGS = /W3 /MD /nologo /I. /D_WIN32_WINNT=0x400 /DHAVE_CONFIG_H CFLAGSD = /Zi $(CFLAGS) +# Uncomment this if config.h defines RETAIN_WSALASTERROR +#XLIBS = wsock32.lib # Default cleanup style CLEANUP = __CLEANUP_C @@ -57,6 +59,7 @@ DLL_OBJS = \ condvar.obj \ create.obj \ dll.obj \ + autostatic.obj \ errno.obj \ exit.obj \ fork.obj \ @@ -114,6 +117,7 @@ SMALL_STATIC_OBJS = \ pthread_cond_wait.obj \ create.obj \ dll.obj \ + autostatic.obj \ errno.obj \ pthread_exit.obj \ fork.obj \ @@ -466,12 +470,12 @@ install: $(DLLS) $(DLLS): $(DLL_OBJS) cl /LDd /Zi /nologo $(DLL_OBJS) \ /link /nodefaultlib:libcmt /implib:$*.lib \ - msvcrt.lib wsock32.lib /out:$@ + msvcrt.lib ${XLIBS} /out:$@ $(INLINED_STAMPS): $(DLL_INLINED_OBJS) cl /LDd /Zi /nologo $(DLL_INLINED_OBJS) \ /link /nodefaultlib:libcmt /implib:$*.lib \ - msvcrt.lib wsock32.lib /out:$*.dll + msvcrt.lib ${XLIBS} /out:$*.dll $(STATIC_STAMPS): $(DLL_INLINED_OBJS) if exist $*.lib del $*.lib @@ -1,6 +1,7 @@ -RELEASE 2.9.0 +CURRENT CVS HEAD Version +RELEASE 2.9.0 pending ------------- -(2008-??-??) +(2010-??-??) General ------- @@ -20,8 +21,29 @@ identifying changes in version.rc to differentiate your build. Testing and verification ------------------------ -This release has been tested on UP and SMP architectures. Thanks to Tim -Theisen for running the Win32 SMP tests. +The current CVS head version has been tested on an SMP architecture +(AMD Phenom 9750 Quad Core) by running the MinGW32 (GCC) builds against +the full test suite, stress tests and benchmarks. + +New Features +------------ +Dependence on the winsock library is now discretionary via +#define RETAIN_WSALASTERROR in config.h. It is undefined by default unless +WINCE is defined (because I (RJ) am unsure of the dependency there). +- Ramiro Polla + +(MSC and GNU builds) The statically linked library now automatically +initialises and cleans up on program start/exit, i.e. statically linked +applications need not call the routines pthread_win32_process_attach_np() +and pthread_win32_process_detach_np() explicitly. The per-thread routine +pthread_win32_thread_detach_np() is also called at program exit to cleanup +POSIX resources acquired by the primary Windows native thread (if I (RJ) +understand the process correctly). Other Windows native threads that call +POSIX API routines may need to call the thread detach routine on thread +exit if the application depends on reclaimed POSIX resources or running +POSIX TSD (TLS) destructors. +See README.NONPORTABLE for descriptions of these routines. +- Ramiro Polla Bug fixes --------- @@ -39,12 +61,19 @@ Various modifications to build and test for Win64. - Kip Streithorst Various fixes to the QueueUserAPCEx async cancellation helper DLL -and pthreads code cleanups. +(this is a separate download) and pthreads code cleanups. - Sebastian Gottschalk Removed potential NULL pointer reference. - Robert Kindred +Removed the requirement that applications restrict the number of threads +calling pthread_barrier_wait() [on the same barrier] to just the barrier +count in order to avoid contention and dead lock. Also reduced the +contention between barrier_wait and barrier_destroy. This change will +have slowed barriers down slightly but halves the number of semaphores +consumed per barrier to one. +- Ross Johnson RELEASE 2.8.0 ------------- diff --git a/README.NONPORTABLE b/README.NONPORTABLE index 192fef6..12095ce 100644 --- a/README.NONPORTABLE +++ b/README.NONPORTABLE @@ -156,19 +156,24 @@ pthread_win32_thread_detach_np (void); These functions contain the code normally run via dllMain when the library is used as a dll but which need to be called explicitly by an application when the library - is statically linked. + is statically linked. As of version 2.9.0 of the library, static + builds using either MSC or GCC will call pthread_win32_process_* + automatically at application startup and exit respectively. - You will need to call pthread_win32_process_attach_np() before - you can call any pthread routines when statically linking. + Otherwise, you will need to call pthread_win32_process_attach_np() + before you can call any pthread routines when statically linking. You should call pthread_win32_process_detach_np() before exiting your application to clean up. pthread_win32_thread_attach_np() is currently a no-op, but pthread_win32_thread_detach_np() is needed to clean up the implicit pthread handle that is allocated to a Win32 thread if - it calls certain pthreads routines. Call this routine when the + it calls any pthreads routines. Call this routine when the Win32 thread exits. + Threads created through pthread_create() do not need to call + pthread_win32_thread_detach_np(). + These functions invariably return TRUE except for pthread_win32_process_attach_np() which will return FALSE if pthreads-win32 initialisation fails. @@ -52,6 +52,12 @@ /* Define if you don't have the GetProcessAffinityMask() */ #undef NEED_PROCESS_AFFINITY_MASK +/* Define if your version of Windows TLSGetValue() clears WSALastError + * and calling SetLastError() isn't enough restore it. You'll also need to + * link against wsock32.lib (or libwsock32.a for MinGW). + */ +#undef RETAIN_WSALASTERROR + /* # ---------------------------------------------------------------------- # The library can be built with some alternative behaviour to better @@ -103,6 +109,8 @@ /* #define NEED_SEM */ #define NEED_UNICODE_CONSTS #define NEED_PROCESS_AFFINITY_MASK +/* This may not be needed */ +#define RETAIN_WSALASTERROR #endif #ifdef _UWIN @@ -49,7 +49,7 @@ pthread_cond_t ptw32_cond_list_tail = NULL; int ptw32_concurrency = 0; -/* What features have been auto-detected */ +/* What features have been auto-detaected */ int ptw32_features = 0; BOOL ptw32_smp_system = PTW32_TRUE; /* Safer if assumed true initially. */ diff --git a/implement.h b/implement.h index 69930ef..00f153f 100644 --- a/implement.h +++ b/implement.h @@ -11,7 +11,7 @@ * Copyright(C) 1998 John E. Bossom * Copyright(C) 1999,2005 Pthreads-win32 contributors * - * Contact Email: rpj@callisto.canberra.edu.au + * Contact Email: Ross.Johnson@homemail.com.au * * The current list of contributors is contained * in the file CONTRIBUTORS included with the source @@ -151,9 +151,6 @@ struct ptw32_thread_t_ int implicit:1; void *keys; void *nextAssoc; -#ifdef _POSIX_CXX09_EXTENSIONS - int refs; /* C++ Thread Support Library extension */ -#endif }; @@ -252,13 +249,31 @@ struct pthread_spinlock_t_ } u; }; +/* + * MCS lock queue node - see ptw32_MCS_lock.c + */ +struct ptw32_mcs_node_t_ +{ + struct ptw32_mcs_node_t_ **lock; /* ptr to tail of queue */ + struct ptw32_mcs_node_t_ *next; /* ptr to successor in queue */ + LONG readyFlag; /* set after lock is released by + predecessor */ + LONG nextFlag; /* set after 'next' ptr is set by + successor */ +}; + +typedef struct ptw32_mcs_node_t_ ptw32_mcs_local_node_t; +typedef struct ptw32_mcs_node_t_ *ptw32_mcs_lock_t; + + struct pthread_barrier_t_ { unsigned int nCurrentBarrierHeight; unsigned int nInitialBarrierHeight; - int iStep; int pshared; - sem_t semBarrierBreeched[2]; + sem_t semBarrierBreeched; + void * lock; /* MCS lock */ + ptw32_mcs_local_node_t proxynode; }; struct pthread_barrierattr_t_ @@ -276,7 +291,6 @@ struct pthread_key_t_ typedef struct ThreadParms ThreadParms; -typedef struct ThreadKeyAssoc ThreadKeyAssoc; struct ThreadParms { @@ -327,22 +341,7 @@ struct pthread_rwlockattr_t_ int pshared; }; -/* - * MCS lock queue node - see ptw32_MCS_lock.c - */ -struct ptw32_mcs_node_t_ -{ - struct ptw32_mcs_node_t_ **lock; /* ptr to tail of queue */ - struct ptw32_mcs_node_t_ *next; /* ptr to successor in queue */ - LONG readyFlag; /* set after lock is released by - predecessor */ - LONG nextFlag; /* set after 'next' ptr is set by - successor */ -}; - -typedef struct ptw32_mcs_node_t_ ptw32_mcs_local_node_t; -typedef struct ptw32_mcs_node_t_ *ptw32_mcs_lock_t; - +typedef struct ThreadKeyAssoc ThreadKeyAssoc; struct ThreadKeyAssoc { @@ -380,17 +379,16 @@ struct ThreadKeyAssoc * general lock (guarding the row) and the thread's general * lock (guarding the column). This avoids the need for a * dedicated lock for each association, which not only consumes - * more handles but requires that: before the lock handle can - * be released - both the key must be deleted and the thread - * must have called the destructor. The two-lock arrangement - * allows the resources to be freed as soon as either thread or - * key is concluded. + * more handles but requires that the lock resources persist + * until both the key is deleted and the thread has called the + * destructor. The two-lock arrangement allows those resources + * to be freed as soon as either thread or key is concluded. * - * To avoid deadlock: whenever both locks are required, the key - * and thread locks are always acquired in the order: key lock - * then thread lock. An exception to this exists when a thread - * calls the destructors, however this is done carefully to - * avoid deadlock. + * To avoid deadlock, whenever both locks are required both the + * key and thread locks are acquired consistently in the order + * "key lock then thread lock". An exception to this exists + * when a thread calls the destructors, however, this is done + * carefully (but inelegantly) to avoid deadlock. * * An association is created when a thread first calls * pthread_setspecific() on a key that has a specified @@ -424,7 +422,7 @@ struct ThreadKeyAssoc * * nextThread * The pthread_key_t->threads attribute is the head of - * a chain of assoctiations that runs through the + * a chain of associations that runs through the * nextThreads link. This chain provides the 1 to many * relationship between a pthread_key_t and all the * PThreads that have called pthread_setspecific for @@ -460,7 +458,7 @@ struct ThreadKeyAssoc * This macro constructs a software exception code following * the same format as the standard Win32 error codes as defined * in WINERROR.H - * Values are 32 bit values layed out as follows: + * Values are 32 bit values laid out as follows: * * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 * +---+-+-+-----------------------+-------------------------------+ @@ -569,7 +567,6 @@ extern "C" int ptw32_cond_check_need_init (pthread_cond_t * cond); int ptw32_mutex_check_need_init (pthread_mutex_t * mutex); int ptw32_rwlock_check_need_init (pthread_rwlock_t * rwlock); - int ptw32_spinlock_check_need_init (pthread_spinlock_t * spinlock); PTW32_INTERLOCKED_LONG WINAPI ptw32_InterlockedCompareExchange (PTW32_INTERLOCKED_LPLONG location, @@ -623,8 +620,12 @@ extern "C" void ptw32_mcs_lock_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node); + int ptw32_mcs_lock_try_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node); + void ptw32_mcs_lock_release (ptw32_mcs_local_node_t * node); + void ptw32_mcs_node_substitute (ptw32_mcs_local_node_t * new_node, ptw32_mcs_local_node_t * old_node); + #ifdef NEED_FTIME void ptw32_timespec_to_filetime (const struct timespec *ts, FILETIME * ft); void ptw32_filetime_to_timespec (const FILETIME * ft, struct timespec *ts); @@ -662,10 +663,8 @@ extern "C" # endif # endif #else -# ifndef WINCE # include <process.h> # endif -#endif /* @@ -673,22 +672,8 @@ extern "C" * See ptw32_InterlockedCompareExchange.c */ #ifndef PTW32_INTERLOCKED_COMPARE_EXCHANGE -# ifdef _WIN64 - /* - * InterlockedCompareExchange is an intrinsic function in Win64. - */ -# define PTW32_INTERLOCKED_COMPARE_EXCHANGE _InterlockedCompareExchange -# else - /* - * The routine pthread_win32_process_attach_np() in pthread_win32_attach_detach_np.c - * checks at runtime that InterlockedCompareExchange is supported within - * KERNEL32.DLL (or COREDLL.DLL for WinCE). This allows the same - * dll to run on all Win32 versions from Win95 onwards. Not sure if this - * is required for WinCE, but should work just the same anyway. - */ # define PTW32_INTERLOCKED_COMPARE_EXCHANGE ptw32_interlocked_compare_exchange # endif -#endif #ifndef PTW32_INTERLOCKED_EXCHANGE #define PTW32_INTERLOCKED_EXCHANGE InterlockedExchange diff --git a/manual/pthread_barrier_init.html b/manual/pthread_barrier_init.html index 065c129..f581358 100644 --- a/manual/pthread_barrier_init.html +++ b/manual/pthread_barrier_init.html @@ -32,8 +32,7 @@ barrier referenced by <I>barrier</I> and release any resources used by the barrier. The effect of subsequent use of the barrier is undefined until the barrier is reinitialized by another call to <B>pthread_barrier_init</B> . An implementation may use this function -to set <I>barrier</I> to an invalid value. The results are undefined -if <B>pthread_barrier_destroy</B> is called when any thread is +to set <I>barrier</I> to an invalid value. An error code is returned if <B>pthread_barrier_destroy</B> is called when any thread is blocked on the barrier, or if this function is called with an uninitialized barrier. </P> @@ -139,10 +138,8 @@ that these routines are implemented and may be used.</P> <DL> <DD STYLE="margin-left: 0cm; margin-bottom: 0.5cm">In <B><SPAN LANG="en-GB"><SPAN LANG="en-GB">pthreads-win32</SPAN></SPAN></B>, - <A HREF="pthread_barrier_wait.html"></A><A HREF="pthread_barrier_wait.html"><B>pthread_barrier_wait</B>(3)</A><A HREF="pthread_barrier_wait.html"></A> - may deadlock if the number of running threads able to wait on the - barrier object exceeds the value given as the <I><SPAN LANG="en-GB"><SPAN LANG="en-GB">count</SPAN></SPAN></I> - parameter in <B>pthread_barrier_init</B>. + the behaviour of threads which enter <A HREF="pthread_barrier_wait.html"><B>pthread_barrier_wait</B>(3)</A> + while the barrier is being destroyed is undefined. </DD></DL> <H2> <A HREF="#toc9" NAME="sect9">See Also</A></H2> diff --git a/manual/pthread_barrier_wait.html b/manual/pthread_barrier_wait.html index 693d8bd..f407aa5 100644 --- a/manual/pthread_barrier_wait.html +++ b/manual/pthread_barrier_wait.html @@ -103,13 +103,7 @@ that this routine is implemented and may be used.</P> </P> <H2><A HREF="#toc11" NAME="sect11">Known Bugs</A></H2> <DL> - <DD STYLE="margin-left: 0cm; margin-bottom: 0.5cm">In - <B><SPAN LANG="en-GB">pthreads-win32</SPAN></B>, - <B>pthread_barrier_wait</B> may deadlock if the number of running - threads able to wait on the barrier object exceeds the value given - as the <I><SPAN LANG="en-GB">count</SPAN></I> parameter in - <A HREF="pthread_barrier_init.html"><B>pthread_barrier_init(3)</B></A>. - </DD></DL> + None.</DL> <H2> <A HREF="#toc9" NAME="sect9">See Also</A></H2> <P><A HREF="pthread_barrier_init.html"><B>pthread_barrier_destroy</B>(3)</A>, diff --git a/manual/pthread_win32_attach_detach_np.html b/manual/pthread_win32_attach_detach_np.html index 57f0339..f8cfc36 100644 --- a/manual/pthread_win32_attach_detach_np.html +++ b/manual/pthread_win32_attach_detach_np.html @@ -27,9 +27,9 @@ statically linking the library.</P> <P><B>BOOL pthread_win32_thread_attach_np (void);</B></P> <P><B>BOOL pthread_win32_thread_detach_np (void);</B></P> <H2><A HREF="#toc2" NAME="sect2">Description</A></H2> -<P>These functions contain the code normally run via <B>dllMain</B> +<P>These functions contain the code normally run via <b>dllMain</b> when the library is used as a dll but which need to be called -explicitly by an application when the library is statically linked.</P> +explicitly by an application when the library is statically linked. As of version 2.9.0, the static library built using either MSC or GCC includes RT hooks which will call the pthread_win32_process_*_np routines automatically on start/exit of the application.</P> <P>You will need to call <B>pthread_win32_process_attach_np</B> before you can call any pthread routines when statically linking. You should call <B>pthread_win32_process_detach_np</B> before exiting diff --git a/need_errno.h b/need_errno.h index a28955a..6099479 100644 --- a/need_errno.h +++ b/need_errno.h @@ -59,8 +59,7 @@ extern "C" { #endif #endif -/* declare reference to errno */ -#ifndef PTW32_STATIC_LIB +#if !defined(PTW32_STATIC_LIB) # ifdef PTW32_BUILD # define PTW32_DLLPORT __declspec (dllexport) # else @@ -49,6 +49,7 @@ #include "condvar.c" #include "create.c" #include "dll.c" +#include "autostatic.c" #include "errno.c" #include "exit.c" #include "fork.c" @@ -528,12 +528,12 @@ extern "C" #endif /* - * When building the DLL code, you should define PTW32_BUILD so that - * the variables/functions are exported correctly. When using the DLL, + * When building the library, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the library, * do NOT define PTW32_BUILD, and then the variables/functions will * be imported correctly. */ -#ifndef PTW32_STATIC_LIB +#if !defined(PTW32_STATIC_LIB) # ifdef PTW32_BUILD # define PTW32_DLLPORT __declspec (dllexport) # else diff --git a/pthread_barrier_destroy.c b/pthread_barrier_destroy.c index 9302ba7..baed212 100644 --- a/pthread_barrier_destroy.c +++ b/pthread_barrier_destroy.c @@ -37,31 +37,67 @@ #include "pthread.h" #include "implement.h" - int pthread_barrier_destroy (pthread_barrier_t * barrier) { int result = 0; pthread_barrier_t b; + ptw32_mcs_local_node_t node; if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID) { return EINVAL; } + if (0 != ptw32_mcs_lock_try_acquire((ptw32_mcs_lock_t *)&(*barrier)->lock, &node)) + { + return EBUSY; + } + b = *barrier; - *barrier = NULL; - if (0 == (result = sem_destroy (&(b->semBarrierBreeched[0])))) + if (b->nCurrentBarrierHeight < b->nInitialBarrierHeight) { - if (0 == (result = sem_destroy (&(b->semBarrierBreeched[1])))) + result = EBUSY; + } + else { + if (0 == (result = sem_destroy (&(b->semBarrierBreeched)))) + { + *barrier = (pthread_barrier_t) PTW32_OBJECT_INVALID; + /* + * Release the lock before freeing b. + * + * FIXME: There may be successors which, when we release the lock, + * will be linked into b->lock, which will be corrupted at some + * point with undefined results for the application. To fix this + * will require changing pthread_barrier_t from a pointer to + * pthread_barrier_t_ to an instance. This is a change to the ABI + * and will require a major version number increment. + */ + ptw32_mcs_lock_release(&node); (void) free (b); return 0; } - (void) sem_init (&(b->semBarrierBreeched[0]), b->pshared, 0); + else + { + /* + * This should not ever be reached. + * Restore the barrier to working condition before returning. + */ + (void) sem_init (&(b->semBarrierBreeched), b->pshared, 0); + } + + if (result != 0) + { + /* + * The barrier still exists and is valid + * in the event of any error above. + */ + result = EBUSY; + } } - *barrier = b; + ptw32_mcs_lock_release(&node); return (result); } diff --git a/pthread_barrier_init.c b/pthread_barrier_init.c index dc1b50c..618bfae 100644 --- a/pthread_barrier_init.c +++ b/pthread_barrier_init.c @@ -55,25 +55,13 @@ pthread_barrier_init (pthread_barrier_t * barrier, ? (*attr)->pshared : PTHREAD_PROCESS_PRIVATE); b->nCurrentBarrierHeight = b->nInitialBarrierHeight = count; - b->iStep = 0; + b->lock = 0; - /* - * Two semaphores are used in the same way as two stepping - * stones might be used in crossing a stream. Once all - * threads are safely on one stone, the other stone can - * be moved ahead, and the threads can start moving to it. - * If some threads decide to eat their lunch before moving - * then the other threads have to wait. - */ - if (0 == sem_init (&(b->semBarrierBreeched[0]), b->pshared, 0)) - { - if (0 == sem_init (&(b->semBarrierBreeched[1]), b->pshared, 0)) + if (0 == sem_init (&(b->semBarrierBreeched), b->pshared, 0)) { *barrier = b; return 0; } - (void) sem_destroy (&(b->semBarrierBreeched[0])); - } (void) free (b); } diff --git a/pthread_barrier_wait.c b/pthread_barrier_wait.c index 01ae297..182b779 100644 --- a/pthread_barrier_wait.c +++ b/pthread_barrier_wait.c @@ -42,57 +42,62 @@ int pthread_barrier_wait (pthread_barrier_t * barrier) { int result; - int step; pthread_barrier_t b; + ptw32_mcs_local_node_t node; + if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID) { return EINVAL; } - b = *barrier; - step = b->iStep; + ptw32_mcs_lock_acquire((ptw32_mcs_lock_t *)&(*barrier)->lock, &node); - if (0 == InterlockedDecrement ((long *) &(b->nCurrentBarrierHeight))) + b = *barrier; + if (--b->nCurrentBarrierHeight == 0) { - /* Must be done before posting the semaphore. */ - b->nCurrentBarrierHeight = b->nInitialBarrierHeight; + /* + * We are the last thread to arrive at the barrier before it releases us. + * Move our MCS local node to the global scope barrier handle so that the + * last thread out (not necessarily us) can release the lock. + */ + ptw32_mcs_node_substitute(&b->proxynode, &node); /* - * There is no race condition between the semaphore wait and post - * because we are using two alternating semas and all threads have - * entered barrier_wait and checked nCurrentBarrierHeight before this - * barrier's sema can be posted. Any threads that have not quite - * entered sem_wait below when the multiple_post has completed - * will nevertheless continue through the semaphore (barrier) - * and will not be left stranded. + * Any threads that have not quite entered sem_wait below when the + * multiple_post has completed will nevertheless continue through + * the semaphore (barrier). */ result = (b->nInitialBarrierHeight > 1 - ? sem_post_multiple (&(b->semBarrierBreeched[step]), + ? sem_post_multiple (&(b->semBarrierBreeched), b->nInitialBarrierHeight - 1) : 0); } else { + ptw32_mcs_lock_release(&node); /* * Use the non-cancelable version of sem_wait(). + * + * It is possible that all nInitialBarrierHeight-1 threads are + * at this point when the last thread enters the barrier, resets + * nCurrentBarrierHeight = nInitialBarrierHeight and leaves. + * If pthread_barrier_destroy is called at that moment then the + * barrier will be destroyed along with the semas. */ - result = ptw32_semwait (&(b->semBarrierBreeched[step])); + result = ptw32_semwait (&(b->semBarrierBreeched)); } + if ((PTW32_INTERLOCKED_LONG)InterlockedIncrement((LPLONG)&b->nCurrentBarrierHeight) + == (PTW32_INTERLOCKED_LONG)b->nInitialBarrierHeight) + { /* - * The first thread across will be the PTHREAD_BARRIER_SERIAL_THREAD. - * This also sets up the alternate semaphore as the next barrier. + * We are the last thread to cross this barrier */ + ptw32_mcs_lock_release(&b->proxynode); if (0 == result) { - 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); + result = PTHREAD_BARRIER_SERIAL_THREAD; + } } return (result); diff --git a/pthread_cancel.c b/pthread_cancel.c index 7d519ee..de2c5a9 100644 --- a/pthread_cancel.c +++ b/pthread_cancel.c @@ -113,16 +113,8 @@ pthread_cancel (pthread_t thread) }; /* - * FIXME!! - * - * Can a thread cancel itself? - * - * The standard doesn't - * specify an error to be returned if the target - * thread is itself. - * - * If it may, then we need to ensure that a thread can't - * deadlock itself trying to cancel itself asyncronously + * For self cancellation we need to ensure that a thread can't + * deadlock itself trying to cancel itself asynchronously * (pthread_cancel is required to be an async-cancel * safe function). */ diff --git a/pthread_getspecific.c b/pthread_getspecific.c index b05ff41..5ee1641 100644 --- a/pthread_getspecific.c +++ b/pthread_getspecific.c @@ -72,12 +72,15 @@ pthread_getspecific (pthread_key_t key) else { int lasterror = GetLastError (); +#if defined(RETAIN_WSALASTERROR) int lastWSAerror = WSAGetLastError (); - +#endif ptr = TlsGetValue (key->key); SetLastError (lasterror); +#if defined(RETAIN_WSALASTERROR) WSASetLastError (lastWSAerror); +#endif } return ptr; diff --git a/pthread_join.c b/pthread_join.c index 3804327..72a3a74 100644 --- a/pthread_join.c +++ b/pthread_join.c @@ -104,7 +104,7 @@ pthread_join (pthread_t thread, void **value_ptr) LeaveCriticalSection (&ptw32_thread_reuse_lock); - if (0 == result) + if (result == 0) { /* * The target thread is joinable and can't be reused before we join it. @@ -124,7 +124,7 @@ pthread_join (pthread_t thread, void **value_ptr) /* * Pthread_join is a cancelation point. * If we are canceled then our target thread must not be - * detached (destroyed) by us. This is guarranteed because + * detached (destroyed). This is guarranteed because * pthreadCancelableWait will not return if we * are canceled. */ diff --git a/pthread_once.c b/pthread_once.c index 96d45f2..4da54fc 100644 --- a/pthread_once.c +++ b/pthread_once.c @@ -41,7 +41,7 @@ static void PTW32_CDECL ptw32_once_on_init_cancel (void * arg) { - /* when the initting thread is cancelled we have to release the lock */ + /* when the initing thread is cancelled we have to release the lock */ ptw32_mcs_local_node_t *node = (ptw32_mcs_local_node_t *)arg; ptw32_mcs_lock_release(node); } diff --git a/pthread_win32_attach_detach_np.c b/pthread_win32_attach_detach_np.c index 5bbd925..7911fe1 100644 --- a/pthread_win32_attach_detach_np.c +++ b/pthread_win32_attach_detach_np.c @@ -283,7 +283,7 @@ pthread_win32_thread_detach_np () */ ptw32_thread_t * sp = (ptw32_thread_t *) pthread_getspecific (ptw32_selfThreadKey); - if (sp != NULL) + if (sp != NULL) // otherwise Win32 thread with no implicit POSIX handle. { ptw32_callUserDestroyRoutines (sp->ptHandle); @@ -291,16 +291,10 @@ pthread_win32_thread_detach_np () sp->state = PThreadStateLast; /* * If the thread is joinable at this point then it MUST be joined - * or detached explicitly by the application because it's - * detachState cannot be changed from this point on. + * or detached explicitly by the application. */ (void) pthread_mutex_unlock (&sp->cancelLock); - /* - * No race condition here because detachState will not be changed - * elsewhere now that thread state is PThreadStateLast (set above - * behind mutex). - */ if (sp->detachState == PTHREAD_CREATE_DETACHED) { ptw32_threadDestroy (sp->ptHandle); diff --git a/ptw32_MCS_lock.c b/ptw32_MCS_lock.c index 6b797c5..80e0f5a 100644 --- a/ptw32_MCS_lock.c +++ b/ptw32_MCS_lock.c @@ -89,8 +89,8 @@ * } */ -#include "pthread.h" #include "implement.h" +#include "pthread.h" /* * ptw32_mcs_flag_set -- notify another thread about an event. @@ -159,8 +159,8 @@ ptw32_mcs_lock_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node) node->next = 0; /* initially, no successor */ /* queue for the lock */ - pred = (ptw32_mcs_local_node_t *)PTW32_INTERLOCKED_EXCHANGE((LPLONG)lock, - (LONG)node); + pred = (ptw32_mcs_local_node_t *)PTW32_INTERLOCKED_EXCHANGE((PTW32_INTERLOCKED_LPLONG)lock, + (PTW32_INTERLOCKED_LONG)node); if (0 != pred) { @@ -184,7 +184,8 @@ ptw32_mcs_lock_release (ptw32_mcs_local_node_t * node) { ptw32_mcs_lock_t *lock = node->lock; ptw32_mcs_local_node_t *next = (ptw32_mcs_local_node_t *) - InterlockedExchangeAdd((LPLONG)&node->next, 0); /* MBR fence */ + InterlockedExchangeAdd((LPLONG)&node->next, + (LONG)0); /* MBR fence */ if (0 == next) { @@ -208,3 +209,55 @@ ptw32_mcs_lock_release (ptw32_mcs_local_node_t * node) /* pass the lock */ ptw32_mcs_flag_set(&next->readyFlag); } + +/* + * ptw32_mcs_lock_try_acquire + */ +INLINE int +ptw32_mcs_lock_try_acquire (ptw32_mcs_lock_t * lock, ptw32_mcs_local_node_t * node) +{ + node->lock = lock; + node->nextFlag = 0; + node->readyFlag = 0; + node->next = 0; /* initially, no successor */ + + return ((PTW32_INTERLOCKED_LPLONG)PTW32_INTERLOCKED_COMPARE_EXCHANGE( + (PTW32_INTERLOCKED_LPLONG)lock, + (PTW32_INTERLOCKED_LONG)node, + (PTW32_INTERLOCKED_LONG)0) + == (PTW32_INTERLOCKED_LPLONG)0) ? 0 : EBUSY; +} + +/* + * ptw32_mcs_node_substitute -- move an MCS lock local node, usually from thread + * space to, for example, global space so that another thread can release + * the lock on behalf of the current lock owner. + * + * Example: used in pthread_barrier_wait where we want the last thread out of + * the barrier to release the lock owned by the last thread to enter the barrier + * (the one that releases all threads but not necessarily the last to leave). + * + * Should only be called by the thread that has the lock. + */ +INLINE void +ptw32_mcs_node_substitute (ptw32_mcs_local_node_t * new_node, ptw32_mcs_local_node_t * old_node) +{ + new_node->lock = old_node->lock; + new_node->nextFlag = 0; /* Not needed - used only in initial Acquire */ + new_node->readyFlag = 0; /* Not needed - we were waiting on this */ + new_node->next = 0; + + if ((ptw32_mcs_local_node_t *)PTW32_INTERLOCKED_COMPARE_EXCHANGE((PTW32_INTERLOCKED_LPLONG)new_node->lock, + (PTW32_INTERLOCKED_LONG)new_node, + (PTW32_INTERLOCKED_LONG)old_node) != old_node) + { + /* + * A successor has queued after us, so wait for them to link to us + */ + while (old_node->next == 0) + { + Sleep(0); + } + new_node->next = old_node->next; + } +} @@ -71,12 +71,12 @@ #endif /* - * When building the DLL code, you should define PTW32_BUILD so that - * the variables/functions are exported correctly. When using the DLL, + * When building the library, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the library, * do NOT define PTW32_BUILD, and then the variables/functions will * be imported correctly. */ -#ifndef PTW32_STATIC_LIB +#if !defined(PTW32_STATIC_LIB) # ifdef PTW32_BUILD # define PTW32_DLLPORT __declspec (dllexport) # else diff --git a/semaphore.h b/semaphore.h index a3330a6..52438c7 100644 --- a/semaphore.h +++ b/semaphore.h @@ -70,12 +70,12 @@ #endif /* - * When building the DLL code, you should define PTW32_BUILD so that - * the variables/functions are exported correctly. When using the DLL, + * When building the library, you should define PTW32_BUILD so that + * the variables/functions are exported correctly. When using the library, * do NOT define PTW32_BUILD, and then the variables/functions will * be imported correctly. */ -#ifndef PTW32_STATIC_LIB +#if !defined(PTW32_STATIC_LIB) # ifdef PTW32_BUILD # define PTW32_DLLPORT __declspec (dllexport) # else diff --git a/tests/Bmakefile b/tests/Bmakefile index 9a2c2b4..169f42c 100644 --- a/tests/Bmakefile +++ b/tests/Bmakefile @@ -40,6 +40,7 @@ MKDIR = mkdir TOUCH = echo Passed > ECHO = @echo +# The next path is relative to $BUILD_DIR QAPC = ..\QueueUserAPCEx\User\quserex.dll CPHDR = pthread.h semaphore.h sched.h @@ -96,7 +97,7 @@ PASSES= loadfree.pass \ self2.pass \ cancel1.pass cancel2.pass \ semaphore4.pass semaphore4t.pass semaphore5.pass \ - barrier1.pass barrier2.pass barrier3.pass barrier4.pass barrier5.pass \ + barrier1.pass barrier2.pass barrier3.pass barrier4.pass barrier5.pass barrier6.pass \ tsd1.pass tsd2.pass delay1.pass delay2.pass eyal1.pass \ condvar3.pass condvar3_1.pass condvar3_2.pass condvar3_3.pass \ condvar4.pass condvar5.pass condvar6.pass \ @@ -114,7 +115,8 @@ PASSES= loadfree.pass \ cancel9.pass create3.pass stress1.pass BENCHRESULTS = \ - benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench + benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench \ + benchtest6.bench help: @ $(ECHO) Run one of the following command lines: @@ -191,7 +193,7 @@ BCX-bench: @ $(CC) /P $(EHFLAGS) $(CFLAGS) $(INCLUDES) $< $(COPYFILES): - @ $(ECHO) Copying $@ + @ $(ECHO) Copying $(BUILD_DIR)\$@ @ $(CP) $(BUILD_DIR)\$@ . pthread.dll: $(CPDLL) @@ -221,11 +223,13 @@ benchtest2.bench: benchtest3.bench: benchtest4.bench: benchtest5.bench: +benchtest6.bench: barrier1.pass: semaphore4.pass barrier2.pass: barrier1.pass barrier3.pass: barrier2.pass barrier4.pass: barrier3.pass barrier5.pass: barrier4.pass +barrier6.pass: barrier5.pass cancel1.pass: create1.pass cancel2.pass: cancel1.pass cancel3.pass: context1.pass diff --git a/tests/ChangeLog b/tests/ChangeLog index 30f6ccb..1bdcc43 100644 --- a/tests/ChangeLog +++ b/tests/ChangeLog @@ -1,3 +1,22 @@ +2010-06-19 Ross Johnson <Ross dot Johnson at homemail dot com dot au> + + * Makefile (STATICRESULTS): Add all tests into suite for static + library. + * GNUmakefile (STATICTESTS): Likewise, except for openmp1.c which + has a DLL dependency. + +2010-02-04 Ross Johnson <Ross dot Johnson at homemail dot com dot au> + + * openmp1.c: New; for libgomp compatibility (OpenMP). + * barrier5.c: Rewrite after changes to barriers. + * barrier6.c: New. + * benchtest6.c: New; timing barriers. + * GNUMakefile: Update for new tests. + * Makefile: Ditto. + * BMakefile: Ditto. + * once3.c: Improve cancelation testing. + * stress1.c: Fix comment. + 2007-01-04 Ross Johnson <Ross dot Johnson at homemail dot com dot au> * context1.c: Include context.h from library sources and remove diff --git a/tests/GNUmakefile b/tests/GNUmakefile index 1762b6c..c431f2f 100644 --- a/tests/GNUmakefile +++ b/tests/GNUmakefile @@ -44,15 +44,15 @@ CAT = cat MKDIR = mkdir TOUCH = echo Passed > ECHO = @echo -MAKE = make +MAKE = make -k # # Mingw32 # XXCFLAGS = -XXLIBS = -lws2_32 +XXLIBS = -lws2_32 -lgomp #CFLAGS = -O3 -UNDEBUG -Wall $(XXCFLAGS) -CFLAGS = -g -UNDEBUG -Wall $(XXCFLAGS) +CFLAGS = -O3 -fopenmp -UNDEBUG -Wall $(XXCFLAGS) BUILD_DIR = .. INCLUDES = -I. @@ -67,6 +67,7 @@ GCX = $(TEST)$(DLL_VER) HDR = pthread.h semaphore.h sched.h LIB = libpthread$(GCX).a DLL = pthread$(GCX).dll +# The next path is relative to $BUILD_DIR QAPC = ../QueueUserAPCEx/User/quserex.dll COPYFILES = $(HDR) $(LIB) $(DLL) $(QAPC) @@ -74,7 +75,8 @@ COPYFILES = $(HDR) $(LIB) $(DLL) $(QAPC) # If a test case returns a non-zero exit code to the shell, make will # stop. -TESTS = sizes loadfree \ +TESTS = \ + sizes loadfree \ self1 mutex5 mutex1 mutex1e mutex1n mutex1r \ semaphore1 semaphore2 semaphore3 \ condvar1 condvar1_1 condvar1_2 condvar2 condvar2_1 exit1 \ @@ -90,8 +92,8 @@ TESTS = sizes loadfree \ once1 once2 once3 once4 self2 \ cancel1 cancel2 \ semaphore4 semaphore4t semaphore5 \ - barrier1 barrier2 barrier3 barrier4 barrier5 \ - tsd1 tsd2 delay1 delay2 eyal1 \ + barrier1 barrier2 barrier3 barrier4 barrier5 barrier6 \ + tsd1 tsd2 openmp1 delay1 delay2 eyal1 \ condvar3 condvar3_1 condvar3_2 condvar3_3 \ condvar4 condvar5 condvar6 condvar7 condvar8 condvar9 \ errno1 \ @@ -109,10 +111,39 @@ STRESSTESTS = \ stress1 BENCHTESTS = \ - benchtest1 benchtest2 benchtest3 benchtest4 benchtest5 + benchtest1 benchtest2 benchtest3 benchtest4 benchtest5 benchtest6 STATICTESTS = \ - self1 + sizes \ + self1 mutex5 mutex1 mutex1e mutex1n mutex1r \ + semaphore1 semaphore2 semaphore3 \ + condvar1 condvar1_1 condvar1_2 condvar2 condvar2_1 exit1 \ + create1 create2 reuse1 reuse2 equal1 \ + kill1 valid1 valid2 \ + exit2 exit3 exit4 exit5 \ + join0 join1 detach1 join2 join3 \ + mutex2 mutex2r mutex2e mutex3 mutex3r mutex3e \ + mutex4 mutex6 mutex6n mutex6e mutex6r \ + mutex6s mutex6es mutex6rs \ + mutex7 mutex7n mutex7e mutex7r mutex8 mutex8n mutex8e mutex8r \ + count1 \ + once1 once2 once3 once4 self2 \ + cancel1 cancel2 \ + semaphore4 semaphore4t semaphore5 \ + barrier1 barrier2 barrier3 barrier4 barrier5 barrier6 \ + tsd1 tsd2 delay1 delay2 eyal1 \ + condvar3 condvar3_1 condvar3_2 condvar3_3 \ + condvar4 condvar5 condvar6 condvar7 condvar8 condvar9 \ + errno1 \ + rwlock1 rwlock2 rwlock3 rwlock4 rwlock5 rwlock6 rwlock7 rwlock8 \ + rwlock2_t rwlock3_t rwlock4_t rwlock5_t rwlock6_t rwlock6_t2 \ + context1 cancel3 cancel4 cancel5 cancel6a cancel6d \ + cancel7 cancel8 \ + cleanup0 cleanup1 cleanup2 cleanup3 \ + priority1 priority2 inherit1 \ + spin1 spin2 spin3 spin4 \ + exception1 exception2 exception3 \ + cancel9 create3 stress1 PASSES = $(TESTS:%=%.pass) BENCHRESULTS = $(BENCHTESTS:%=%.bench) @@ -184,12 +215,14 @@ benchtest2.bench: benchtest3.bench: benchtest4.bench: benchtest5.bench: +benchtest6.bench: barrier1.pass: semaphore4.pass barrier2.pass: barrier1.pass barrier3.pass: barrier2.pass barrier4.pass: barrier3.pass barrier5.pass: barrier4.pass +barrier6.pass: barrier5.pass cancel1.pass: create1.pass cancel2.pass: cancel1.pass cancel2_1.pass: cancel2.pass @@ -277,6 +310,7 @@ once1.pass: create1.pass once2.pass: once1.pass once3.pass: once2.pass once4.pass: once3.pass +openmp1.pass: tsd2.pass priority1.pass: join1.pass priority2.pass: priority1.pass barrier3.pass reuse1.pass: create2.pass @@ -345,7 +379,7 @@ sizes.pass: sizes.exe @ $(CC) -S $(CFLAGS) -o $@ $< $(INCLUDES) $(COPYFILES): - @ $(ECHO) Copying $@ + @ $(ECHO) Copying $(BUILD_DIR)/$@ @ $(CP) $(BUILD_DIR)/$@ . benchlib.o: benchlib.c @@ -366,6 +400,7 @@ clean: - $(RM) *.e - $(RM) *.i - $(RM) *.o + - $(RM) *.so - $(RM) *.obj - $(RM) *.pdb - $(RM) *.exe diff --git a/tests/Makefile b/tests/Makefile index 57fd2f4..c3342be 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -40,6 +40,7 @@ MKDIR = mkdir TOUCH = echo Passed > ECHO = @echo +# The next path is relative to $BUILD_DIR QAPC = ..\QueueUserAPCEx\User\quserex.dll CPHDR = pthread.h semaphore.h sched.h @@ -67,7 +68,7 @@ VCXFLAGS = /GX /TP /D__CLEANUP_C CPLIB = $(VCLIB) CPDLL = $(VCDLL) -CFLAGS= $(OPTIM) /W3 /WX /MD /nologo /Zi +CFLAGS= $(OPTIM) /W3 /WX /MD /nologo /Yd /Zi LFLAGS= /INCREMENTAL:NO INCLUDES=-I. BUILD_DIR=.. @@ -100,7 +101,7 @@ PASSES= sizes.pass loadfree.pass \ self2.pass \ cancel1.pass cancel2.pass \ semaphore4.pass semaphore4t.pass semaphore5.pass \ - barrier1.pass barrier2.pass barrier3.pass barrier4.pass barrier5.pass \ + barrier1.pass barrier2.pass barrier3.pass barrier4.pass barrier5.pass barrier6.pass \ tsd1.pass tsd2.pass delay1.pass delay2.pass eyal1.pass \ condvar3.pass condvar3_1.pass condvar3_2.pass condvar3_3.pass \ condvar4.pass condvar5.pass condvar6.pass \ @@ -119,13 +120,50 @@ PASSES= sizes.pass loadfree.pass \ cancel9.pass create3.pass stress1.pass BENCHRESULTS = \ - benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench + benchtest1.bench benchtest2.bench benchtest3.bench benchtest4.bench benchtest5.bench \ + benchtest6.bench STRESSRESULTS = \ stress1.stress STATICRESULTS = \ - self1.pass + sizes.pass \ + self1.pass mutex5.pass \ + mutex1.pass mutex1n.pass mutex1e.pass mutex1r.pass \ + semaphore1.pass semaphore2.pass semaphore3.pass \ + mutex2.pass mutex3.pass \ + mutex2r.pass mutex2e.pass mutex3r.pass mutex3e.pass \ + condvar1.pass condvar1_1.pass condvar1_2.pass condvar2.pass condvar2_1.pass \ + exit1.pass create1.pass create2.pass reuse1.pass reuse2.pass equal1.pass \ + kill1.pass valid1.pass valid2.pass \ + exit2.pass exit3.pass exit4.pass exit5.pass \ + join0.pass join1.pass detach1.pass join2.pass join3.pass \ + mutex4.pass mutex6.pass mutex6n.pass mutex6e.pass mutex6r.pass \ + mutex6s.pass mutex6es.pass mutex6rs.pass \ + mutex7.pass mutex7n.pass mutex7e.pass mutex7r.pass \ + mutex8.pass mutex8n.pass mutex8e.pass mutex8r.pass \ + count1.pass \ + once1.pass once2.pass once3.pass once4.pass \ + self2.pass \ + cancel1.pass cancel2.pass \ + semaphore4.pass semaphore4t.pass semaphore5.pass \ + barrier1.pass barrier2.pass barrier3.pass barrier4.pass barrier5.pass barrier6.pass \ + tsd1.pass tsd2.pass delay1.pass delay2.pass eyal1.pass \ + condvar3.pass condvar3_1.pass condvar3_2.pass condvar3_3.pass \ + condvar4.pass condvar5.pass condvar6.pass \ + condvar7.pass condvar8.pass condvar9.pass \ + errno1.pass \ + rwlock1.pass rwlock2.pass rwlock3.pass rwlock4.pass \ + rwlock5.pass rwlock6.pass rwlock7.pass rwlock8.pass \ + rwlock2_t.pass rwlock3_t.pass rwlock4_t.pass rwlock5_t.pass rwlock6_t.pass rwlock6_t2.pass \ + context1.pass \ + cancel3.pass cancel4.pass cancel5.pass cancel6a.pass cancel6d.pass \ + cancel7.pass cancel8.pass \ + cleanup0.pass cleanup1.pass cleanup2.pass cleanup3.pass \ + priority1.pass priority2.pass inherit1.pass \ + spin1.pass spin2.pass spin3.pass spin4.pass \ + exception1.pass exception2.pass exception3.pass \ + cancel9.pass create3.pass stress1.pass help: @ $(ECHO) Run one of the following command lines: @@ -245,7 +283,7 @@ VC-static: @ $(CC) /P $(EHFLAGS) $(CFLAGS) $(INCLUDES) $< $(COPYFILES): - @ $(ECHO) Copying $@ + @ $(ECHO) Copying $(BUILD_DIR)\$@ @ $(CP) $(BUILD_DIR)\$@ . pthread.dll: $(CPDLL) @@ -274,12 +312,14 @@ benchtest2.bench: benchtest3.bench: benchtest4.bench: benchtest5.bench: +benchtest6.bench: barrier1.pass: semaphore4.pass barrier2.pass: barrier1.pass barrier3.pass: barrier2.pass barrier4.pass: barrier3.pass barrier5.pass: barrier4.pass +barrier6.pass: barrier5.pass cancel1.pass: create1.pass cancel2.pass: cancel1.pass cancel3.pass: context1.pass diff --git a/tests/SIZES.GC b/tests/SIZES.GC index ae09a84..9252581 100755 --- a/tests/SIZES.GC +++ b/tests/SIZES.GC @@ -1,20 +1,21 @@ Sizes of pthreads-win32 structs ------------------------------- - pthread_t_ 124 + pthread_t 8
+ ptw32_thread_t 140
pthread_attr_t_ 28 - sem_t_ 4 - pthread_mutex_t_ 44 + sem_t_ 12
+ pthread_mutex_t_ 24
pthread_mutexattr_t_ 8 pthread_spinlock_t_ 8 - pthread_barrier_t_ 24 + pthread_barrier_t_ 36
pthread_barrierattr_t_ 4 pthread_key_t_ 16 pthread_cond_t_ 32 pthread_condattr_t_ 4 pthread_rwlock_t_ 28 pthread_rwlockattr_t_ 4 - pthread_once_t_ 8 + pthread_once_t_ 16
ptw32_cleanup_t 12 + ptw32_mcs_node_t_ 16
sched_param 4 ------------------------------- - diff --git a/tests/SIZES.GCE b/tests/SIZES.GCE index f36d0d2..a9ea64d 100755 --- a/tests/SIZES.GCE +++ b/tests/SIZES.GCE @@ -1,20 +1,21 @@ Sizes of pthreads-win32 structs ------------------------------- - pthread_t_ 60 + pthread_t 8
+ ptw32_thread_t 76
pthread_attr_t_ 28 - sem_t_ 4 - pthread_mutex_t_ 44 + sem_t_ 12
+ pthread_mutex_t_ 24
pthread_mutexattr_t_ 8 pthread_spinlock_t_ 8 - pthread_barrier_t_ 24 + pthread_barrier_t_ 36
pthread_barrierattr_t_ 4 pthread_key_t_ 16 pthread_cond_t_ 32 pthread_condattr_t_ 4 pthread_rwlock_t_ 28 pthread_rwlockattr_t_ 4 - pthread_once_t_ 8 + pthread_once_t_ 16
ptw32_cleanup_t 12 + ptw32_mcs_node_t_ 16
sched_param 4 ------------------------------- - diff --git a/tests/Wmakefile b/tests/Wmakefile index 83cd34b..a4b64a3 100644 --- a/tests/Wmakefile +++ b/tests/Wmakefile @@ -68,7 +68,10 @@ LFLAGS= INCLUDES= -i=. BUILD_DIR=.. -COPYFILES = $(CPHDR) $(CPLIB) $(CPDLL) +# The next path is relative to $BUILD_DIR +QAPC = ..\QueueUserAPCEx\User\quserex.dll + +COPYFILES = $(CPHDR) $(CPLIB) $(CPDLL) $(QAPC) TEST = EHFLAGS = @@ -188,7 +191,7 @@ sizes.pass: sizes.exe @ $(CC) /P $(EHFLAGS) $(CFLAGS) $(INCLUDES) $< $(COPYFILES): .SYMBOLIC - @ $(ECHO) Copying $@ + @ $(ECHO) Copying $(BUILD_DIR)\$@ @ $(CP) $(BUILD_DIR)\$@ . pthread.dll: diff --git a/tests/barrier5.c b/tests/barrier5.c index 5b598c9..851398c 100644 --- a/tests/barrier5.c +++ b/tests/barrier5.c @@ -33,47 +33,36 @@ * * -------------------------------------------------------------------------- * - * Declare a single barrier object, set up a sequence of - * barrier points to prove lockstepness, and then destroy it. - * + * Set up a series of barriers at different heights and test various numbers + * of threads accessing, especially cases where there are more threads than the + * barrier height (count), i.e. test contention when the barrier is released. */ #include "test.h" enum { - NUMTHREADS = 16, - BARRIERS = 10000 + NUMTHREADS = 15, + HEIGHT = 10, + BARRIERMULTIPLE = 1000 }; pthread_barrier_t barrier = NULL; pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER; - -int barrierReleases[BARRIERS + 1]; +LONG totalThreadCrossings; void * -func(void * barrierHeight) +func(void * crossings) { - int i; int result; int serialThreads = 0; - for (i = 1; i < BARRIERS; i++) + while ((LONG)crossings >= (LONG)InterlockedIncrement((LPLONG)&totalThreadCrossings)) { result = pthread_barrier_wait(&barrier); - assert(pthread_mutex_lock(&mx) == 0); - barrierReleases[i]++; - assert(pthread_mutex_unlock(&mx) == 0); - /* - * Confirm the correct number of releases from the previous - * barrier. We can't do the current barrier yet because there may - * still be threads waking up. - */ if (result == PTHREAD_BARRIER_SERIAL_THREAD) { serialThreads++; - assert(barrierReleases[i - 1] == (int) barrierHeight); - barrierReleases[i + 1] = 0; } else if (result != 0) { @@ -92,20 +81,23 @@ main() int i, j; int result; int serialThreadsTotal; + LONG Crossings; pthread_t t[NUMTHREADS + 1]; for (j = 1; j <= NUMTHREADS; j++) { - printf("Barrier height = %d\n", j); + int height = j<HEIGHT?j:HEIGHT; + + totalThreadCrossings = 0; + Crossings = height * BARRIERMULTIPLE; - barrierReleases[0] = j; - barrierReleases[1] = 0; + printf("Threads=%d, Barrier height=%d\n", j, height); - assert(pthread_barrier_init(&barrier, NULL, j) == 0); + assert(pthread_barrier_init(&barrier, NULL, height) == 0); for (i = 1; i <= j; i++) { - assert(pthread_create(&t[i], NULL, func, (void *) j) == 0); + assert(pthread_create(&t[i], NULL, func, (void *) Crossings) == 0); } serialThreadsTotal = 0; @@ -115,9 +107,7 @@ main() serialThreadsTotal += result; } - assert(serialThreadsTotal == BARRIERS - 1); - assert(barrierReleases[BARRIERS - 1] == j); - assert(barrierReleases[BARRIERS] == 0); + assert(serialThreadsTotal == BARRIERMULTIPLE); assert(pthread_barrier_destroy(&barrier) == 0); } diff --git a/tests/once3.c b/tests/once3.c index 51d2daa..c706f80 100644 --- a/tests/once3.c +++ b/tests/once3.c @@ -34,7 +34,7 @@ * -------------------------------------------------------------------------- * * Create several pthread_once objects and channel several threads - * through each. Make the init_routine cancelable and cancel them + * through each. Make the init_routine cancelable and cancel them with * waiters waiting. * * Depends on API functions: @@ -45,6 +45,8 @@ * pthread_once() */ +/* #define ASSERT_TRACE */ + #include "test.h" #define NUM_THREADS 100 /* Targeting each once control */ @@ -66,6 +68,7 @@ myfunc(void) { EnterCriticalSection(&numOnce.cs); numOnce.i++; + assert(numOnce.i > 0); LeaveCriticalSection(&numOnce.cs); /* Simulate slow once routine so that following threads pile up behind it */ Sleep(10); @@ -78,11 +81,11 @@ mythread(void * arg) { /* * Cancel every thread. These threads are deferred cancelable only, so - * only the thread performing the init_routine will see it (there are + * only the thread performing the once routine (my_func) will see it (there are * no other cancelation points here). The result will be that every thread - * eventually cancels only when it becomes the new initter. + * eventually cancels only when it becomes the new once thread. */ - pthread_cancel(pthread_self()); + assert(pthread_cancel(pthread_self()) == 0); assert(pthread_once(&once[(int) arg], myfunc) == 0); EnterCriticalSection(&numThreads.cs); numThreads.i++; diff --git a/tests/self1.c b/tests/self1.c index 59498d9..aa08328 100644 --- a/tests/self1.c +++ b/tests/self1.c @@ -54,7 +54,7 @@ main(int argc, char * argv[]) */ pthread_t self; -#ifdef PTW32_STATIC_LIB +#if defined(PTW32_STATIC_LIB) && !(defined(_MSC_VER) || defined(__MINGW32__)) pthread_win32_process_attach_np(); #endif @@ -62,7 +62,7 @@ main(int argc, char * argv[]) assert(self.p != NULL); -#ifdef PTW32_STATIC_LIB +#if defined(PTW32_STATIC_LIB) && !(defined(_MSC_VER) || defined(__MINGW32__)) pthread_win32_process_detach_np(); #endif return 0; diff --git a/tests/stress1.c b/tests/stress1.c index efaf445..3588361 100644 --- a/tests/stress1.c +++ b/tests/stress1.c @@ -54,7 +54,7 @@ * - Master and slave do battle continuously until main tells them to stop. * - Afterwards, the CV must be successfully destroyed (will return an * error if there are waiters (including any internal semaphore waiters, - * which, if there are, cannot not be real waiters). + * which, if there are, cannot be real waiters). * * Environment: * - @@ -97,7 +97,7 @@ static int timeoutCount = 0; static int signalsTakenCount = 0; static int signalsSent = 0; static int bias = 0; -static int timeout = 10;
// Must be > 0 +static int timeout = 10; // Must be > 0 enum { CTL_STOP = -1 diff --git a/tests/test.h b/tests/test.h index 3132c69..56fa335 100644 --- a/tests/test.h +++ b/tests/test.h @@ -56,7 +56,7 @@ #endif -char * error_string[] = { +const char * error_string[] = { "ZERO_or_EOK", "EPERM", "ENOFILE_or_ENOENT", @@ -99,7 +99,7 @@ char * error_string[] = { "ENOLCK", "ENOSYS", "ENOTEMPTY", - "EILSEQ", + "EILSEQ" }; /* |