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. | 
