diff options
author | rpj <rpj> | 2001-07-05 11:57:32 +0000 |
---|---|---|
committer | rpj <rpj> | 2001-07-05 11:57:32 +0000 |
commit | 99e8ecc5759668fd3af379eaddd70b4ae50ecd7f (patch) | |
tree | 8b824cc1eb8de6fd15a4b5636f5f62fa95541105 | |
parent | 861a8bb5523f257b474f68334c2c5300e52c5371 (diff) |
Added new routines from POSIX 1003.1j. This is alpha level code.
* 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.
-rw-r--r-- | ANNOUNCE | 306 | ||||
-rw-r--r-- | ChangeLog | 19 | ||||
-rw-r--r-- | barrier.c | 517 | ||||
-rw-r--r-- | implement.h | 20 | ||||
-rw-r--r-- | mutex.c | 5 | ||||
-rw-r--r-- | nonportable.c | 45 | ||||
-rw-r--r-- | pthread.def | 24 | ||||
-rw-r--r-- | pthread.h | 89 | ||||
-rw-r--r-- | rwlock.c | 21 | ||||
-rw-r--r-- | spin.c | 207 | ||||
-rw-r--r-- | tests/README.benchtests | 66 |
11 files changed, 1074 insertions, 245 deletions
@@ -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
@@ -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; @@ -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
@@ -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. */ @@ -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) @@ -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. |