diff options
| -rw-r--r-- | ChangeLog | 87 | ||||
| -rw-r--r-- | dll.c | 13 | ||||
| -rw-r--r-- | global.c | 7 | ||||
| -rw-r--r-- | implement.h | 5 | ||||
| -rw-r--r-- | mutex.c | 508 | ||||
| -rw-r--r-- | private.c | 14 | ||||
| -rw-r--r-- | pthread.h | 20 | ||||
| -rw-r--r-- | tests/eyal1.c | 1 | 
8 files changed, 613 insertions, 42 deletions
| @@ -1,3 +1,18 @@ +Wed Jan 20 09:31:28 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au> + +	* pthread.h (pthread_mutexattr_t): Changed to a pointer. + +	* mutex.c (pthread_mutex_init): Conditionally create Win32 mutex +	- from John Bossom's implementation. +	(pthread_mutex_destroy): Conditionally close Win32 mutex +	- from John Bossom's implementation. +	(pthread_mutexattr_init): Replaced by John Bossom's version. +	(pthread_mutexattr_destroy): Ditto. +	(pthread_mutexattr_getpshared): New function from John Bossom's +	implementation. +	(pthread_mutexattr_setpshared): New function from John Bossom's +	implementation. +  Tue Jan 19 18:27:42 1999  Ross Johnson  <rpj@swan.canberra.edu.au>  	* pthread.h (pthreadCancelableTimedWait): New prototype. @@ -16,6 +31,78 @@ Tue Jan 19 18:27:42 1999  Ross Johnson  <rpj@swan.canberra.edu.au>  	pthreadCancelableTimedWait().  	- Scott Lightner <scott@curriculum.com> +Tue Jan 19 10:27:39 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au> + +	* pthread.h (pthread_mutexattr_setforcecs_np): New prototype. +	 +	* mutex.c (pthread_mutexattr_init): Init 'pshared' and 'forcecs' +	attributes to 0. +	(pthread_mutexattr_setforcecs_np): New function (not portable). + +	* pthread.h (pthread_mutex_t):  +	Add 'mutex' element. Set to NULL in PTHREAD_MUTEX_INITIALIZER. +	The pthread_mutex_*() routines will try to optimise performance +	by choosing either mutexes or critical sections as the basis +	for pthread mutexes for each indevidual mutex. +	(pthread_mutexattr_t_): Add 'forcecs' element. +	Some applications may choose to force use of critical sections +	if they know that:- +	     the mutex is PROCESS_PRIVATE and,  +	         either the OS supports TryEnterCriticalSection() or +	         pthread_mutex_trylock() will never be called on the mutex. +	This attribute will be setable via a non-portable routine. + +	Note: We don't yet support PROCESS_SHARED mutexes, so the +	implementation as it stands will default to Win32 mutexes only if +	the OS doesn't support TryEnterCriticalSection. On Win9x, and early +	versions of NT 'forcecs' will need to be set in order to get +	critical section based mutexes. + +Sun Jan 17 12:01:26 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au> + +	* pthread.h (PTHREAD_MUTEX_INITIALIZER): Init new 'staticinit' +	value to '1' and existing 'valid' value to '1'. + +	* global.c (_pthread_mutex_test_init_lock): Add. + +	* implement.h (_pthread_mutex_test_init_lock.): Add extern. + +	* private.c (_pthread_processInitialize): Init critical section for +	global lock used by _mutex_check_need_init(). +	(_pthread_processTerminate): Ditto (:s/Init/Destroy/). + +	* dll.c (dllMain): Move call to FreeLibrary() so that it is only +	called once when the process detaches. + +	* mutex.c (_mutex_check_need_init): New static function to test +	and init PTHREAD_MUTEX_INITIALIZER mutexes. Provides serialised +	access to the internal state of the uninitialised static mutex.  +	Called from pthread_mutex_trylock() and pthread_mutex_lock() which +	do a quick unguarded test to check if _mutex_check_need_init() +	needs to be called. This is safe as the test is conservative + 	and is repeated inside the guarded section of  +	_mutex_check_need_init(). Thus in all calls except the first +	calls to lock static mutexes, the additional overhead to lock any +	mutex is a single memory fetch and test for zero. + +	* pthread.h (pthread_mutex_t_): Add 'staticinit' member. Mutexes +	initialised by PTHREAD_MUTEX_INITIALIZER aren't really initialised +	until the first attempt to lock it. Using the 'valid' +	flag (which flags the mutex as destroyed or not) to record this +	information would be messy. It is possible for a statically +	initialised mutex such as this to be destroyed before ever being +	used. + +	* mutex.c (pthread_mutex_trylock): Call _mutex_check_need_init() +	to test/init PTHREAD_MUTEX_INITIALIZER mutexes. +	(pthread_mutex_lock): Ditto. +	(pthread_mutex_unlock): Add check to ensure we don't try to unlock +	an unitialised static mutex. +	(pthread_mutex_destroy): Add check to ensure we don't try to delete +	a critical section that we never created. Allows us to destroy +	a static mutex that has never been locked (and hence initialised). +	(pthread_mutex_init): Set 'staticinit' flag to 0 for the new mutex. +  Sun Jan 17 12:01:26 1999  Ross Johnson  <rpj@ixobrychus.canberra.edu.au>  	* private.c (_pthread_sem_timedwait): Move from semaphore.c. @@ -10,12 +10,17 @@  #include "implement.h" -/* Function pointer to TryEnterCriticalSection if it exists; otherwise NULL */ +/*  + * Function pointer to TryEnterCriticalSection if it exists; otherwise NULL  + */  BOOL (WINAPI *_pthread_try_enter_critical_section)(LPCRITICAL_SECTION) = NULL; -/* Handle to kernel32.dll */ +/* + * Handle to kernel32.dll  + */  static HINSTANCE _pthread_h_kernel32; +  #ifdef _MSC_VER  /*    * lpvReserved yields an unreferenced formal parameter; @@ -89,11 +94,11 @@ DllMain (  		 * The DLL is being unmapped into the process's address space  		 */  		_pthread_processTerminate (); + +		(void) FreeLibrary(_pthread_h_kernel32);  	      }  	  } -	(void) FreeLibrary(_pthread_h_kernel32); -  	result = TRUE;        }        break; @@ -14,4 +14,11 @@ int _pthread_processInitialized = FALSE;  pthread_key_t _pthread_selfThreadKey = NULL;  pthread_key_t _pthread_cleanupKey = NULL; +/* + * Global lock for testing internal state of PTHREAD_MUTEX_INITIALIZER + * created mutexes. + */ +CRITICAL_SECTION _pthread_mutex_test_init_lock; + + diff --git a/implement.h b/implement.h index be71b7e..f897033 100644 --- a/implement.h +++ b/implement.h @@ -135,6 +135,7 @@ extern BOOL (WINAPI *_pthread_try_enter_critical_section)(LPCRITICAL_SECTION);  extern int _pthread_processInitialized;  extern pthread_key_t _pthread_selfThreadKey;  extern pthread_key_t _pthread_cleanupKey; +extern CRITICAL_SECTION _pthread_mutex_test_init_lock;  #ifdef __cplusplus @@ -183,7 +184,8 @@ int _pthread_sem_timedwait (sem_t * sem, const struct timespec * abstime);   */  #if defined(__CYGWIN32__) || defined(__CYGWIN__) -/* Macro uses args so we can cast start_proc to LPTHREAD_START_ROUTINE +/*  + * Macro uses args so we can cast start_proc to LPTHREAD_START_ROUTINE   * in order to avoid warnings because of return type   */ @@ -206,3 +208,4 @@ int _pthread_sem_timedwait (sem_t * sem, const struct timespec * abstime);  #endif /* _IMPLEMENT_H */ + @@ -10,85 +10,505 @@  #include "pthread.h"  #include "implement.h" + +static int +_mutex_check_need_init(pthread_mutex_t *mutex) +{ +  int result = 0; + +  /* +   * The following guarded test is specifically for statically +   * initialised mutexes (via PTHREAD_MUTEX_INITIALIZER). +   * +   * Note that by not providing this synchronisation we risk +   * introducing race conditions into applications which are +   * correctly written. +   * +   * Approach +   * -------- +   * We know that static mutexes will not be PROCESS_SHARED +   * so we can serialise access to internal state using +   * Win32 Critical Sections rather than Win32 Mutexes. +   * +   * We still have a problem in that we would like a per-mutex +   * lock, but attempting to create one will again lead +   * to a race condition. We are forced to use a global +   * lock in this instance. +   * +   * 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 mutex, the pointer perhaps. At a guess, +   * a good value for the optimal number of global locks might be +   * the number of processors + 1. +   * +   * We need to maintain the following per-mutex state independently: +   *   - mutex staticinit (true iff static mutex and still +   *                        needs to be initialised) +   *   - mutex valid (false iff mutex has been destroyed) +   * +   * For example, a mutex initialised by PTHREAD_MUTEX_INITIALIZER +   * in this implementation will be valid but uninitialised until the thread +   * attempts to lock it. It can also be destroyed (made invalid) before +   * ever being locked. +   */ +  EnterCriticalSection(&_pthread_mutex_test_init_lock); + +  /* +   * We got here because staticinit tested true. +   * Check staticinit again inside the critical section +   * and only initialise if the mutex is valid (not been destroyed). +   * If a static mutex has been destroyed, the application can +   * re-initialise it only by calling pthread_mutex_init() +   * explicitly. +   */ +  if (mutex->staticinit && mutex->valid) +    { +      result = pthread_mutex_init(mutex, NULL); +    } + +  LeaveCriticalSection(&_pthread_mutex_test_init_lock); + +  return(result); +} +  int  pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)  { +  int result = 0; +    if (mutex == NULL)      {        return EINVAL;      } -  /* Create a critical section. */ -  InitializeCriticalSection(&mutex->cs); +  mutex->mutex = 0; -  /* Mark as valid. */ -  mutex->valid = 1; +  /*  +   * Assuming any race condition here is harmless. +   */ +  if (mutex->valid && !mutex->staticinit) +    { +      return EBUSY; +    } -  return 0; +  if (attr != NULL +      && *attr != NULL +      && (*attr)->pshared == PTHREAD_PROCESS_SHARED +      ) +    { +      /* +       * Creating mutex that can be shared between +       * processes. +       */ +#if _POSIX_THREAD_PROCESS_SHARED + +      /* +       * Not implemented yet. +       */ + +#error ERROR [__FILE__, line __LINE__]: Process shared mutexes are not supported yet. + +      mutex->mutex = CreateMutex ( +				  NULL, +				  FALSE, +				  ????); +      result = (mutex->mutex == 0) ? EAGAIN : 0; + +#else + +      result = ENOSYS; + +#endif /* _POSIX_THREAD_PROCESS_SHARED */ +    } +  else +    { +      if (_pthread_try_enter_critical_section != NULL +	  || (attr != NULL +	      && *attr != NULL +	      && (*attr)->forcecs == 1) +	  ) +	{ +	  /*  +	   * Create a critical section.  +	   */ +	  InitializeCriticalSection(&mutex->cs); +	} +      else +	{ +	  /* +	   * Create a mutex that can only be used within the +	   * current process +	   */ +	  mutex->mutex = CreateMutex (NULL, +				      FALSE, +				      NULL); +	  result = (mutex->mutex == 0) ? EAGAIN : 0; +	} +    } + +  if (result == 0) +    { +      mutex->staticinit = 0; + +      /* Mark as valid. */ +      mutex->valid = 1; +    } + +  return(result);  }  int  pthread_mutex_destroy(pthread_mutex_t *mutex)  { +  int result = 0; +    if (mutex == NULL)      {        return EINVAL;      } -  DeleteCriticalSection(&mutex->cs); -   -  /* Mark as invalid. */ -  mutex->valid = 0; +  /* +   * Check to see if we have something to delete. +   */ +  if (!mutex->staticinit) +    { +      if (mutex->mutex == 0) +	{ +	  DeleteCriticalSection(&mutex->cs); +	} +      else +	{ +	  result = (CloseHandle (mutex->mutex) ? 0 : EINVAL); +	} +    } -  return 0; +  if (result == 0) +    { +      mutex->mutex = 0; + +      /* Mark as invalid. */ +      mutex->valid = 0; +    } + +  return(result);  }  int -pthread_mutexattr_init(pthread_mutexattr_t *attr) +pthread_mutexattr_init (pthread_mutexattr_t * attr) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      Initializes a mutex attributes object with default +      *      attributes. +      * +      * PARAMETERS +      *      attr +      *              pointer to an instance of pthread_mutexattr_t +      * +      * +      * DESCRIPTION +      *      Initializes a mutex attributes object with default +      *      attributes. +      * +      *      NOTES: +      *              1)      Used to define mutex types +      * +      * RESULTS +      *              0               successfully initialized attr, +      *              ENOMEM          insufficient memory for attr. +      * +      * ------------------------------------------------------ +      */ +{ +  pthread_mutexattr_t attr_result; +  int result = 0; + +  attr_result = calloc (1, sizeof (*attr_result)); + +  result = (attr_result == NULL) +    ? ENOMEM +    : 0; + +  *attr = attr_result; + +  return (result); + +}                               /* pthread_mutexattr_init */ + + +int +pthread_mutexattr_destroy (pthread_mutexattr_t * attr) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      Destroys a mutex attributes object. The object can +      *      no longer be used. +      * +      * PARAMETERS +      *      attr +      *              pointer to an instance of pthread_mutexattr_t +      * +      * +      * DESCRIPTION +      *      Destroys a mutex attributes object. The object can +      *      no longer be used. +      * +      *      NOTES: +      *              1)      Does not affect mutexes created using 'attr' +      * +      * RESULTS +      *              0               successfully released attr, +      *              EINVAL          'attr' is invalid. +      * +      * ------------------------------------------------------ +      */ +{ +  int result = 0; + +  if (attr == NULL || *attr == NULL) +    { +      result = EINVAL; + +    } +  else +    { +      free (*attr); + +      *attr = NULL; +      result = 0; +    } + +  return (result); + +}                               /* pthread_mutexattr_destroy */ + + +int +pthread_mutexattr_setforcecs_np(pthread_mutexattr_t *attr, +				int forcecs)  { -  if (attr == NULL) +  if (attr == NULL || *attr == NULL)      {        /* This is disallowed. */        return EINVAL;      } -  /* None of the optional attributes are supported yet. */ +  (*attr)->forcecs = forcecs; +    return 0;  } +  int -pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +pthread_mutexattr_getpshared (const pthread_mutexattr_t * attr, +			      int *pshared) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      Determine whether mutexes created with 'attr' can be +      *      shared between processes. +      * +      * PARAMETERS +      *      attr +      *              pointer to an instance of pthread_mutexattr_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_mutex_t variable is allocated +      *      in memory shared by these processes. +      *      NOTES: +      *              1)      pshared mutexes MUST be allocated in shared +      *                      memory. +      *              2)      The following macro is defined if shared mutexes +      *                      are supported: +      *                              _POSIX_THREAD_PROCESS_SHARED +      * +      * RESULTS +      *              0               successfully retrieved attribute, +      *              EINVAL          'attr' is invalid, +      * +      * ------------------------------------------------------ +      */  { -  /* Nothing to do. */ -  return 0; -} +  int result; + +  if ((attr != NULL && *attr != NULL) && +      (pshared != NULL)) +    { +      *pshared = (*attr)->pshared; +      result = 0; +    } +  else +    { +      *pshared = PTHREAD_PROCESS_PRIVATE; +      result = EINVAL; +    } + +  return (result); + +}                               /* pthread_mutexattr_getpshared */ + + +int +pthread_mutexattr_setpshared (pthread_mutexattr_t * attr, +			      int pshared) +     /* +      * ------------------------------------------------------ +      * DOCPUBLIC +      *      Mutexes created with 'attr' can be shared between +      *      processes if pthread_mutex_t variable is allocated +      *      in memory shared by these processes. +      * +      * PARAMETERS +      *      attr +      *              pointer to an instance of pthread_mutexattr_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_mutex_t variable is allocated +      *      in memory shared by these processes. +      * +      *      NOTES: +      *              1)      pshared mutexes MUST be allocated in shared +      *                      memory. +      * +      *              2)      The following macro is defined if shared mutexes +      *                      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_mutexattr_setpshared */ +  int  pthread_mutex_lock(pthread_mutex_t *mutex)  { -  if (!mutex->valid) +  int result = 0; + +  /* +   * We do a quick check to see if we need to do more work +   * to initialise a static mutex. We check 'staticinit' +   * again inside the guarded section of _mutex_check_need_init() +   * to avoid race conditions. +   */ +  if (mutex->staticinit)      { -      pthread_mutex_init(mutex, NULL); +      result = _mutex_check_need_init(mutex);      } -  EnterCriticalSection(&mutex->cs); -  return 0; + +  if (result == 0) +    { +      if (mutex->mutex == 0) +	{ +	  EnterCriticalSection(&mutex->cs); +	} +      else +	{ +	  result = (WaitForSingleObject(mutex->mutex, INFINITE)  +		    == WAIT_OBJECT_0) +	    ? 0 +	    : EINVAL; +	} +    } + +  return(result);  }  int  pthread_mutex_unlock(pthread_mutex_t *mutex)  { -  if (!mutex->valid) +  int result = 0; + +  /*  +   * If the thread calling us holds the mutex then there is no +   * race condition. If another thread holds the +   * lock then we shouldn't be in here. +   */ +  if (!mutex->staticinit && mutex->valid)      { -      return EINVAL; +      if (mutex->mutex == 0) +	{ +	  LeaveCriticalSection(&mutex->cs); +	} +      else +	{ +	  result = (ReleaseMutex (mutex->mutex) ? 0 : EINVAL); +	}      } -  LeaveCriticalSection(&mutex->cs); -  return 0; +  else +    { +      result = EINVAL; +    } + +  return(result);  }  int  pthread_mutex_trylock(pthread_mutex_t *mutex)  { -  if (_pthread_try_enter_critical_section == NULL) +  int result = 0; + +  if (mutex->mutex == 0 && _pthread_try_enter_critical_section == NULL)      {        /* TryEnterCriticalSection does not exist in the OS; return ENOSYS. */        return ENOSYS; @@ -99,10 +519,40 @@ pthread_mutex_trylock(pthread_mutex_t *mutex)        return EINVAL;      } -  if (!mutex->valid) +  /* +   * We do a quick check to see if we need to do more work +   * to initialise a static mutex. We check 'staticinit' +   * again inside the guarded section of _mutex_check_need_init() +   * to avoid race conditions. +   */ +  if (mutex->staticinit) +    { +      result = _mutex_check_need_init(mutex); +    } + +  if (result == 0)      { -      pthread_mutex_init(mutex, NULL); +      if (mutex->mutex == 0) +	{ +	  if ((*_pthread_try_enter_critical_section)(&mutex->cs) != TRUE) +	    { +	      result = EBUSY; +	    } +	} +      else +	{ +	  DWORD status; + +	  status = WaitForSingleObject (mutex->mutex, 0); + +	  if (status != WAIT_OBJECT_0) +	    { +	      result = ((status == WAIT_TIMEOUT) +			? EBUSY +			: EINVAL); +	    } +	}      } -  return ((*_pthread_try_enter_critical_section)(&mutex->cs) != TRUE) ? EBUSY : 0; +  return(result);  } @@ -9,6 +9,8 @@  #include "pthread.h"  #include "implement.h" +#include <sys/timeb.h> +  /*   * Code contributed by John E. Bossom <JEB>.   */ @@ -49,6 +51,11 @@ _pthread_processInitialize (void)        _pthread_processTerminate ();      } +  /*  +   * Set up the global mutex test and init check lock. +   */ +  InitializeCriticalSection(&_pthread_mutex_test_init_lock); +    return (_pthread_processInitialized);  }				/* processInitialize */ @@ -99,6 +106,11 @@ _pthread_processTerminate (void)  	  _pthread_cleanupKey = NULL;  	} +      /*  +       * Destroy up the global mutex test and init check lock. +       */ +      DeleteCriticalSection(&_pthread_mutex_test_init_lock); +        _pthread_processInitialized = FALSE;      } @@ -483,7 +495,7 @@ _pthread_sem_timedwait (sem_t * sem, const struct timespec * abstime)    if (abstime == NULL)      { -      msecs = INFINITE; +      milliseconds = INFINITE;      }    else      { @@ -415,7 +415,7 @@ extern "C"    typedef struct pthread_once_t_ pthread_once_t;    typedef struct pthread_key_t_ *pthread_key_t;    typedef struct pthread_mutex_t_ pthread_mutex_t; -  typedef struct pthread_mutexattr_t_ pthread_mutexattr_t; +  typedef struct pthread_mutexattr_t_ *pthread_mutexattr_t;    typedef struct pthread_cond_t_ *pthread_cond_t;    typedef struct pthread_condattr_t_ *pthread_condattr_t; @@ -460,10 +460,10 @@ extern "C"   * ====================   */ -/* FIXME: Replace the NULL with a valid critical section initializer - * and then also change the 0 (first element) to 1. +/*  + *    */ -#define PTHREAD_MUTEX_INITIALIZER { 0, NULL } +#define PTHREAD_MUTEX_INITIALIZER { 1, 1, 0, NULL }  /* @@ -569,13 +569,16 @@ struct pthread_attr_t_ {  struct pthread_mutex_t_ { -	int valid; -	CRITICAL_SECTION cs; -  }; +  int staticinit; +  int valid; +  HANDLE mutex; +  CRITICAL_SECTION cs; +};  struct pthread_mutexattr_t_ {    int pshared; +  int forcecs;  }; @@ -838,6 +841,9 @@ int pthread_mutexattr_getpshared (const pthread_mutexattr_t  int pthread_mutexattr_setpshared (pthread_mutexattr_t * attr,  				  int pshared); +int pthread_mutexattr_setforcecs_np(pthread_mutexattr_t *attr, +				    int forcecs); +  /*   * Mutex Functions   */ diff --git a/tests/eyal1.c b/tests/eyal1.c index e32d29e..f51bc9c 100644 --- a/tests/eyal1.c +++ b/tests/eyal1.c @@ -161,6 +161,7 @@ print_server (void *ptr)  */  			if (pthread_mutex_lock (&mutex_todo))  				return (-6); +  			mywork = todo;  			if (todo >= 0) {  				++todo; | 
