summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--pthread_once.c577
-rw-r--r--tests/Bmakefile3
-rw-r--r--tests/ChangeLog4
-rw-r--r--tests/GNUmakefile697
-rw-r--r--tests/README.benchtests194
-rw-r--r--tests/Wmakefile7
-rw-r--r--tests/once4.c186
8 files changed, 991 insertions, 683 deletions
diff --git a/ChangeLog b/ChangeLog
index e0c746b..e50b60a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2005-04-11 Ross Johnson <ross at callisto.canberra.edu.au>
+
+ * pthread_once.c (pthread_once): Added priority boosting to
+ solve starvation problem after once_routine cancellation.
+ See notes in file.
+
2005-04-06 Kevin Lussier <Kevin at codegreennetworks.com>
* Makefile: Added debug targets for all versions of the library.
diff --git a/pthread_once.c b/pthread_once.c
index 1c0e01f..0a8c45e 100644
--- a/pthread_once.c
+++ b/pthread_once.c
@@ -1,234 +1,343 @@
-/*
- * pthread_once.c
- *
- * Description:
- * This translation unit implements miscellaneous thread functions.
- *
- * --------------------------------------------------------------------------
- *
- * Pthreads-win32 - POSIX Threads Library for Win32
- * Copyright(C) 1998 John E. Bossom
- * Copyright(C) 1999,2005 Pthreads-win32 contributors
- *
- * Contact Email: rpj@callisto.canberra.edu.au
- *
- * The current list of contributors is contained
- * in the file CONTRIBUTORS included with the source
- * code distribution. The list can also be seen at the
- * following World Wide Web location:
- * http://sources.redhat.com/pthreads-win32/contributors.html
- *
- * 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 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 in the file COPYING.LIB;
- * if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#include "pthread.h"
-#include "implement.h"
-
-
-static void
-ptw32_once_init_routine_cleanup(void * arg)
-{
- pthread_once_t * once_control = (pthread_once_t *) arg;
-
- (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state, (LONG)PTW32_ONCE_CANCELLED);
- (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->started, (LONG)PTW32_FALSE);
-
- EnterCriticalSection(&ptw32_once_event_lock);
- if (once_control->event)
- {
- /*
- * There are waiters, wake some up
- * We're deliberately not using PulseEvent. It's iffy, and deprecated.
- */
- SetEvent(once_control->event);
- }
- LeaveCriticalSection(&ptw32_once_event_lock);
-}
-
-
-int
-pthread_once (pthread_once_t * once_control, void (*init_routine) (void))
- /*
- * ------------------------------------------------------
- * DOCPUBLIC
- * If any thread in a process with a once_control parameter
- * makes a call to pthread_once(), the first call will summon
- * the init_routine(), but subsequent calls will not. The
- * once_control parameter determines whether the associated
- * initialization routine has been called. The init_routine()
- * is complete upon return of pthread_once().
- * This function guarantees that one and only one thread
- * executes the initialization routine, init_routine when
- * access is controlled by the pthread_once_t control
- * key.
- *
- * pthread_once() is not a cancelation point, but the init_routine
- * can be. If it's cancelled then the effect on the once_control is
- * as if pthread_once had never been entered.
- *
- *
- * PARAMETERS
- * once_control
- * pointer to an instance of pthread_once_t
- *
- * init_routine
- * pointer to an initialization routine
- *
- *
- * DESCRIPTION
- * See above.
- *
- * RESULTS
- * 0 success,
- * EINVAL once_control or init_routine is NULL
- *
- * ------------------------------------------------------
- */
-{
- int result;
-
- if (once_control == NULL || init_routine == NULL)
- {
-
- result = EINVAL;
- goto FAIL0;
-
- }
- else
- {
- result = 0;
- }
-
- while (!(InterlockedExchangeAdd((LPLONG)&once_control->state, 0L) /* Atomic Read */
- & (LONG)PTW32_ONCE_DONE))
- {
- if (!PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->started, (LONG)PTW32_TRUE))
- {
- /*
- * Clear residual state from a cancelled init_routine
- * (and DONE still hasn't been set of course).
- */
- if (PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state, (LONG)PTW32_ONCE_CLEAR)
- & PTW32_ONCE_CANCELLED)
- {
- /*
- * The previous initter was cancelled.
- * We now have a new initter (us) and we need to make the rest wait again.
- */
- EnterCriticalSection(&ptw32_once_event_lock);
- if (once_control->event)
- {
- ResetEvent(once_control->event);
- }
- LeaveCriticalSection(&ptw32_once_event_lock);
-
- /*
- * Any threads entering the wait section and getting out again before
- * the CANCELLED state can be cleared and the event is reset will, at worst, just go
- * around again or, if they suspend and we (the initter) completes before they resume,
- * they will see state == DONE and leave immediately.
- */
- }
-
- pthread_cleanup_push(ptw32_once_init_routine_cleanup, (void *) once_control);
- (*init_routine)();
- pthread_cleanup_pop(0);
-
- (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state, (LONG)PTW32_ONCE_DONE);
-
- /*
- * we didn't create the event.
- * it is only there if there is someone waiting
- */
- EnterCriticalSection(&ptw32_once_event_lock);
- if (once_control->event)
- {
- SetEvent(once_control->event);
- }
- LeaveCriticalSection(&ptw32_once_event_lock);
- }
- else
- {
- /*
- * wait for init.
- * while waiting, create an event to wait on
- */
-
- EnterCriticalSection(&ptw32_once_event_lock);
- once_control->eventUsers++;
-
- /*
- * RE CANCELLATION:
- * If we are the first thread after the initter thread, and the init_routine is cancelled
- * while we're suspended at this point in the code:-
- * - state will not get set to PTW32_ONCE_DONE;
- * - cleanup will not see an event and cannot set it;
- * - therefore, we will eventually resume, create an event and wait on it, maybe forever;
- * Remedy: cleanup must set state == CANCELLED before checking for an event, so that
- * we will see it and avoid waiting (as for state == DONE). We will go around again and
- * we may become the initter.
- * If we are still the only other thread when we get to the end of this block, we will
- * have closed the event (good). If another thread beats us to be initter, then we will
- * re-enter here (good). In case the old event is reused, the event is always reset by
- * the new initter after clearing the CANCELLED state, causing any threads that are
- * cycling around the loop to wait again.
- */
-
- if (!once_control->event)
- {
- once_control->event = CreateEvent(NULL, PTW32_TRUE, PTW32_FALSE, NULL);
- }
- LeaveCriticalSection(&ptw32_once_event_lock);
-
- /*
- * check 'state' again in case the initting thread has finished or cancelled
- * and left before seeing that there was an event to trigger.
- * (Now that the event IS created, if init gets finished AFTER this,
- * then the event handle is guaranteed to be seen and triggered).
- */
-
- if (!InterlockedExchangeAdd((LPLONG)&once_control->state, 0L)) /* Atomic Read */
- {
- /* Neither DONE nor CANCELLED */
- (void) WaitForSingleObject(once_control->event, INFINITE);
- }
-
- /* last one out shut off the lights */
- EnterCriticalSection(&ptw32_once_event_lock);
- if (0 == --once_control->eventUsers)
- {
- /* we were last */
- CloseHandle(once_control->event);
- once_control->event = 0;
- }
- LeaveCriticalSection(&ptw32_once_event_lock);
- }
- }
-
-
- /*
- * Fall through Intentionally
- */
-
- /*
- * ------------
- * Failure Code
- * ------------
- */
-FAIL0:
- return (result);
-
-} /* pthread_once */
+/*
+ * pthread_once.c
+ *
+ * Description:
+ * This translation unit implements miscellaneous thread functions.
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Pthreads-win32 - POSIX Threads Library for Win32
+ * Copyright(C) 1998 John E. Bossom
+ * Copyright(C) 1999,2005 Pthreads-win32 contributors
+ *
+ * Contact Email: rpj@callisto.canberra.edu.au
+ *
+ * The current list of contributors is contained
+ * in the file CONTRIBUTORS included with the source
+ * code distribution. The list can also be seen at the
+ * following World Wide Web location:
+ * http://sources.redhat.com/pthreads-win32/contributors.html
+ *
+ * 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 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 in the file COPYING.LIB;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+/*
+ * NOTES:
+ * pthread_once() performs a very simple task. So why is this implementation
+ * so complicated?
+ *
+ * The original implementation WAS very simple, but it relied on Windows random
+ * priority boosting to resolve starvation problems. Windows priority boosting
+ * does not occur for realtime priority classes (levels 16 to 31).
+ *
+ * You can check back to previous versions of code in the CVS repository or
+ * search the mailing list archives for discussion.
+ *
+ * Version A
+ * ---------
+ * Waiting threads would resume and suspend again using Sleep(0) until the
+ * init_routine had completed, but a higher priority waiter could hog the CPU and
+ * starve the initter thread until Windows randomly boosted it's priority, or forever
+ * for realtime applications.
+ *
+ * Version B
+ * ---------
+ * This was fixed by introducing a per once_control manual-reset event that is
+ * created and destroyed dynamically only if there are waiters. The design did not
+ * need global critical sections. Each once_control remained independent. A waiter
+ * could be confident that if the event was not null then it did not need to create
+ * the event.
+ *
+ * Version C
+ * ---------
+ * Since a change in ABI would result from version B, it was decided to take
+ * the opportunity and make pthread_once() fully compliant with the Single Unix
+ * Specification (version 3 at the time). This required allowing the init_routine
+ * to be a cancelation point. A cancelation meant that at least some waiting threads
+ * if any had to be woken so that one might become the new initter thread.
+ * Waiters could no longer simply assume that, if the event was not null, it did
+ * not need to create an event. Some real critical sections were needed, and in the
+ * current library, a global CRITICAL_SECTION is probably more efficient than a per
+ * once_control PTHREAD_MUTEX_INITIALIZER that should be somehow destroyed on exit from
+ * pthread_once(). Also, the cancelled init thread needed to set the event, and the
+ * new init thread (the winner of the race between any newly arriving threads and
+ * waking waiters) would need to reset it again. In the meantime, threads could be
+ * happily looping around until they either suspended on the reset event, or exited
+ * because the init thread had completed. It was also once again possible for a higher
+ * priority waiter to starve the init thread.
+ *
+ * Version D
+ * ---------
+ * There were now two options considered:
+ * - use an auto-reset event; OR
+ * - add our own priority boosting.
+ *
+ * An auto-reset event would stop threads from looping ok, but it makes threads
+ * dependent on earlier threads to successfully set the event in turn when it's time
+ * to wake up, and this serialises threads unecessarily on MP systems. It also adds
+ * an extra kernel call for each waking thread. If one waiter wakes and dies (async
+ * cancelled or killed) before it can set the event, then all remaining waiters are
+ * stranded.
+ *
+ * Priority boosting is a standard method for solving priority inversion and
+ * starvation problems. Furthermore, all of the priority boost logic can
+ * be restricted to the post cancellation tracks. That is, it need not slow
+ * the normal cancel-free behaviour. Threads remain independent of other threads.
+ *
+ * The implementation below adds only a few local (to the thread) integer comparisons
+ * to the normal track through the routine and additional bus locking/cache line
+ * syncing operations have been avoided altogether in the uncontended track.
+ */
+
+#include "pthread.h"
+#include "implement.h"
+
+
+static void
+ptw32_once_init_routine_cleanup(void * arg)
+{
+ pthread_once_t * once_control = (pthread_once_t *) arg;
+
+ (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state, (LONG)PTW32_ONCE_CANCELLED);
+ (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->started, (LONG)PTW32_FALSE);
+
+ EnterCriticalSection(&ptw32_once_event_lock);
+ if (once_control->event)
+ {
+ /*
+ * There are waiters, wake some up
+ * We're deliberately not using PulseEvent. It's iffy, and deprecated.
+ */
+ SetEvent(once_control->event);
+ }
+ LeaveCriticalSection(&ptw32_once_event_lock);
+}
+
+
+int
+pthread_once (pthread_once_t * once_control, void (*init_routine) (void))
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * If any thread in a process with a once_control parameter
+ * makes a call to pthread_once(), the first call will summon
+ * the init_routine(), but subsequent calls will not. The
+ * once_control parameter determines whether the associated
+ * initialization routine has been called. The init_routine()
+ * is complete upon return of pthread_once().
+ * This function guarantees that one and only one thread
+ * executes the initialization routine, init_routine when
+ * access is controlled by the pthread_once_t control
+ * key.
+ *
+ * pthread_once() is not a cancelation point, but the init_routine
+ * can be. If it's cancelled then the effect on the once_control is
+ * as if pthread_once had never been entered.
+ *
+ *
+ * PARAMETERS
+ * once_control
+ * pointer to an instance of pthread_once_t
+ *
+ * init_routine
+ * pointer to an initialization routine
+ *
+ *
+ * DESCRIPTION
+ * See above.
+ *
+ * RESULTS
+ * 0 success,
+ * EINVAL once_control or init_routine is NULL
+ *
+ * ------------------------------------------------------
+ */
+{
+ int result;
+ LONG state;
+ pthread_t self;
+ HANDLE w32Thread = 0;
+
+ if (once_control == NULL || init_routine == NULL)
+ {
+
+ result = EINVAL;
+ goto FAIL0;
+
+ }
+ else
+ {
+ result = 0;
+ }
+
+ while (!((state = InterlockedExchangeAdd((LPLONG)&once_control->state, 0L)) /* Atomic Read */
+ & (LONG)PTW32_ONCE_DONE))
+ {
+ LONG cancelled = (state & PTW32_ONCE_CANCELLED);
+
+ if (cancelled)
+ {
+ /* Boost priority momentarily */
+ if (!w32Thread)
+ {
+ self = pthread_self();
+ w32Thread = pthread_getw32threadhandle_np(self);
+ }
+ /*
+ * Prevent pthread_setschedparam() from changing our priority while we're boosted.
+ */
+ pthread_mutex_lock(&((ptw32_thread_t *)self.p)->threadLock);
+ SetThreadPriority(w32Thread, THREAD_PRIORITY_HIGHEST);
+ }
+
+ if (!PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->started, (LONG)PTW32_TRUE))
+ {
+ /*
+ * Clear residual state from a cancelled init_routine
+ * (and DONE still hasn't been set of course).
+ */
+ if (cancelled)
+ {
+ /*
+ * The previous initter was cancelled.
+ * We now have a new initter (us) and we need to make the rest wait again.
+ * Furthermore, we're running at max priority until after we've reset the event
+ * so we will not be starved by any other threads that may now be looping
+ * around.
+ */
+ EnterCriticalSection(&ptw32_once_event_lock);
+ if (once_control->event)
+ {
+ ResetEvent(once_control->event);
+ }
+ LeaveCriticalSection(&ptw32_once_event_lock);
+
+ /*
+ * Any threads entering the wait section and getting out again before
+ * the event is reset and the CANCELLED state is cleared will, at worst,
+ * just go around again or, if they suspend and we (the initter) completes before
+ * they resume, they will see state == DONE and leave immediately.
+ */
+ PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state, (LONG)PTW32_ONCE_CLEAR);
+
+ /*
+ * Restore priority. We catch any changes to this thread's priority
+ * only if they were done through the POSIX API (i.e. pthread_setschedparam)
+ */
+ SetThreadPriority(w32Thread, ((ptw32_thread_t *)self.p)->sched_priority);
+ pthread_mutex_unlock(&((ptw32_thread_t *)self.p)->threadLock);
+ }
+
+ pthread_cleanup_push(ptw32_once_init_routine_cleanup, (void *) once_control);
+ (*init_routine)();
+ pthread_cleanup_pop(0);
+
+ (void) PTW32_INTERLOCKED_EXCHANGE((LPLONG)&once_control->state, (LONG)PTW32_ONCE_DONE);
+
+ /*
+ * we didn't create the event.
+ * it is only there if there is someone waiting
+ */
+ if (once_control->event)
+ {
+ SetEvent(once_control->event);
+ }
+ }
+ else
+ {
+ if (cancelled)
+ {
+ /*
+ * Restore priority. We catch any changes to this thread's priority
+ * only if they were done through the POSIX API (i.e. pthread_setschedparam.
+ */
+ SetThreadPriority(w32Thread, ((ptw32_thread_t *)self.p)->sched_priority);
+ pthread_mutex_unlock(&((ptw32_thread_t *)self.p)->threadLock);
+ }
+
+ /*
+ * wait for init.
+ * while waiting, create an event to wait on
+ */
+
+ EnterCriticalSection(&ptw32_once_event_lock);
+ once_control->eventUsers++;
+
+ /*
+ * RE CANCELLATION:
+ * If we are the first thread after the initter thread, and the init_routine is cancelled
+ * while we're suspended at this point in the code:-
+ * - state will not get set to PTW32_ONCE_DONE;
+ * - cleanup will not see an event and cannot set it;
+ * - therefore, we will eventually resume, create an event and wait on it;
+ * cleanup will set state == CANCELLED before checking for an event, so that
+ * we will see it and avoid waiting (as for state == DONE). We will go around again and
+ * we may then become the initter.
+ * If we are still the only other thread when we get to the end of this block, we will
+ * have closed the event (good). If another thread beats us to be initter, then we will
+ * re-enter here (good). In case the old event is reused, the event is always reset by
+ * the new initter before clearing the CANCELLED state, causing any threads that are
+ * cycling around the loop to wait again.
+ * The initter thread is guaranteed to be at equal or higher priority than any waiters
+ * so no waiters will starve the initter, which might otherwise cause us to loop
+ * forever.
+ */
+
+ if (!once_control->event)
+ {
+ once_control->event = CreateEvent(NULL, PTW32_TRUE, PTW32_FALSE, NULL);
+ }
+ LeaveCriticalSection(&ptw32_once_event_lock);
+
+ /*
+ * Check 'state' again in case the initting thread has finished or cancelled
+ * and left before seeing that there was an event to trigger.
+ * (Now that the event IS created, if init gets finished AFTER this,
+ * then the event handle is guaranteed to be seen and triggered).
+ */
+
+ if (!InterlockedExchangeAdd((LPLONG)&once_control->state, 0L)) /* Atomic Read */
+ {
+ /* Neither DONE nor CANCELLED */
+ (void) WaitForSingleObject(once_control->event, INFINITE);
+ }
+
+ /* last one out shut off the lights */
+ EnterCriticalSection(&ptw32_once_event_lock);
+ if (0 == --once_control->eventUsers)
+ {
+ /* we were last */
+ CloseHandle(once_control->event);
+ once_control->event = 0;
+ }
+ LeaveCriticalSection(&ptw32_once_event_lock);
+ }
+ }
+
+
+ /*
+ * Fall through Intentionally
+ */
+
+ /*
+ * ------------
+ * Failure Code
+ * ------------
+ */
+FAIL0:
+ return (result);
+
+} /* pthread_once */
diff --git a/tests/Bmakefile b/tests/Bmakefile
index 924ea06..6263866 100644
--- a/tests/Bmakefile
+++ b/tests/Bmakefile
@@ -91,7 +91,7 @@ PASSES= loadfree.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 tsd1.pass \
+ count1.pass once1.pass once2.pass once3.pass once4.pass tsd1.pass \
self2.pass \
cancel1.pass cancel2.pass \
semaphore4.pass semaphore4t.pass \
@@ -308,6 +308,7 @@ mutex8r.pass: mutex7r.pass
once1.pass: create1.pass
once2.pass: once1.pass
once3.pass: once2.pass
+once4.pass: once3.pass
priority1.pass: join1.pass
priority2.pass: priority1.pass barrier3.pass
reuse1.pass: create2.pass
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 4d1a832..1f0ad0d 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,7 @@
+2005-04-11 Ross Johnson <rpj@callisto.canberra.edu.au>
+
+ * once4.c: New test; tries to test priority adjustments.
+
2005-04-06 Ross Johnson <rpj@callisto.canberra.edu.au>
* cleanup0.c: Fix unguarded global variable accesses.
diff --git a/tests/GNUmakefile b/tests/GNUmakefile
index 2f3cf85..c031cd1 100644
--- a/tests/GNUmakefile
+++ b/tests/GNUmakefile
@@ -1,348 +1,349 @@
-# Makefile for the pthreads test suite.
-# If all of the .pass files can be created, the test suite has passed.
-#
-# --------------------------------------------------------------------------
-#
-# Pthreads-win32 - POSIX Threads Library for Win32
-# Copyright(C) 1998 John E. Bossom
-# Copyright(C) 1999,2005 Pthreads-win32 contributors
-#
-# Contact Email: rpj@callisto.canberra.edu.au
-#
-# The current list of contributors is contained
-# in the file CONTRIBUTORS included with the source
-# code distribution. The list can also be seen at the
-# following World Wide Web location:
-# http://sources.redhat.com/pthreads-win32/contributors.html
-#
-# 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 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 in the file COPYING.LIB;
-# if not, write to the Free Software Foundation, Inc.,
-# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
-#
-
-DLL_VER = 2
-
-CP = cp -f
-MV = mv -f
-RM = rm -f
-CAT = cat
-#CP = copy
-#MV = rename
-#RM = erase
-#CAT = type
-MKDIR = mkdir
-TOUCH = echo Passed >
-ECHO = @echo
-MAKE = make
-
-#
-# Mingw32
-#
-XXCFLAGS =
-XXLIBS = -lws2_32
-#CFLAGS = -O3 -UNDEBUG -Wall $(XXCFLAGS)
-CFLAGS = -g -UNDEBUG -Wall $(XXCFLAGS)
-BUILD_DIR = ..
-INCLUDES = -I.
-
-
-TEST = GC
-
-# Default lib version
-GCX = $(TEST)$(DLL_VER)
-
-# Files we need to run the tests
-# - paths are relative to pthreads build dir.
-HDR = pthread.h semaphore.h sched.h
-LIB = libpthread$(GCX).a
-DLL = pthread$(GCX).dll
-QAPC = ../QueueUserAPCEx/User/quserex.dll
-
-COPYFILES = $(HDR) $(LIB) $(DLL) $(QAPC)
-
-# If a test case returns a non-zero exit code to the shell, make will
-# stop.
-
-TESTS = sizes loadfree \
- 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 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 tsd1 self2 \
- cancel1 cancel2 \
- semaphore4 semaphore4t \
- 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 \
- barrier1 barrier2 barrier3 barrier4 barrier5 \
- exception1 exception2 exception3 \
- cancel9 create3
-
-BENCHTESTS = \
- benchtest1 benchtest2 benchtest3 benchtest4 benchtest5
-
-STATICTESTS = \
- self1
-
-PASSES = $(TESTS:%=%.pass)
-BENCHRESULTS = $(BENCHTESTS:%=%.bench)
-STATICRESULTS = $(STATICTESTS:%=%.pass)
-
-help:
- @ $(ECHO) "Run one of the following command lines:"
- @ $(ECHO) "make clean GC (to test using GC dll with C (no EH) applications)"
- @ $(ECHO) "make clean GCX (to test using GC dll with C++ (EH) applications)"
- @ $(ECHO) "make clean GCE (to test using GCE dll with C++ (EH) applications)"
- @ $(ECHO) "make clean GC-bench (to benchtest using GNU C dll with C cleanup code)"
- @ $(ECHO) "make clean GCE-bench (to benchtest using GNU C dll with C++ exception handling)"
- @ $(ECHO) "make clean GC-static (to test using GC static lib with C (no EH) applications)"
-
-all:
- @ $(MAKE) clean GC
- @ $(MAKE) clean GCX
- @ $(MAKE) clean GCE
-
-GC:
- $(MAKE) TEST=GC CC=gcc XXCFLAGS="-D__CLEANUP_C" all-pass
-
-GCE:
- $(MAKE) TEST=GCE CC=g++ XXCFLAGS="-mthreads -D__CLEANUP_CXX" all-pass
-
-GCX:
- $(MAKE) TEST=GC CC=g++ XXCFLAGS="-mthreads -D__CLEANUP_C" all-pass
-
-GC-bench:
- $(MAKE) TEST=GC CC=gcc XXCFLAGS="-D__CLEANUP_C" XXLIBS="benchlib.o" all-bench
-
-GCE-bench:
- $(MAKE) TEST=GCE CC=g++ XXCFLAGS="-mthreads -D__CLEANUP_CXX" XXLIBS="benchlib." all-bench
-
-GC-static:
- $(MAKE) TEST=GC CC=gcc XXCFLAGS="-D__CLEANUP_C -DPTW32_STATIC_LIB" DLL="" all-static
-
-all-pass: $(PASSES)
- @ $(ECHO) ALL TESTS PASSED! Congratulations!
-
-all-bench: $(BENCHRESULTS)
- @ $(ECHO) BENCH TESTS COMPLETED.
-
-all-static: $(STATICRESULTS)
- @ $(ECHO) ALL STATIC TESTS PASSED! Congratulations!
- @ $(ECHO) Build and test the DLL to run all tests.
- @ $(ECHO) This test only confirms that the static lib links correctly.
-
-benchtest1.bench:
-benchtest2.bench:
-benchtest3.bench:
-benchtest4.bench:
-benchtest5.bench:
-
-barrier1.pass:
-barrier2.pass: barrier1.pass
-barrier3.pass: barrier2.pass
-barrier4.pass: barrier3.pass
-barrier5.pass: barrier4.pass
-cancel1.pass: create1.pass
-cancel2.pass: cancel1.pass
-cancel2_1.pass: cancel2.pass
-cancel3.pass: context1.pass
-cancel4.pass: cancel3.pass
-cancel5.pass: cancel3.pass
-cancel6a.pass: cancel3.pass
-cancel6d.pass: cancel3.pass
-cancel7.pass: kill1.pass
-cancel8.pass: cancel7.pass
-cleanup0.pass: cancel5.pass
-cleanup1.pass: cleanup0.pass
-cleanup2.pass: cleanup1.pass
-cleanup3.pass: cleanup2.pass
-condvar1.pass:
-condvar1_1.pass: condvar1.pass
-condvar1_2.pass: join2.pass
-condvar2.pass: condvar1.pass
-condvar2_1.pass: condvar2.pass join2.pass
-condvar3.pass: create1.pass condvar2.pass
-condvar3_1.pass: condvar3.pass join2.pass
-condvar3_2.pass: condvar3_1.pass
-condvar3_3.pass: condvar3_2.pass
-condvar4.pass: create1.pass
-condvar5.pass: condvar4.pass
-condvar6.pass: condvar5.pass
-condvar7.pass: condvar6.pass cleanup1.pass
-condvar8.pass: condvar7.pass
-condvar9.pass: condvar8.pass
-context1.pass: cancel2.pass
-count1.pass: join1.pass
-create1.pass: mutex2.pass
-create2.pass: create1.pass
-create3.pass:
-delay1.pass: cancel2.pass
-delay2.pass: delay1.pass
-equal1.pass: create1.pass
-errno1.pass: mutex3.pass
-exception1.pass: cancel4.pass
-exception2.pass: exception1.pass
-exception3.pass: exception2.pass
-exit1.pass:
-exit2.pass: create1.pass
-exit3.pass: create1.pass
-exit4.pass:
-exit5.pass: exit4.pass kill1.pass
-eyal1.pass: tsd1.pass
-inherit1.pass: join1.pass priority1.pass
-join0.pass: create1.pass
-join1.pass: create1.pass
-join2.pass: create1.pass
-join3.pass: join2.pass
-kill1.pass:
-loadfree.pass: pthread.dll
-mutex1.pass: self1.pass
-mutex1n.pass: mutex1.pass
-mutex1e.pass: mutex1.pass
-mutex1r.pass: mutex1.pass
-mutex2.pass: mutex1.pass
-mutex2r.pass: mutex2.pass
-mutex2e.pass: mutex2.pass
-mutex3.pass: create1.pass
-mutex3r.pass: mutex3.pass
-mutex3e.pass: mutex3.pass
-mutex4.pass: mutex3.pass
-mutex5.pass:
-mutex6.pass: mutex4.pass
-mutex6n.pass: mutex4.pass
-mutex6e.pass: mutex4.pass
-mutex6r.pass: mutex4.pass
-mutex6s.pass: mutex6.pass
-mutex6rs.pass: mutex6r.pass
-mutex6es.pass: mutex6e.pass
-mutex7.pass: mutex6.pass
-mutex7n.pass: mutex6n.pass
-mutex7e.pass: mutex6e.pass
-mutex7r.pass: mutex6r.pass
-mutex8.pass: mutex7.pass
-mutex8n.pass: mutex7n.pass
-mutex8e.pass: mutex7e.pass
-mutex8r.pass: mutex7r.pass
-once1.pass: create1.pass
-once2.pass: once1.pass
-once3.pass: once2.pass
-priority1.pass: join1.pass
-priority2.pass: priority1.pass barrier3.pass
-reuse1.pass: create2.pass
-reuse2.pass: reuse1.pass
-rwlock1.pass: condvar6.pass
-rwlock2.pass: rwlock1.pass
-rwlock3.pass: rwlock2.pass
-rwlock4.pass: rwlock3.pass
-rwlock5.pass: rwlock4.pass
-rwlock6.pass: rwlock5.pass
-rwlock7.pass: rwlock6.pass
-rwlock8.pass: rwlock7.pass
-rwlock2_t.pass: rwlock2.pass
-rwlock3_t.pass: rwlock2_t.pass
-rwlock4_t.pass: rwlock3_t.pass
-rwlock5_t.pass: rwlock4_t.pass
-rwlock6_t.pass: rwlock5_t.pass
-rwlock6_t2.pass: rwlock6_t.pass
-self1.pass:
-self2.pass: create1.pass
-semaphore1.pass:
-semaphore2.pass:
-semaphore3.pass: semaphore2.pass
-semaphore4.pass: semaphore3.pass cancel1.pass
-semaphore4t.pass: semaphore4.pass
-sizes.pass:
-spin1.pass:
-spin2.pass: spin1.pass
-spin3.pass: spin2.pass
-spin4.pass: spin3.pass
-tsd1.pass: join1.pass
-valid1.pass: join1.pass
-valid2.pass: valid1.pass
-cancel9.pass: cancel8.pass
-
-sizes.pass: sizes.exe
- @ $(ECHO) Running $*
- $< > SIZES.$(TEST)
- @ $(CAT) SIZES.$(TEST)
- @ $(ECHO) Passed
- @ $(TOUCH) $@
-
-%.pass: %.exe
- @ $(ECHO) Running $*
- $*
- @ $(ECHO) Passed
- @ $(TOUCH) $@
-
-%.bench: $(LIB) $(DLL) $(HDR) $(QAPC) $(XXLIBS) %.exe
- @ $(ECHO) Running $*
- $*
- @ $(ECHO) Done
- @ $(TOUCH) $@
-
-%.exe: %.c $(LIB) $(DLL) $(HDR) $(QAPC)
- @ $(ECHO) Compiling $@
- @ $(ECHO) $(CC) $(CFLAGS) -o $@ $< $(INCLUDES) -L. -lpthread$(GCX) -lsupc++ $(XXLIBS)
- @ $(CC) $(CFLAGS) -o $@ $< $(INCLUDES) -L. -lpthread$(GCX) -lsupc++ $(XXLIBS)
-
-%.pre: %.c $(HDR)
- @ $(CC) -E $(CFLAGS) -o $@ $< $(INCLUDES)
-
-%.s: %.c $(HDR)
- @ $(CC) -S $(CFLAGS) -o $@ $< $(INCLUDES)
-
-$(COPYFILES):
- @ $(ECHO) Copying $@
- @ $(CP) $(BUILD_DIR)/$@ .
-
-benchlib.o: benchlib.c
- @ $(ECHO) Compiling $@
- @ $(ECHO) $(CC) -c $(CFLAGS) $< $(INCLUDES)
- @ $(CC) -c $(CFLAGS) $< $(INCLUDES)
-
-pthread.dll: $(DLL)
- @ $(CP) $(DLL) $@
-
-clean:
- - $(RM) *.dll
- - $(RM) *.lib
- - $(RM) pthread.h
- - $(RM) semaphore.h
- - $(RM) sched.h
- - $(RM) *.a
- - $(RM) *.e
- - $(RM) *.i
- - $(RM) *.o
- - $(RM) *.obj
- - $(RM) *.pdb
- - $(RM) *.exe
- - $(RM) *.pass
- - $(RM) *.bench
- - $(RM) *.static
- - $(RM) *.log
+# Makefile for the pthreads test suite.
+# If all of the .pass files can be created, the test suite has passed.
+#
+# --------------------------------------------------------------------------
+#
+# Pthreads-win32 - POSIX Threads Library for Win32
+# Copyright(C) 1998 John E. Bossom
+# Copyright(C) 1999,2005 Pthreads-win32 contributors
+#
+# Contact Email: rpj@callisto.canberra.edu.au
+#
+# The current list of contributors is contained
+# in the file CONTRIBUTORS included with the source
+# code distribution. The list can also be seen at the
+# following World Wide Web location:
+# http://sources.redhat.com/pthreads-win32/contributors.html
+#
+# 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 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 in the file COPYING.LIB;
+# if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+#
+
+DLL_VER = 2
+
+CP = cp -f
+MV = mv -f
+RM = rm -f
+CAT = cat
+#CP = copy
+#MV = rename
+#RM = erase
+#CAT = type
+MKDIR = mkdir
+TOUCH = echo Passed >
+ECHO = @echo
+MAKE = make
+
+#
+# Mingw32
+#
+XXCFLAGS =
+XXLIBS = -lws2_32
+#CFLAGS = -O3 -UNDEBUG -Wall $(XXCFLAGS)
+CFLAGS = -g -UNDEBUG -Wall $(XXCFLAGS)
+BUILD_DIR = ..
+INCLUDES = -I.
+
+
+TEST = GC
+
+# Default lib version
+GCX = $(TEST)$(DLL_VER)
+
+# Files we need to run the tests
+# - paths are relative to pthreads build dir.
+HDR = pthread.h semaphore.h sched.h
+LIB = libpthread$(GCX).a
+DLL = pthread$(GCX).dll
+QAPC = ../QueueUserAPCEx/User/quserex.dll
+
+COPYFILES = $(HDR) $(LIB) $(DLL) $(QAPC)
+
+# If a test case returns a non-zero exit code to the shell, make will
+# stop.
+
+TESTS = sizes loadfree \
+ 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 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 tsd1 self2 \
+ cancel1 cancel2 \
+ semaphore4 semaphore4t \
+ 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 \
+ barrier1 barrier2 barrier3 barrier4 barrier5 \
+ exception1 exception2 exception3 \
+ cancel9 create3
+
+BENCHTESTS = \
+ benchtest1 benchtest2 benchtest3 benchtest4 benchtest5
+
+STATICTESTS = \
+ self1
+
+PASSES = $(TESTS:%=%.pass)
+BENCHRESULTS = $(BENCHTESTS:%=%.bench)
+STATICRESULTS = $(STATICTESTS:%=%.pass)
+
+help:
+ @ $(ECHO) "Run one of the following command lines:"
+ @ $(ECHO) "make clean GC (to test using GC dll with C (no EH) applications)"
+ @ $(ECHO) "make clean GCX (to test using GC dll with C++ (EH) applications)"
+ @ $(ECHO) "make clean GCE (to test using GCE dll with C++ (EH) applications)"
+ @ $(ECHO) "make clean GC-bench (to benchtest using GNU C dll with C cleanup code)"
+ @ $(ECHO) "make clean GCE-bench (to benchtest using GNU C dll with C++ exception handling)"
+ @ $(ECHO) "make clean GC-static (to test using GC static lib with C (no EH) applications)"
+
+all:
+ @ $(MAKE) clean GC
+ @ $(MAKE) clean GCX
+ @ $(MAKE) clean GCE
+
+GC:
+ $(MAKE) TEST=GC CC=gcc XXCFLAGS="-D__CLEANUP_C" all-pass
+
+GCE:
+ $(MAKE) TEST=GCE CC=g++ XXCFLAGS="-mthreads -D__CLEANUP_CXX" all-pass
+
+GCX:
+ $(MAKE) TEST=GC CC=g++ XXCFLAGS="-mthreads -D__CLEANUP_C" all-pass
+
+GC-bench:
+ $(MAKE) TEST=GC CC=gcc XXCFLAGS="-D__CLEANUP_C" XXLIBS="benchlib.o" all-bench
+
+GCE-bench:
+ $(MAKE) TEST=GCE CC=g++ XXCFLAGS="-mthreads -D__CLEANUP_CXX" XXLIBS="benchlib." all-bench
+
+GC-static:
+ $(MAKE) TEST=GC CC=gcc XXCFLAGS="-D__CLEANUP_C -DPTW32_STATIC_LIB" DLL="" all-static
+
+all-pass: $(PASSES)
+ @ $(ECHO) ALL TESTS PASSED! Congratulations!
+
+all-bench: $(BENCHRESULTS)
+ @ $(ECHO) BENCH TESTS COMPLETED.
+
+all-static: $(STATICRESULTS)
+ @ $(ECHO) ALL STATIC TESTS PASSED! Congratulations!
+ @ $(ECHO) Build and test the DLL to run all tests.
+ @ $(ECHO) This test only confirms that the static lib links correctly.
+
+benchtest1.bench:
+benchtest2.bench:
+benchtest3.bench:
+benchtest4.bench:
+benchtest5.bench:
+
+barrier1.pass:
+barrier2.pass: barrier1.pass
+barrier3.pass: barrier2.pass
+barrier4.pass: barrier3.pass
+barrier5.pass: barrier4.pass
+cancel1.pass: create1.pass
+cancel2.pass: cancel1.pass
+cancel2_1.pass: cancel2.pass
+cancel3.pass: context1.pass
+cancel4.pass: cancel3.pass
+cancel5.pass: cancel3.pass
+cancel6a.pass: cancel3.pass
+cancel6d.pass: cancel3.pass
+cancel7.pass: kill1.pass
+cancel8.pass: cancel7.pass
+cleanup0.pass: cancel5.pass
+cleanup1.pass: cleanup0.pass
+cleanup2.pass: cleanup1.pass
+cleanup3.pass: cleanup2.pass
+condvar1.pass:
+condvar1_1.pass: condvar1.pass
+condvar1_2.pass: join2.pass
+condvar2.pass: condvar1.pass
+condvar2_1.pass: condvar2.pass join2.pass
+condvar3.pass: create1.pass condvar2.pass
+condvar3_1.pass: condvar3.pass join2.pass
+condvar3_2.pass: condvar3_1.pass
+condvar3_3.pass: condvar3_2.pass
+condvar4.pass: create1.pass
+condvar5.pass: condvar4.pass
+condvar6.pass: condvar5.pass
+condvar7.pass: condvar6.pass cleanup1.pass
+condvar8.pass: condvar7.pass
+condvar9.pass: condvar8.pass
+context1.pass: cancel2.pass
+count1.pass: join1.pass
+create1.pass: mutex2.pass
+create2.pass: create1.pass
+create3.pass:
+delay1.pass: cancel2.pass
+delay2.pass: delay1.pass
+equal1.pass: create1.pass
+errno1.pass: mutex3.pass
+exception1.pass: cancel4.pass
+exception2.pass: exception1.pass
+exception3.pass: exception2.pass
+exit1.pass:
+exit2.pass: create1.pass
+exit3.pass: create1.pass
+exit4.pass:
+exit5.pass: exit4.pass kill1.pass
+eyal1.pass: tsd1.pass
+inherit1.pass: join1.pass priority1.pass
+join0.pass: create1.pass
+join1.pass: create1.pass
+join2.pass: create1.pass
+join3.pass: join2.pass
+kill1.pass:
+loadfree.pass: pthread.dll
+mutex1.pass: self1.pass
+mutex1n.pass: mutex1.pass
+mutex1e.pass: mutex1.pass
+mutex1r.pass: mutex1.pass
+mutex2.pass: mutex1.pass
+mutex2r.pass: mutex2.pass
+mutex2e.pass: mutex2.pass
+mutex3.pass: create1.pass
+mutex3r.pass: mutex3.pass
+mutex3e.pass: mutex3.pass
+mutex4.pass: mutex3.pass
+mutex5.pass:
+mutex6.pass: mutex4.pass
+mutex6n.pass: mutex4.pass
+mutex6e.pass: mutex4.pass
+mutex6r.pass: mutex4.pass
+mutex6s.pass: mutex6.pass
+mutex6rs.pass: mutex6r.pass
+mutex6es.pass: mutex6e.pass
+mutex7.pass: mutex6.pass
+mutex7n.pass: mutex6n.pass
+mutex7e.pass: mutex6e.pass
+mutex7r.pass: mutex6r.pass
+mutex8.pass: mutex7.pass
+mutex8n.pass: mutex7n.pass
+mutex8e.pass: mutex7e.pass
+mutex8r.pass: mutex7r.pass
+once1.pass: create1.pass
+once2.pass: once1.pass
+once3.pass: once2.pass
+once4.pass: once3.pass
+priority1.pass: join1.pass
+priority2.pass: priority1.pass barrier3.pass
+reuse1.pass: create2.pass
+reuse2.pass: reuse1.pass
+rwlock1.pass: condvar6.pass
+rwlock2.pass: rwlock1.pass
+rwlock3.pass: rwlock2.pass
+rwlock4.pass: rwlock3.pass
+rwlock5.pass: rwlock4.pass
+rwlock6.pass: rwlock5.pass
+rwlock7.pass: rwlock6.pass
+rwlock8.pass: rwlock7.pass
+rwlock2_t.pass: rwlock2.pass
+rwlock3_t.pass: rwlock2_t.pass
+rwlock4_t.pass: rwlock3_t.pass
+rwlock5_t.pass: rwlock4_t.pass
+rwlock6_t.pass: rwlock5_t.pass
+rwlock6_t2.pass: rwlock6_t.pass
+self1.pass:
+self2.pass: create1.pass
+semaphore1.pass:
+semaphore2.pass:
+semaphore3.pass: semaphore2.pass
+semaphore4.pass: semaphore3.pass cancel1.pass
+semaphore4t.pass: semaphore4.pass
+sizes.pass:
+spin1.pass:
+spin2.pass: spin1.pass
+spin3.pass: spin2.pass
+spin4.pass: spin3.pass
+tsd1.pass: join1.pass
+valid1.pass: join1.pass
+valid2.pass: valid1.pass
+cancel9.pass: cancel8.pass
+
+sizes.pass: sizes.exe
+ @ $(ECHO) Running $*
+ $< > SIZES.$(TEST)
+ @ $(CAT) SIZES.$(TEST)
+ @ $(ECHO) Passed
+ @ $(TOUCH) $@
+
+%.pass: %.exe
+ @ $(ECHO) Running $*
+ $*
+ @ $(ECHO) Passed
+ @ $(TOUCH) $@
+
+%.bench: $(LIB) $(DLL) $(HDR) $(QAPC) $(XXLIBS) %.exe
+ @ $(ECHO) Running $*
+ $*
+ @ $(ECHO) Done
+ @ $(TOUCH) $@
+
+%.exe: %.c $(LIB) $(DLL) $(HDR) $(QAPC)
+ @ $(ECHO) Compiling $@
+ @ $(ECHO) $(CC) $(CFLAGS) -o $@ $< $(INCLUDES) -L. -lpthread$(GCX) -lsupc++ $(XXLIBS)
+ @ $(CC) $(CFLAGS) -o $@ $< $(INCLUDES) -L. -lpthread$(GCX) -lsupc++ $(XXLIBS)
+
+%.pre: %.c $(HDR)
+ @ $(CC) -E $(CFLAGS) -o $@ $< $(INCLUDES)
+
+%.s: %.c $(HDR)
+ @ $(CC) -S $(CFLAGS) -o $@ $< $(INCLUDES)
+
+$(COPYFILES):
+ @ $(ECHO) Copying $@
+ @ $(CP) $(BUILD_DIR)/$@ .
+
+benchlib.o: benchlib.c
+ @ $(ECHO) Compiling $@
+ @ $(ECHO) $(CC) -c $(CFLAGS) $< $(INCLUDES)
+ @ $(CC) -c $(CFLAGS) $< $(INCLUDES)
+
+pthread.dll: $(DLL)
+ @ $(CP) $(DLL) $@
+
+clean:
+ - $(RM) *.dll
+ - $(RM) *.lib
+ - $(RM) pthread.h
+ - $(RM) semaphore.h
+ - $(RM) sched.h
+ - $(RM) *.a
+ - $(RM) *.e
+ - $(RM) *.i
+ - $(RM) *.o
+ - $(RM) *.obj
+ - $(RM) *.pdb
+ - $(RM) *.exe
+ - $(RM) *.pass
+ - $(RM) *.bench
+ - $(RM) *.static
+ - $(RM) *.log
diff --git a/tests/README.benchtests b/tests/README.benchtests
index e02cb3e..01051a2 100644
--- a/tests/README.benchtests
+++ b/tests/README.benchtests
@@ -1,97 +1,97 @@
-
-------------
-Benchmarking
-------------
-There is a new but growing set a benchmarking programs in the
-"tests" directory. These should be runnable using the
-following command-lines corresponding to each of the possible
-library builds:
-
-MSVC:
-nmake clean VC-bench
-nmake clean VCE-bench
-nmake clean VSE-bench
-
-Mingw32:
-make clean GC-bench
-make clean GCE-bench
-
-UWIN:
-The benchtests are run as part of the testsuite.
-
-
-Mutex benchtests
-----------------
-
-benchtest1 - Lock plus unlock on an unlocked mutex.
-benchtest2 - Lock plus unlock on a locked mutex.
-benchtest3 - Trylock on a locked mutex.
-benchtest4 - Trylock plus unlock on an unlocked mutex.
-
-
-Each test times up to three alternate synchronisation
-implementations as a reference, and then times each of
-the four mutex types provided by the library. Each is
-described below:
-
-Simple Critical Section
-- uses a simple Win32 critical section. There is no
-additional overhead for this case as there is in the
-remaining cases.
-
-POSIX mutex implemented using a Critical Section
-- The old implementation which uses runtime adaptation
-depending on the Windows variant being run on. When
-the pthreads DLL was run on WinNT or higher then
-POSIX mutexes would use Win32 Critical Sections.
-
-POSIX mutex implemented using a Win32 Mutex
-- The old implementation which uses runtime adaptation
-depending on the Windows variant being run on. When
-the pthreads DLL was run on Win9x then POSIX mutexes
-would use Win32 Mutexes (because TryEnterCriticalSection
-is not implemented on Win9x).
-
-PTHREAD_MUTEX_DEFAULT
-PTHREAD_MUTEX_NORMAL
-PTHREAD_MUTEX_ERRORCHECK
-PTHREAD_MUTEX_RECURSIVE
-- The current implementation supports these mutex types.
-The underlying basis of POSIX mutexes is now the same
-irrespective of the Windows variant, and should therefore
-have consistent performance.
-
-
-In all benchtests, the operation is repeated a large
-number of times and an average is calculated. Loop
-overhead is measured and subtracted from all test times.
-
-Comment on the results
-----------------------
-The gain in performance for Win9x systems is enormous - up to
-40 times faster for unlocked mutexes (2 times faster for locked
-mutexes).
-
-Pthread_mutex_trylock also appears to be faster for locked mutexes.
-
-The price for the new consistency between WinNT and Win9x is
-slower performance (up to twice as long) across a lock/unlock
-sequence. It is difficult to get a good split timing for lock
-and unlock operations, but by code inspection, it is the unlock
-operation that is slowing the pair down in comparison with the
-old-style CS mutexes, even for the fast PTHREAD_MUTEX_NORMAL mutex
-type with no other waiting threads. However, comparitive
-performance for operations on already locked mutexes is very close.
-
-When this is translated to real-world applications, the overall
-camparitive performance should be almost identical on NT class
-systems. That is, applications with heavy mutex contention should
-have almost equal performance, while applications with only light
-mutex contention should also have almost equal performance because
-the most critical operation in this case is the lock operation.
-
-Overall, the newer pthreads-win32 mutex routines are only slower
-(on NT class systems) where and when it is least critical.
-
-Thanks go to Thomas Pfaff for the current implementation of mutex
-routines.
+
+------------
+Benchmarking
+------------
+There is a new but growing set a benchmarking programs in the
+"tests" directory. These should be runnable using the
+following command-lines corresponding to each of the possible
+library builds:
+
+MSVC:
+nmake clean VC-bench
+nmake clean VCE-bench
+nmake clean VSE-bench
+
+Mingw32:
+make clean GC-bench
+make clean GCE-bench
+
+UWIN:
+The benchtests are run as part of the testsuite.
+
+
+Mutex benchtests
+----------------
+
+benchtest1 - Lock plus unlock on an unlocked mutex.
+benchtest2 - Lock plus unlock on a locked mutex.
+benchtest3 - Trylock on a locked mutex.
+benchtest4 - Trylock plus unlock on an unlocked mutex.
+
+
+Each test times up to three alternate synchronisation
+implementations as a reference, and then times each of
+the four mutex types provided by the library. Each is
+described below:
+
+Simple Critical Section
+- uses a simple Win32 critical section. There is no
+additional overhead for this case as there is in the
+remaining cases.
+
+POSIX mutex implemented using a Critical Section
+- The old implementation which uses runtime adaptation
+depending on the Windows variant being run on. When
+the pthreads DLL was run on WinNT or higher then
+POSIX mutexes would use Win32 Critical Sections.
+
+POSIX mutex implemented using a Win32 Mutex
+- The old implementation which uses runtime adaptation
+depending on the Windows variant being run on. When
+the pthreads DLL was run on Win9x then POSIX mutexes
+would use Win32 Mutexes (because TryEnterCriticalSection
+is not implemented on Win9x).
+
+PTHREAD_MUTEX_DEFAULT
+PTHREAD_MUTEX_NORMAL
+PTHREAD_MUTEX_ERRORCHECK
+PTHREAD_MUTEX_RECURSIVE
+- The current implementation supports these mutex types.
+The underlying basis of POSIX mutexes is now the same
+irrespective of the Windows variant, and should therefore
+have consistent performance.
+
+
+In all benchtests, the operation is repeated a large
+number of times and an average is calculated. Loop
+overhead is measured and subtracted from all test times.
+
+Comment on the results
+----------------------
+The gain in performance for Win9x systems is enormous - up to
+40 times faster for unlocked mutexes (2 times faster for locked
+mutexes).
+
+Pthread_mutex_trylock also appears to be faster for locked mutexes.
+
+The price for the new consistency between WinNT and Win9x is
+slower performance (up to twice as long) across a lock/unlock
+sequence. It is difficult to get a good split timing for lock
+and unlock operations, but by code inspection, it is the unlock
+operation that is slowing the pair down in comparison with the
+old-style CS mutexes, even for the fast PTHREAD_MUTEX_NORMAL mutex
+type with no other waiting threads. However, comparitive
+performance for operations on already locked mutexes is very close.
+
+When this is translated to real-world applications, the overall
+camparitive performance should be almost identical on NT class
+systems. That is, applications with heavy mutex contention should
+have almost equal performance, while applications with only light
+mutex contention should also have almost equal performance because
+the most critical operation in this case is the lock operation.
+
+Overall, the newer pthreads-win32 mutex routines are only slower
+(on NT class systems) where and when it is least critical.
+
+Thanks go to Thomas Pfaff for the current implementation of mutex
+routines.
diff --git a/tests/Wmakefile b/tests/Wmakefile
index ff54700..512b2ce 100644
--- a/tests/Wmakefile
+++ b/tests/Wmakefile
@@ -31,9 +31,9 @@
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
#
-
+
DLL_VER = 2
-
+
.EXTENSIONS:
.EXTENSIONS: .pass .exe .obj .i .c
@@ -91,7 +91,7 @@ PASSES = sizes.pass loadfree.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 tsd1.pass &
+ count1.pass once1.pass once2.pass once3.pass once4.pass tsd1.pass &
self2.pass &
cancel1.pass cancel2.pass &
semaphore4.pass semaphore4t.pass &
@@ -305,6 +305,7 @@ mutex8r.pass: mutex7r.pass
once1.pass: create1.pass
once2.pass: once1.pass
once3.pass: once2.pass
+once4.pass: once3.pass
priority1.pass: join1.pass
priority2.pass: priority1.pass barrier3.pass
reuse1.pass: create2.pass
diff --git a/tests/once4.c b/tests/once4.c
new file mode 100644
index 0000000..980c9fe
--- /dev/null
+++ b/tests/once4.c
@@ -0,0 +1,186 @@
+/*
+ * once4.c
+ *
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Pthreads-win32 - POSIX Threads Library for Win32
+ * Copyright(C) 1998 John E. Bossom
+ * Copyright(C) 1999,2005 Pthreads-win32 contributors
+ *
+ * Contact Email: rpj@callisto.canberra.edu.au
+ *
+ * The current list of contributors is contained
+ * in the file CONTRIBUTORS included with the source
+ * code distribution. The list can also be seen at the
+ * following World Wide Web location:
+ * http://sources.redhat.com/pthreads-win32/contributors.html
+ *
+ * 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 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 in the file COPYING.LIB;
+ * if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ * --------------------------------------------------------------------------
+ *
+ * Create several pthread_once objects and channel several threads
+ * through each. Make the init_routine cancelable and cancel them
+ * waiters waiting. Vary the priorities.
+ *
+ * Depends on API functions:
+ * pthread_once()
+ * pthread_create()
+ * pthread_testcancel()
+ * pthread_cancel()
+ * pthread_once()
+ */
+
+#include "test.h"
+
+#define NUM_THREADS 100 /* Targeting each once control */
+#define NUM_ONCE 10
+
+pthread_once_t o = PTHREAD_ONCE_INIT;
+pthread_once_t once[NUM_ONCE];
+
+typedef struct {
+ int i;
+ CRITICAL_SECTION cs;
+} sharedInt_t;
+
+static sharedInt_t numOnce = {0, {0}};
+static sharedInt_t numThreads = {0, {0}};
+
+typedef struct {
+ int threadnum;
+ int oncenum;
+ int myPrio;
+ HANDLE w32Thread;
+} bag_t;
+
+static bag_t threadbag[NUM_THREADS][NUM_ONCE];
+
+CRITICAL_SECTION print_lock;
+
+void
+mycleanupfunc(void * arg)
+{
+ bag_t * bag = (bag_t *) arg;
+ EnterCriticalSection(&print_lock);
+ /* once thrd prio error */
+ printf("%4d %4d %4d %4d\n",
+ bag->oncenum,
+ bag->threadnum,
+ bag->myPrio,
+ bag->myPrio - GetThreadPriority(bag->w32Thread));
+ LeaveCriticalSection(&print_lock);
+}
+
+void
+myinitfunc(void)
+{
+ EnterCriticalSection(&numOnce.cs);
+ numOnce.i++;
+ LeaveCriticalSection(&numOnce.cs);
+ /* Simulate slow once routine so that following threads pile up behind it */
+ Sleep(10);
+ /* test for cancelation late so we're sure to have waiters. */
+ pthread_testcancel();
+}
+
+void *
+mythread(void * arg)
+{
+ bag_t * bag = (bag_t *) arg;
+ struct sched_param param;
+
+ /*
+ * Cancel every thread. These threads are deferred cancelable only, so
+ * only the thread performing the init_routine 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.
+ */
+ pthread_t self = pthread_self();
+ bag->w32Thread = pthread_getw32threadhandle_np(self);
+ /*
+ * Set priority between -2 and 2 inclusive.
+ */
+ bag->myPrio = (bag->threadnum % 5) - 2;
+ param.sched_priority = bag->myPrio;
+ pthread_setschedparam(self, SCHED_OTHER, &param);
+
+ /* Trigger a cancellation at the next cancellation point in this thread */
+ pthread_cancel(self);
+#if 0
+ pthread_cleanup_push(mycleanupfunc, arg);
+ assert(pthread_once(&once[bag->oncenum], myinitfunc) == 0);
+ pthread_cleanup_pop(1);
+#else
+ assert(pthread_once(&once[bag->oncenum], myinitfunc) == 0);
+#endif
+ EnterCriticalSection(&numThreads.cs);
+ numThreads.i++;
+ LeaveCriticalSection(&numThreads.cs);
+ return 0;
+}
+
+int
+main()
+{
+ pthread_t t[NUM_THREADS][NUM_ONCE];
+ int i, j;
+
+ InitializeCriticalSection(&print_lock);
+ InitializeCriticalSection(&numThreads.cs);
+ InitializeCriticalSection(&numOnce.cs);
+
+#if 0
+ /* once thrd prio change */
+ printf("once thrd prio error\n");
+#endif
+
+ /* Set main thread to lower prio than threads */
+ SetThreadPriority(GetCurrentThread(), -2);
+
+ for (j = 0; j < NUM_ONCE; j++)
+ {
+ once[j] = o;
+
+ for (i = 0; i < NUM_THREADS; i++)
+ {
+ bag_t * bag = &threadbag[i][j];
+ bag->threadnum = i;
+ bag->oncenum = j;
+ assert(pthread_create(&t[i][j], NULL, mythread, (void *) bag) == 0);
+ }
+ }
+
+ for (j = 0; j < NUM_ONCE; j++)
+ for (i = 0; i < NUM_THREADS; i++)
+ if (pthread_join(t[i][j], NULL) != 0)
+ printf("Join failed for [thread,once] = [%d,%d]\n", i, j);
+
+ /*
+ * All threads will cancel, none will return normally from
+ * pthread_once and so numThreads should never be incremented. However,
+ * numOnce should be incremented by every thread (NUM_THREADS*NUM_ONCE).
+ */
+ assert(numOnce.i == NUM_ONCE * NUM_THREADS);
+ assert(numThreads.i == 0);
+
+ DeleteCriticalSection(&numOnce.cs);
+ DeleteCriticalSection(&numThreads.cs);
+ DeleteCriticalSection(&print_lock);
+
+ return 0;
+}