From 99e8ecc5759668fd3af379eaddd70b4ae50ecd7f Mon Sep 17 00:00:00 2001 From: rpj Date: Thu, 5 Jul 2001 11:57:32 +0000 Subject: 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. --- ANNOUNCE | 306 +++++++--------------------- ChangeLog | 19 ++ barrier.c | 517 ++++++++++++++++++++++++++++++++++++++++++++++++ implement.h | 20 +- mutex.c | 5 +- nonportable.c | 45 +++++ pthread.def | 24 ++- pthread.h | 89 ++++++++- rwlock.c | 21 ++ spin.c | 207 +++++++++++++++++++ tests/README.benchtests | 66 +++++++ 11 files changed, 1074 insertions(+), 245 deletions(-) create mode 100644 barrier.c create mode 100644 spin.c create mode 100644 tests/README.benchtests 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 + + * 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 * 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, @@ -742,6 +784,20 @@ int pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, 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 */ @@ -756,6 +812,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 */ @@ -842,6 +922,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. -- cgit v1.2.3