summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ANNOUNCE306
-rw-r--r--ChangeLog19
-rw-r--r--barrier.c517
-rw-r--r--implement.h20
-rw-r--r--mutex.c5
-rw-r--r--nonportable.c45
-rw-r--r--pthread.def24
-rw-r--r--pthread.h89
-rw-r--r--rwlock.c21
-rw-r--r--spin.c207
-rw-r--r--tests/README.benchtests66
11 files changed, 1074 insertions, 245 deletions
diff --git a/ANNOUNCE b/ANNOUNCE
index 4067979..bf9d431 100644
--- a/ANNOUNCE
+++ b/ANNOUNCE
@@ -1,4 +1,4 @@
- PTHREADS-WIN32 SNAPSHOT 2001-07-03
+ PTHREADS-WIN32 SNAPSHOT 2001-??-??
----------------------------------
Web Site: http://sources.redhat.com/pthreads-win32/
FTP Site: ftp://sources.redhat.com/pub/pthreads-win32
@@ -26,236 +26,41 @@ announcement for the list of contributors.
Changes since the last snapshot
-------------------------------
------------------------
-Additions to Scheduling
------------------------
-New routines:
- pthread_attr_setinheritsched()
- pthread_attr_getinheritsched()
- pthread_attr_setschedpolicy()
- pthread_attr_getschedpolicy()
- sched_setscheduler()
- sched_getscheduler()
- sched_rr_get_interval()
-Now defined:
- _POSIX_THREAD_PRIORITY_SCHEDULING
-
-These routines complete the set required for defining
-_POSIX_THREAD_PRIORITY_SCHEDULING.
-
-
-sched_setscheduler
-sched_getscheduler
-------------------
-These routines will require patching to work with UWIN
-and any other system that provides it's own pid_t.
-Our pthread.h conditionally defines pid_t as a DWORD, which
-is the type returned by GetCurrentProcessId().
-
-The only supported policy is SCHED_OTHER, however, in
-order to provide semantic compatibility these routines
-verify the following:
-- that the process identified by pid exists;
-- that permission is granted to set or query the policy;
-The appropriate error is returned if either of these fail.
-On success, both routines return SCHED_OTHER.
-
-
-sched_rr_get_interval
----------------------
-Always returns -1 and sets errno to ENOTSUP. This is
-implemented as a macro. It returns ENOTSUP rather than
-ENOSYS because sched_get_priority_max() and
-sched_get_priority_min() are supported, but only for
-SCHED_OTHER.
-
-
-pthread_attr_setschedpolicy
-pthread_attr_getschedpolicy
----------------------------
-The only valid supported policy is SCHED_OTHER.
-Attempting to set other policies results in an ENOTSUP
-error.
-
-
-pthread_attr_setinheritsched
-pthread_attr_getinheritsched
-----------------------------
-The two possible values that can be set are
-PTHREAD_INHERIT_SCHED and PTHREAD_EXPLICIT_SCHED.
-
-Neither the POSIX standard nor the Single Unix Spec
-specifies which should be the default value.
-Consequently, implementations use different defaults,
-eg (from a scan of the web):
-
-PTHREAD_INHERIT_SCHED default:
- HP, MKS Toolkit, QNX, AIX (?)
-
-PTHREAD_EXPLICIT_SCHED default:
- IBM OS/400, Linux, Sun
-
-All Win32 threads are created with THREAD_PRIORITY_NORMAL.
-They do not inherit the priority of the parent thread or the
-process. This behaviour is equivalent to the following
-Pthreads defaults:
-
- Inheritance: PTHREAD_EXPLICIT_SCHED
- Priority: THREAD_PRIORITY_NORMAL
-
-These are also the defaults in pthreads-win32, and now
-reinforced by changes to the library which now actually
-use these values and routines. This choice maintains the
-notion that, from the Pthread point-of-view, Win32
-threads (those not created via pthread_create()) are
-treated as detached POSIX threads with default attribute
-values.
-
-
-------------------
-Changes to Mutexes
-------------------
-Background:
-Snapshot-2001-06-06 included Thomas Pfaff's enhancements
-to the mutex routines to improve speed. The improvements
-are very large on Win9x class systems where pthreads-win32
-previously used Win32 mutexes rather than critical
-sections as the underlying mechanism. Based on some new
-benchtest programs that have been run on a Win98 machine
-(and included in the tests directory), WNT systems speed
-has probably decreased a little. On Win9x the enhancements
-also resulted in speed improvements in other primitives
-which use mutexes internally, such as condition variables
-and read-write locks. Thomas also added mutex
-types to the library as described in the Single Unix
-Specification documentation, and as provided with
-the majority of major Unix and Linux Pthreads
-implementations.
-
-Overall, the library now provides far more consistent performance
-across the different Windows variants and greater compatibility.
-Future work will continue to improve on this.
-
-New changes:
-Changes have been made to further improve the speed of the
-default PTHREAD_MUTEX_NORMAL type (and therefore also
-PTHREAD_MUTEX_DEFAULT which is equivalent in pthreads-win32).
-
-Specifically, the library no longer sets or checks the real
-mutex owner when locking, unlocking, trylocking, or
-destroying PTHREAD_MUTEX_NORMAL mutexes. This saves
-significant overhead and results in measured speed increases
-of around 90 percent for non-blocking lock operations, and a
-slight improvement for blocking lock operations. Since the
-type of mutex used internally is PTHREAD_MUTEX_DEFAULT, this
-also results in additional speed improvements to CVs and R/W
-lock operations. Subjective observation shows an
-improvement of up to 30-35% in R/W locks
-(from tests/rwlock7.c). This is compared to the
-already improved snapshot-2001-06-06.
-
-The price paid for this improvement is that some checking
-for errors is not done for these mutex types. The onus
-is placed on the developer to check application code
-for logical errors, or use a safer mutex type such as
-PTHREAD_MUTEX_ERRORCHECK. For example, it is now
-possible for a non-owner thread to unlock or destroy
-a mutex of these types. However, because the owner
-is not simply left as NULL but set to a special anonymous
-owner value when locked and then reset to NULL when
-unlocked or destroyed, an error will ultimately eventuate
-when the owner thread subsequently attempts to unlock or
-destroy the mutex.
-
-These changes are consistent with both the behaviour exhibited
-by PTHREAD_MUTEX_NORMAL in other implementations and their
-documentation, including the Open Group documentation.
-
-
-------------
-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.
-
-
-In this snapshot there are four benchtests timing
-various mutex senarios. They are:
-
-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.
-
-
-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.
-
+----------------------------------
+New: Spin locks (alpha level code)
+----------------------------------
+These are part of POSIX 1003.1j. They are
+useful only on multiprocessor machines.
+
+ #define _POSIX_SPIN_LOCKS
+ pthread_spin_init()
+ pthread_spin_destroy()
+ pthread_spin_lock()
+ pthread_spin_unlock()
+ pthread_spin_trylock()
+
+--------------------------------
+New: Barriers (alpha level code)
+--------------------------------
+These are part of POSIX 1003.1j. They are generally
+useful.
+
+ #define _POSIX_BARRIERS
+ pthread_barrier_init()
+ pthread_barrier_destroy()
+ pthread_barrier_wait()
+ pthread_barrierattr_init()
+ pthread_barrierattr_destroy()
+ pthread_barrierattr_getpshared()
+ pthread_barrierattr_setpshared()
---------
Bug fixes
---------
-Pthread_create now sets the priority of the new thread
-from the value set in the thread attribute.
-- from Ralf.Brese@pdb4.siemens.de.
+In pthread_mutex_lock, return the error if an
+auto-initialiser initialisation fails.
-In the last snapshot I introduced a "lost signal" bug into
-the condition variables code.
-- fixed by Alexander Terekhov
-- reported by Timur Aydin taydin@snet.net
-
-
----------
-New tests
----------
-Several new tests have been added to the test suite.
+_POSIX_READER_WRITER_LOCKS is now defined.
---------------------------
@@ -327,16 +132,19 @@ reliably.
Level of standards conformance
------------------------------
-The following POSIX 1003.1c 1995 and POSIX 1003.1b options are defined:
+The following POSIX 1003.1c/1b/1j options are defined:
_POSIX_THREADS
_POSIX_THREAD_SAFE_FUNCTIONS
_POSIX_THREAD_ATTR_STACKSIZE
_POSIX_THREAD_PRIORITY_SCHEDULING
_POSIX_SEMAPHORES
+ _POSIX_READER_WRITER_LOCKS
+ _POSIX_SPIN_LOCKS
+ _POSIX_BARRIERS
-The following POSIX 1003.1c 1995 options are not defined:
+The following POSIX 1003.1c options are not defined:
_POSIX_THREAD_ATTR_STACKADDR
_POSIX_THREAD_PRIO_INHERIT
@@ -415,7 +223,7 @@ The following functions are implemented:
pthread_cond_broadcast
---------------------------
- Read/Write Locks:
+ Read/Write Locks - POSIX 1j
---------------------------
pthread_rwlock_init
pthread_rwlock_destroy
@@ -426,17 +234,37 @@ The following functions are implemented:
pthread_rwlock_unlock
---------------------------
- Semaphores
+ Spin Locks - POSIX 1j
+ ---------------------------
+ pthread_spin_init
+ pthread_spin_destroy
+ pthread_spin_lock
+ pthread_spin_unlock
+ pthread_spin_trylock
+
+ ---------------------------
+ Barriers - POSIX 1j
+ ---------------------------
+ pthread_barrier_init
+ pthread_barrier_destroy
+ pthread_barrier_wait
+ pthread_barrierattr_init
+ pthread_barrierattr_destroy
+ pthread_barrierattr_getpshared
+ pthread_barrierattr_setpshared
+
+ ---------------------------
+ Semaphores - POSIX 1b
---------------------------
- sem_init (POSIX 1b)
- sem_destroy (POSIX 1b)
- sem_post (POSIX 1b)
- sem_wait (POSIX 1b)
- sem_trywait (POSIX 1b)
- sem_open (POSIX 1b - returns an error ENOSYS)
- sem_close (POSIX 1b - returns an error ENOSYS)
- sem_unlink (POSIX 1b - returns an error ENOSYS)
- sem_getvalue (POSIX 1b - returns an error ENOSYS)
+ sem_init
+ sem_destroy
+ sem_post
+ sem_wait
+ sem_trywait
+ sem_open (returns an error ENOSYS)
+ sem_close (returns an error ENOSYS)
+ sem_unlink (returns an error ENOSYS)
+ sem_getvalue (returns an error ENOSYS)
---------------------------
RealTime Scheduling
diff --git a/ChangeLog b/ChangeLog
index fba734d..767f7fc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2001-07-05 Ross Johnson <rpj@setup1.ise.canberra.edu.au>
+
+ * spin.c: New module implementing spin locks.
+ * barrier.c: New module implementing barriers.
+ * pthread.h (_POSIX_SPIN_LOCKS): defined.
+ (_POSIX_BARRIERS): Defined.
+ (pthread_spin_*): Defined.
+ (pthread_barrier*): Defined.
+ (PTHREAD_BARRIER_SERIAL_THREAD): Defined.
+ * implement.h (pthread_spinlock_t_): Defined.
+ (pthread_barrier_t_): Defined.
+ (pthread_barrierattr_t_): Defined.
+
+ * mutex.c (pthread_mutex_lock): Return with the error
+ if an auto-initialiser initialisation fails.
+
+ * nonportable.c (pthread_getprocessors_np): New; gets the
+ number of available processors for the current process.
+
2001-07-03 Ross Johnson <rpj@setup1.ise.canberra.edu.au>
* pthread.h (_POSIX_READER_WRITER_LOCKS): Define it
diff --git a/barrier.c b/barrier.c
new file mode 100644
index 0000000..4495e33
--- /dev/null
+++ b/barrier.c
@@ -0,0 +1,517 @@
+/*
+ * barrier.c
+ *
+ * Description:
+ * This translation unit implements spin locks primitives.
+ *
+ * Pthreads-win32 - POSIX Threads Library for Win32
+ * Copyright (C) 1998
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA
+ */
+
+#include "pthread.h"
+#include "implement.h"
+
+
+static int
+ptw32_barrier_check_need_init(pthread_barrier_t *barrier)
+{
+ int result = 0;
+
+ /*
+ * The following guarded test is specifically for statically
+ * initialised barriers (via PTHREAD_BARRIER_INITIALIZER).
+ *
+ * Note that by not providing this synchronisation we risk
+ * introducing race conditions into applications which are
+ * correctly written.
+ *
+ * Approach
+ * --------
+ * We know that static barriers will not be PROCESS_SHARED
+ * so we can serialise access to internal state using
+ * Win32 Critical Sections rather than Win32 Mutexes.
+ *
+ * If using a single global lock slows applications down too much,
+ * multiple global locks could be created and hashed on some random
+ * value associated with each barrier, the pointer perhaps. At a guess,
+ * a good value for the optimal number of global locks might be
+ * the number of processors + 1.
+ *
+ */
+ EnterCriticalSection(&ptw32_barrier_test_init_lock);
+
+ /*
+ * We got here possibly under race
+ * conditions. Check again inside the critical section
+ * and only initialise if the barrier is valid (not been destroyed).
+ * If a static barrier has been destroyed, the application can
+ * re-initialise it only by calling pthread_barrier_init()
+ * explicitly.
+ */
+ if (*barrier == (pthread_barrier_t) PTW32_OBJECT_AUTO_INIT)
+ {
+ result = pthread_barrier_init(barrier, NULL);
+ }
+ else if (*barrier == NULL)
+ {
+ /*
+ * The barrier has been destroyed while we were waiting to
+ * initialise it, so the operation that caused the
+ * auto-initialisation should fail.
+ */
+ result = EINVAL;
+ }
+
+ LeaveCriticalSection(&ptw32_barrier_test_init_lock);
+
+ return(result);
+}
+
+
+int
+pthread_barrier_init(pthread_barrier_t * barrier,
+ const pthread_barrierattr_t * attr,
+ int count)
+{
+ int result = 0;
+ int pshared = PTHREAD_PROCESS_PRIVATE;
+ pthread_barrier_t * b;
+
+ if (barrier == NULL)
+ {
+ return EINVAL;
+ }
+
+ b = (pthread_barrier_t *) calloc(1, sizeof(*b));
+
+ if (b == NULL)
+ {
+ result = ENOMEM;
+ goto FAIL0;
+ }
+
+ if (attr != NULL && *attr != NULL)
+ {
+ pshared = (*attr)->pshared;
+ }
+
+ b->nCurrentBarrierHeight = b->nInitialBarrierHeight = count;
+
+ result = pthread_mutex_init(&b->mtxExclusiveAccess, NULL);
+ if (0 != result)
+ {
+ goto FAIL0;
+ }
+
+ result = sem_init(&(b->semBarrierBreeched), pshared, 0);
+ if (0 != result)
+ {
+ goto FAIL1;
+ }
+
+ goto DONE;
+
+ FAIL1:
+ (void) pthread_mutex_destroy(&b->mtxExclusiveAccess);
+
+ FAIL0:
+ (void) free(b);
+
+ DONE:
+ return(result);
+}
+
+int
+pthread_barrier_destroy(pthread_barrier_t *barrier)
+{
+ int result = 0;
+ pthread_barrier_t b;
+
+ if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID)
+ {
+ return EINVAL;
+ }
+
+ if (*barrier != (pthread_barrier_t) PTW32_OBJECT_AUTO_INIT)
+ {
+ b = *barrier;
+
+ if (0 == pthread_mutex_trylock(&b->mtxExclusiveAccess))
+ {
+ /*
+ * FIXME!!!
+ * The mutex isn't held by another thread but we could still
+ * be too late invalidating the barrier below since another thread
+ * may alredy have entered barrier_wait and the check for a valid
+ * *barrier != NULL.
+ */
+ *barrier = NULL;
+
+ (void) sem_destroy(&(b->semBarrierBreeched));
+ (void) pthread_mutex_unlock(&(b->mtxExclusiveAccess));
+ (void) pthread_mutex_destroy(&(b->mtxExclusiveAccess));
+ (void) free(b);
+ }
+ }
+ else
+ {
+ /*
+ * See notes in ptw32_barrier_check_need_init() above also.
+ */
+ EnterCriticalSection(&ptw32_barrier_test_init_lock);
+
+ /*
+ * Check again.
+ */
+ if (*barrier == (pthread_barrier_t) PTW32_OBJECT_AUTO_INIT)
+ {
+ /*
+ * This is all we need to do to destroy a statically
+ * initialised barrier that has not yet been used (initialised).
+ * If we get to here, another thread
+ * waiting to initialise this barrier will get an EINVAL.
+ */
+ *barrier = NULL;
+ }
+ else
+ {
+ /*
+ * The barrier has been initialised while we were waiting
+ * so assume it's in use.
+ */
+ result = EBUSY;
+ }
+
+ LeaveCriticalSection(&ptw32_barrier_test_init_lock);
+ }
+
+ return(result);
+}
+
+
+int
+pthread_barrier_wait(pthread_barrier_t *barrier)
+{
+ int result;
+ pthread_barrier_t b;
+
+ if (barrier == NULL || *barrier == (pthread_barrier_t) PTW32_OBJECT_INVALID)
+ {
+ return EINVAL;
+ }
+
+ if (*barrier == (pthread_barrier_t) PTHREAD_OBJECT_AUTO_INIT)
+ {
+ if ((result = ptw32_barrier_check_need_init(barrier)) != 0)
+ {
+ return(result);
+ }
+ }
+
+ b = *barrier;
+
+ result = pthread_mutex_lock(b->mtxExclusiveAccess);
+ if (0 == result)
+ {
+ if (0 == --(b->nCurrentBarrierHeight))
+ {
+ b->nCurrentBarrierHeight = b->nInitialBarrierHeight;
+ (void) pthread_mutex_unlock(b->mtxExclusiveAccess);
+ (void) sem_post_multiple(&(b->semBarrierBreeched),
+ b->InitialBarrierHeight);
+ /*
+ * Would be better if the first thread to return
+ * from this routine got this value. On a single
+ * processor machine that will be the last thread
+ * to reach the barrier (us), most of the time.
+ */
+ result = PTHREAD_BARRIER_SERIAL_THREAD;
+ }
+ else
+ {
+ /*
+ * pthread_barrier_wait() is not a cancelation point
+ * so temporarily prevent sem_wait() from being one.
+ */
+ pthread_t self = pthread_self();
+ int cancelType = pthread_getcanceltype(self);
+ int oldCancelState;
+
+ if (cancelType == PTHREAD_CANCEL_DEFERRED)
+ {
+ oldCancelState = pthread_setcancelstate(self,
+ PTHREAD_CANCEL_DISABLED);
+ }
+
+ /* Could still be PTHREAD_CANCEL_ASYNCHRONOUS. */
+ pthread_cleanup_push(pthread_mutex_unlock,
+ (void *) &(b->mtxExclusiveAccess));
+
+ if (0 != sem_wait(&(b->semBarrierBreeched)))
+ {
+ result = errno;
+ }
+
+ if (cancelType == PTHREAD_CANCEL_DEFERRED)
+ {
+ pthread_setcancelstate(self, oldCancelState);
+ }
+
+ pthread_cleanup_pop(1);
+ }
+ }
+
+ return(result);
+}
+
+
+int
+pthread_barrierattr_init (pthread_barrierattr_t * attr)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * Initializes a barrier attributes object with default
+ * attributes.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_barrierattr_t
+ *
+ *
+ * DESCRIPTION
+ * Initializes a barrier attributes object with default
+ * attributes.
+ *
+ * NOTES:
+ * 1) Used to define barrier types
+ *
+ * RESULTS
+ * 0 successfully initialized attr,
+ * ENOMEM insufficient memory for attr.
+ *
+ * ------------------------------------------------------
+ */
+{
+ pthread_barrierattr_t ba;
+ int result = 0;
+
+ ba = (pthread_barrierattr_t) calloc (1, sizeof (*ba));
+
+ if (ba == NULL)
+ {
+ result = ENOMEM;
+ }
+
+ ba->pshared = PTHREAD_PROCESS_PRIVATE;
+
+ *attr = ba;
+
+ return (result);
+
+} /* pthread_barrierattr_init */
+
+
+int
+pthread_barrierattr_destroy (pthread_barrierattr_t * attr)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * Destroys a barrier attributes object. The object can
+ * no longer be used.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_barrierattr_t
+ *
+ *
+ * DESCRIPTION
+ * Destroys a barrier attributes object. The object can
+ * no longer be used.
+ *
+ * NOTES:
+ * 1) Does not affect barrieres created using 'attr'
+ *
+ * RESULTS
+ * 0 successfully released attr,
+ * EINVAL 'attr' is invalid.
+ *
+ * ------------------------------------------------------
+ */
+{
+ int result = 0;
+
+ if (attr == NULL || *attr == NULL)
+ {
+ result = EINVAL;
+ }
+ else
+ {
+ pthread_barrierattr_t ba = *attr;
+
+ *attr = NULL;
+ free (ba);
+
+ result = 0;
+ }
+
+ return (result);
+
+} /* pthread_barrierattr_destroy */
+
+
+int
+pthread_barrierattr_getpshared (const pthread_barrierattr_t * attr,
+ int *pshared)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * Determine whether barriers created with 'attr' can be
+ * shared between processes.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_barrierattr_t
+ *
+ * pshared
+ * will be set to one of:
+ *
+ * PTHREAD_PROCESS_SHARED
+ * May be shared if in shared memory
+ *
+ * PTHREAD_PROCESS_PRIVATE
+ * Cannot be shared.
+ *
+ *
+ * DESCRIPTION
+ * Mutexes creatd with 'attr' can be shared between
+ * processes if pthread_barrier_t variable is allocated
+ * in memory shared by these processes.
+ * NOTES:
+ * 1) pshared barriers MUST be allocated in shared
+ * memory.
+ * 2) The following macro is defined if shared barriers
+ * are supported:
+ * _POSIX_THREAD_PROCESS_SHARED
+ *
+ * RESULTS
+ * 0 successfully retrieved attribute,
+ * EINVAL 'attr' is invalid,
+ *
+ * ------------------------------------------------------
+ */
+{
+ int result;
+
+ if ((attr != NULL && *attr != NULL) &&
+ (pshared != NULL))
+ {
+ *pshared = (*attr)->pshared;
+ result = 0;
+ }
+ else
+ {
+ *pshared = PTHREAD_PROCESS_PRIVATE;
+ result = EINVAL;
+ }
+
+ return (result);
+
+} /* pthread_barrierattr_getpshared */
+
+
+int
+pthread_barrierattr_setpshared (pthread_barrierattr_t * attr,
+ int pshared)
+ /*
+ * ------------------------------------------------------
+ * DOCPUBLIC
+ * Barriers created with 'attr' can be shared between
+ * processes if pthread_barrier_t variable is allocated
+ * in memory shared by these processes.
+ *
+ * PARAMETERS
+ * attr
+ * pointer to an instance of pthread_barrierattr_t
+ *
+ * pshared
+ * must be one of:
+ *
+ * PTHREAD_PROCESS_SHARED
+ * May be shared if in shared memory
+ *
+ * PTHREAD_PROCESS_PRIVATE
+ * Cannot be shared.
+ *
+ * DESCRIPTION
+ * Mutexes creatd with 'attr' can be shared between
+ * processes if pthread_barrier_t variable is allocated
+ * in memory shared by these processes.
+ *
+ * NOTES:
+ * 1) pshared barriers MUST be allocated in shared
+ * memory.
+ *
+ * 2) The following macro is defined if shared barriers
+ * are supported:
+ * _POSIX_THREAD_PROCESS_SHARED
+ *
+ * RESULTS
+ * 0 successfully set attribute,
+ * EINVAL 'attr' or pshared is invalid,
+ * ENOSYS PTHREAD_PROCESS_SHARED not supported,
+ *
+ * ------------------------------------------------------
+ */
+{
+ int result;
+
+ if ((attr != NULL && *attr != NULL) &&
+ ((pshared == PTHREAD_PROCESS_SHARED) ||
+ (pshared == PTHREAD_PROCESS_PRIVATE)))
+ {
+ if (pshared == PTHREAD_PROCESS_SHARED)
+ {
+
+#if !defined( _POSIX_THREAD_PROCESS_SHARED )
+
+ result = ENOSYS;
+ pshared = PTHREAD_PROCESS_PRIVATE;
+
+#else
+
+ result = 0;
+
+#endif /* _POSIX_THREAD_PROCESS_SHARED */
+
+ }
+ else
+ {
+ result = 0;
+ }
+
+ (*attr)->pshared = pshared;
+ }
+ else
+ {
+ result = EINVAL;
+ }
+
+ return (result);
+
+} /* pthread_barrierattr_setpshared */
+
diff --git a/implement.h b/implement.h
index e2acba3..34055dd 100644
--- a/implement.h
+++ b/implement.h
@@ -136,6 +136,8 @@ struct sem_t_ {
#define PTW32_OBJECT_AUTO_INIT ((void *) -1)
#define PTW32_OBJECT_INVALID NULL
+#define PTW32_SPIN_UNLOCKED ((void *) 1)
+#define PTW32_SPIN_LOCKED ((void *) 2)
struct pthread_mutex_t_ {
LONG lock_idx;
@@ -146,12 +148,28 @@ struct pthread_mutex_t_ {
CRITICAL_SECTION try_lock_cs;
};
-
struct pthread_mutexattr_t_ {
int pshared;
int kind;
};
+struct pthread_spinlock_t_ {
+ union {
+ LONG interlock;
+ pthread_mutex_t mx;
+ } u;
+};
+
+struct pthread_barrier_t_ {
+ LONG nCurrentBarrierHeight;
+ LONG nInitialBarrierHeight;
+ sem_t semBarrierBreeched;
+ pthread_mutex_t mtxExclusiveAccess;
+};
+
+struct pthread_barrierattr_t_ {
+ int pshared;
+};
struct pthread_key_t_ {
DWORD key;
diff --git a/mutex.c b/mutex.c
index 0396e5f..22a7e7b 100644
--- a/mutex.c
+++ b/mutex.c
@@ -632,7 +632,10 @@ pthread_mutex_lock(pthread_mutex_t *mutex)
*/
if (*mutex == (pthread_mutex_t) PTW32_OBJECT_AUTO_INIT)
{
- result = ptw32_mutex_check_need_init(mutex);
+ if ((result = ptw32_mutex_check_need_init(mutex)) != 0)
+ {
+ return(result);
+ }
}
mx = *mutex;
diff --git a/nonportable.c b/nonportable.c
index 150b0c0..5fcb1b8 100644
--- a/nonportable.c
+++ b/nonportable.c
@@ -134,6 +134,51 @@ pthread_delay_np (struct timespec * interval)
}
+/*
+ * pthread_getprocessors_np()
+ *
+ * Get the number of CPUs available to the process.
+ *
+ * If the available number of CPUs is 1 then pthread_spin_lock()
+ * will block rather than spin if the lock is already owned.
+ *
+ * pthread_spin_init() calls this routine when initialising
+ * a spinlock. If the number of available processors changes
+ * (after a call to SetProcessAffinityMask()) then only
+ * newly initialised spinlocks will notice.
+ */
+int
+pthread_getprocessors_np(int * count)
+{
+ DWORD vProcessCPUs;
+ DWORD vSystemCPUs;
+ int result = 0;
+
+ if (GetProcessAffinityMask(GetCurrentProcess(),
+ &vProcessCPUs,
+ &vSystemCPUs))
+ {
+ DWORD bit;
+ int CPUs = 0;
+
+ for (bit = 1; bit != 0; bit <<= 1)
+ {
+ if (vProcessCPUs & bit)
+ {
+ CPUs++;
+ }
+ }
+ *count = CPUs;
+ }
+ else
+ {
+ result = EAGAIN;
+ }
+
+ return(result);
+}
+
+
BOOL
pthread_win32_process_attach_np ()
{
diff --git a/pthread.def b/pthread.def
index 9ca0cbd..53c2b27 100644
--- a/pthread.def
+++ b/pthread.def
@@ -1,12 +1,11 @@
; pthread.def
-; Last updated: $Date: 2001/07/01 14:35:50 $
+; Last updated: $Date: 2001/07/05 11:57:32 $
; Currently unimplemented functions are commented out.
;LIBRARY pthread
EXPORTS
-ptw32_processInitialize
;pthread_atfork
pthread_attr_destroy
pthread_attr_getdetachstate
@@ -109,6 +108,24 @@ pthread_rwlock_rdlock
pthread_rwlock_wrlock
pthread_rwlock_unlock
;
+; Spin locks
+;
+pthread_spin_init
+pthread_spin_destroy
+pthread_spin_lock
+pthread_spin_unlock
+pthread_spin_trylock
+;
+; Barriers
+;
+pthread_barrier_init
+pthread_barrier_destroy
+pthread_barrier_wait
+pthread_barrierattr_init
+pthread_barrierattr_destroy
+pthread_barrierattr_getpshared
+pthread_barrierattr_setpshared
+;
; Non-portable/compatibility with other implementations
;
pthread_delay_np
@@ -118,9 +135,12 @@ pthread_mutexattr_setkind_np
; Non-portable local implementation only
;
pthread_getw32threadhandle_np
+pthread_getprocessors_np
pthreadCancelableWait
pthreadCancelableTimedWait
+;
; For use when linking statically
+;
pthread_win32_process_attach_np
pthread_win32_process_detach_np
pthread_win32_thread_attach_np
diff --git a/pthread.h b/pthread.h
index 8675c56..ca36b8b 100644
--- a/pthread.h
+++ b/pthread.h
@@ -265,6 +265,19 @@ extern "C"
* Maximum number of threads supported per
* process (must be at least 64).
*
+ *
+ * POSIX 1003.1j/D10-1999 Options
+ * ==============================
+ *
+ * _POSIX_READER_WRITER_LOCKS (set)
+ * If set, you can use read/write locks
+ *
+ * _POSIX_SPIN_LOCKS (set)
+ * If set, you can use spin locks
+ *
+ * _POSIX_BARRIERS (set)
+ * If set, you can use barriers
+ *
* -------------------------------------------------------------
*/
@@ -275,6 +288,18 @@ extern "C"
#define _POSIX_THREADS
#endif
+#ifndef _POSIX_READER_WRITER_LOCKS
+#define _POSIX_READER_WRITER_LOCKS
+#endif
+
+#ifndef _POSIX_SPIN_LOCKS
+#define _POSIX_SPIN_LOCKS
+#endif
+
+#ifndef _POSIX_BARRIERS
+#define _POSIX_BARRIERS
+#endif
+
#define _POSIX_THREAD_SAFE_FUNCTIONS
#define _POSIX_THREAD_ATTR_STACKSIZE
#define _POSIX_THREAD_PRIORITY_SCHEDULING
@@ -336,7 +361,8 @@ typedef struct pthread_condattr_t_ *pthread_condattr_t;
#endif
typedef struct pthread_rwlock_t_ *pthread_rwlock_t;
typedef struct pthread_rwlockattr_t_ *pthread_rwlockattr_t;
-
+typedef struct pthread_spinlock_t_ pthread_spinlock_t;
+typedef struct pthread_barrier_t_ *pthread_barrier_t;
/*
* ====================
@@ -382,7 +408,12 @@ enum {
* pthread_condattr_{get,set}pshared
*/
PTHREAD_PROCESS_PRIVATE = 0,
- PTHREAD_PROCESS_SHARED = 1
+ PTHREAD_PROCESS_SHARED = 1,
+
+/*
+ * pthread_barrier_wait
+ */
+ PTHREAD_BARRIER_SERIAL_THREAD = -1
};
/*
@@ -412,12 +443,23 @@ struct pthread_once_t_
};
+/*
+ * ====================
+ * ====================
+ * Object initialisers
+ * ====================
+ * ====================
+ */
#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t) -1)
#define PTHREAD_COND_INITIALIZER ((pthread_cond_t) -1)
#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t) -1)
+#define PTHREAD_SPINLOCK_INITIALIZER {1}
+
+#define PTHREAD_BARRIER_INITIALIZER ((pthread_barrier_t) -1)
+
enum
{
PTHREAD_MUTEX_FAST_NP,
@@ -743,6 +785,20 @@ int pthread_mutexattr_settype (pthread_mutexattr_t * attr, int kind);
int pthread_mutexattr_gettype (pthread_mutexattr_t * attr, int *kind);
/*
+ * Barrier Attribute Functions
+ */
+int pthread_barrierattr_init (pthread_barrierattr_t * attr);
+
+int pthread_barrierattr_destroy (pthread_barrierattr_t * attr);
+
+int pthread_barrierattr_getpshared (const pthread_barrierattr_t
+ * attr,
+ int *pshared);
+
+int pthread_barrierattr_setpshared (pthread_barrierattr_t * attr,
+ int pshared);
+
+/*
* Mutex Functions
*/
int pthread_mutex_init (pthread_mutex_t * mutex,
@@ -757,6 +813,30 @@ int pthread_mutex_trylock (pthread_mutex_t * mutex);
int pthread_mutex_unlock (pthread_mutex_t * mutex);
/*
+ * Spinlock Functions
+ */
+int pthread_spin_init (pthread_spinlock_t * lock);
+
+int pthread_spin_destroy (pthread_spinlock_t * lock);
+
+int pthread_spin_lock (pthread_spinlock_t * lock);
+
+int pthread_spin_trylock (pthread_spinlock_t * lock);
+
+int pthread_spin_unlock (pthread_spinlock_t * lock);
+
+/*
+ * Barrier Functions
+ */
+int pthread_barrier_init (pthread_barrier_t * barrier,
+ const pthread_barrierattr_t * attr,
+ int count);
+
+int pthread_barrier_destroy (pthread_barrier_t * barrier);
+
+int pthread_barrier_wait (pthread_barrier_t * barrier);
+
+/*
* Condition Variable Attribute Functions
*/
int pthread_condattr_init (pthread_condattr_t * attr);
@@ -843,6 +923,11 @@ int pthread_delay_np (struct timespec * interval);
HANDLE pthread_getw32threadhandle_np(pthread_t thread);
/*
+ * Returns the number of CPUs available to the process.
+ */
+int pthread_getprocessors_np(int * count);
+
+/*
* Useful if an application wants to statically link
* the lib rather than load the DLL at run-time.
*/
diff --git a/rwlock.c b/rwlock.c
index cc85b7f..c2d85d8 100644
--- a/rwlock.c
+++ b/rwlock.c
@@ -378,8 +378,24 @@ pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)
if (rwl->nSharedAccessCount > 0)
{
+ /*
+ * pthread_rwlock_wrlock() is not a cancelation point
+ * so temporarily prevent pthread_cond_wait() from being one.
+ */
+ pthread_t self = pthread_self();
+ int cancelType = pthread_getcanceltype(self);
+ int oldCancelState;
+
rwl->nCompletedSharedAccessCount = -rwl->nSharedAccessCount;
+ if (cancelType == PTHREAD_CANCEL_DEFERRED)
+ {
+ oldCancelState =
+ pthread_setcancelstate(self,
+ PTHREAD_CANCEL_DISABLED);
+ }
+
+ /* Could still be PTHREAD_CANCEL_ASYNCHRONOUS. */
pthread_cleanup_push(ptw32_rwlock_cancelwrwait, (void*)rwl);
do
@@ -389,6 +405,11 @@ pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)
}
while (result == 0 && rwl->nCompletedSharedAccessCount < 0);
+ if (cancelType == PTHREAD_CANCEL_DEFERRED)
+ {
+ pthread_setcancelstate(self, oldCancelState);
+ }
+
pthread_cleanup_pop ((result != 0) ? 1 : 0);
if (result == 0)
diff --git a/spin.c b/spin.c
new file mode 100644
index 0000000..5f30cfc
--- /dev/null
+++ b/spin.c
@@ -0,0 +1,207 @@
+/*
+ * spin.c
+ *
+ * Description:
+ * This translation unit implements spin locks primitives.
+ *
+ * Pthreads-win32 - POSIX Threads Library for Win32
+ * Copyright (C) 1998
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA
+ */
+
+#include "pthread.h"
+#include "implement.h"
+
+/*
+ * This works because the mask that is formed exposes all but the
+ * first two LSBs. If the spinlock is using a mutex rather than
+ * the interlock mechanism then there will always be high bits
+ * to indicate this. This is all just to save the overhead of
+ * using a non-simple struct for spinlocks.
+ */
+#define PTW32_SPIN_SPINS(_lock) \
+ (0 == (_lock->u.mx & (pthread_mutex_t) ~(PTW32_OBJECT_INVALID | PTW32_SPIN_UNLOCK | PTW32_SPIN_LOCKED)))
+
+
+int
+pthread_spin_init(pthread_spinlock_t *lock, int pshared)
+{
+ int CPUs = 1;
+ int result = 0;
+
+ if (lock == NULL)
+ {
+ return EINVAL;
+ }
+
+ (void) pthread_getprocessors_np(&CPUs);
+
+ if (CPUs > 1)
+ {
+ if (pshared == PTHREAD_PROCESS_SHARED)
+ {
+ /*
+ * Creating spinlock that can be shared between
+ * processes.
+ */
+#if _POSIX_THREAD_PROCESS_SHARED
+
+ /*
+ * Not implemented yet.
+ */
+
+#error ERROR [__FILE__, line __LINE__]: Process shared spin locks are not supported yet.
+
+
+#else
+
+ result = ENOSYS;
+ goto FAIL0;
+
+#endif /* _POSIX_THREAD_PROCESS_SHARED */
+
+ }
+
+ lock->u.interlock = PTW32_SPIN_UNLOCKED;
+ }
+ else
+ {
+ pthread_mutexattr_t ma;
+ result = pthread_mutexattr_init(&ma);
+
+ if (0 == result)
+ {
+ ma->pshared = pshared;
+ result = pthread_mutex_init(&(lock->u.mx), &ma);
+ }
+ }
+
+FAIL0:
+
+ return(result);
+}
+
+int
+pthread_spin_destroy(pthread_spinlock_t *lock)
+{
+ if (lock == NULL)
+ {
+ return EINVAL;
+ }
+
+ if (PTW32_SPIN_SPINS(lock))
+ {
+ if ( PTW32_SPIN_UNLOCKED !=
+ InterlockedCompareExchange((LPLONG) &(lock->u.interlock),
+ (LPLONG) PTW32_OBJECT_INVALID,
+ (LPLONG) PTW32_SPIN_UNLOCKED))
+ {
+ return EINVAL;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ return pthread_mutex_destroy(&(lock->u.mx));
+ }
+}
+
+
+int
+pthread_spin_lock(pthread_spinlock_t *lock)
+{
+ if (lock == NULL)
+ {
+ return EINVAL;
+ }
+
+ if (PTW32_SPIN_SPINS(lock))
+ {
+ while ( PTW32_SPIN_UNLOCKED !=
+ InterlockedCompareExchange((LPLONG) &(lock->u.interlock),
+ (LPLONG) PTW32_SPIN_LOCKED,
+ (LPLONG) PTW32_SPIN_UNLOCKED) )
+ {
+ /* Spin */
+ }
+ }
+ else
+ {
+ return pthread_mutex_lock(&(lock->u.mx));
+ }
+
+ return 0;
+}
+
+int
+pthread_spin_unlock(pthread_spinlock_t *lock)
+{
+ if (lock == NULL)
+ {
+ return EINVAL;
+ }
+
+ if (PTW32_SPIN_SPINS(lock))
+ {
+ if (PTW32_SPIN_LOCKED !=
+ InterlockedCompareExchange((LPLONG) &(lock->u.interlock),
+ (LPLONG) PTW32_SPIN_UNLOCKED,
+ (LPLONG) PTW32_SPIN_LOCKED ) )
+ {
+ return 0;
+ }
+ else
+ {
+ return EINVAL;
+ }
+ }
+ else
+ {
+ return pthread_mutex_unlock(&(lock->u.mx));
+ }
+}
+
+int
+pthread_spin_trylock(pthread_spinlock_t *lock)
+{
+ if (lock == NULL)
+ {
+ return EINVAL;
+ }
+
+ if (PTW32_SPIN_SPINS(lock))
+ {
+ if (PTW32_SPIN_UNLOCKED !=
+ InterlockedCompareExchange((LPLONG) &(lock->u.interlock),
+ (LPLONG) PTW32_SPIN_LOCKED,
+ (LPLONG) PTW32_SPIN_UNLOCKED ) )
+ {
+ return EBUSY;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ else
+ {
+ return pthread_mutex_trylock(&(lock->u.mx));
+ }
+}
diff --git a/tests/README.benchtests b/tests/README.benchtests
new file mode 100644
index 0000000..1d8e1e3
--- /dev/null
+++ b/tests/README.benchtests
@@ -0,0 +1,66 @@
+
+------------
+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.
+
+
+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.