diff options
| -rw-r--r-- | ChangeLog | 23 | ||||
| -rw-r--r-- | Makefile | 20 | ||||
| -rw-r--r-- | implement.h | 1 | ||||
| -rw-r--r-- | misc.c | 31 | ||||
| -rw-r--r-- | mutex.c | 48 | ||||
| -rw-r--r-- | pthread.h | 4 | ||||
| -rw-r--r-- | signal.c | 76 | ||||
| -rw-r--r-- | tests/mutex4.c | 62 | 
8 files changed, 250 insertions, 15 deletions
| @@ -40,6 +40,29 @@  	* FAQ: Update Answer 6 re getting a fully working  	Mingw32 built library. +2000-10-10  Ross Johnson  <rpj@setup1.ise.canberra.edu.au> +  +        * misc.c (pthread_self): Restore Win32 "last error" +        cleared by TlsGetValue() call in +        pthread_getspecific() +        - "Steven Reddie" <smr@essemer.com.au> +  +2000-09-20  Ross Johnson  <rpj@setup1.ise.canberra.edu.au> +  +        * mutex.c (pthread_mutex_lock): Record the owner +        of the mutex. This requires also keeping count of +        recursive locks ourselves rather than leaving it +        to Win32 since we need to know when to NULL the +        thread owner when the mutex is unlocked. +        (pthread_mutex_trylock): Likewise. +        (pthread_mutex_unlock): Check that the calling +        thread owns the mutex, decrement the recursive +        lock count, and NULL the owner if zero. Return +        EPERM if the mutex is owned by another thread. +        * implement.h (pthread_mutex_t_): Add ownerThread +        and lockCount members. +        - reported by Arthur Kantor <akantor@bexusa.com> +  2000-09-13  Ross Johnson  <rpj@setup1.ise.canberra.edu.au>  	* mutex.c (pthread_mutex_init): Call @@ -53,15 +53,21 @@ VSE:  	@ nmake /nologo EHFLAGS="$(VSEFLAGS)" pthreadVSE.dll
  realclean: clean
 -	del *.dll
 -	del *.lib
 +	@ for %%ext in (dll lib) do \
 +		if exist *.%%ext del *.%%ext
 +
 +#	del *.dll
 +#	del *.lib
  clean:
 -	del *.obj
 -	del *.ilk
 -	del *.pdb
 -	del *.exp
 -	del *.o
 +	@ for %%ext in (obj ilk pdb exp o) do \
 +		if exist *.%%ext del *.%%ext
 +
 +#	if exist *.obj del *.obj
 +#	if exist *.ilk del *.ilk
 +#	if exist *.pdb del *.pdb
 +#	if exist *.exp del *.exp
 +#	if exist *.o del *.o
  install: $(DLLS)
 diff --git a/implement.h b/implement.h index 623e474..9c74bb4 100644 --- a/implement.h +++ b/implement.h @@ -124,6 +124,7 @@ struct pthread_attr_t_ {  struct pthread_mutex_t_ {    HANDLE mutex;    CRITICAL_SECTION cs; +  pthread_t ownerThread;  }; @@ -143,12 +143,37 @@ pthread_self (void)        */  {    pthread_t self = NULL; +  DWORD lastErr; +    /* -   * need to ensure there always is a self +   * Need to ensure there always is a self. +   * +   * The following call to pthread_getspecific uses TlsGetValue. +   * Win32 functions that return indications of failure call SetLastError when +   * they fail. They generally do not call SetLastError when they succeed. The +   * TlsGetValue function is an exception to this general rule. The TlsGetValue +   * function calls SetLastError to clear a thread's last error when it +   * succeeds. +   * +   * We restore the last error if TlsGetValue succeeds.     */ +  lastErr = GetLastError(); +  self = (pthread_t) pthread_getspecific (ptw32_selfThreadKey); +  if (GetLastError() == NO_ERROR) +    { +      SetLastError(lastErr); +    } +  else +    { +      /* +       * What else can we do? GetLastError will tell the +       * the caller more but this is not supposed to +       * happen. +       */ +      return(NULL); +    } -  if ((self = (pthread_t) pthread_getspecific (ptw32_selfThreadKey))  -      == NULL) +  if (self == NULL)      {        /*         * Need to create an implicit 'self' for the currently @@ -555,6 +555,12 @@ pthread_mutex_lock(pthread_mutex_t *mutex)  	}      } +  if (result == 0) +    { +      mx->ownerThread = pthread_self(); +      mx->lockCount++; +    } +    return(result);  } @@ -578,13 +584,43 @@ pthread_mutex_unlock(pthread_mutex_t *mutex)     */    if (mx != (pthread_mutex_t) PTW32_OBJECT_AUTO_INIT)      { -      if (mx->mutex == 0) +      pthread_t self = pthread_self(); + +      if (pthread_equal(mx->ownerThread, self) == 0)  	{ -	  LeaveCriticalSection(&mx->cs); +	  int oldCount = mx->lockCount; +	  pthread_t oldOwner = mx->ownerThread; + +	  if (mx->lockCount > 0) +	    { +	      mx->lockCount--; +	    } + +	  if (mx->lockCount == 0) +	    { +	      mx->ownerThread = NULL; +	    } +	       +	  if (mx->mutex == 0) +	    { +	      LeaveCriticalSection(&mx->cs); +	    } +	  else +	    { +	      if (!ReleaseMutex(mx->mutex)) +		{ +		  result = EINVAL; +		  /* +		   * Put things back the way they were. +		   */ +		  mx->lockCount = oldCount; +		  mx->ownerThread = oldOwner; +		} +	    }  	}        else  	{ -	  result = (ReleaseMutex (mx->mutex) ? 0 : EINVAL); +	  result = EPERM;  	}      }    else @@ -643,5 +679,11 @@ pthread_mutex_trylock(pthread_mutex_t *mutex)  	}      } +  if (result == 0) +    { +      mx->ownerThread = pthread_self(); +      mx->lockCount++; +    } +    return(result);  } @@ -942,8 +942,12 @@ int pthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout);   * Thread-Safe C Runtime Library Mappings.   */  #if (! defined(NEED_ERRNO)) || (! defined( _REENTRANT ) && (! defined( _MT ) || ! defined( _MD ))) +#if defined(PTW32_BUILD) +__declspec( dllexport ) int * _errno( void ); +#else  int * _errno( void );  #endif +#endif  /*   * WIN32 C runtime library had been made thread-safe @@ -23,13 +23,68 @@   * MA 02111-1307, USA   */ -/* errno.h or a replacement file is included by pthread.h */ -//#include <errno.h> +/* + * Strategy for implementing pthread_kill() + * ======================================== + * + * Win32 does not implement signals. + * Signals are simply software interrupts. + * pthread_kill() asks the system to deliver a specified + * signal (interrupt) to a specified thread in the same + * process. + * Signals are always asynchronous (no deferred signals). + * Pthread-win32 has an async cancelation mechanism. + * A similar system can be written to deliver signals + * within the same process (on ix86 processors at least). + * + * Each thread maintains information about which + * signals it will respond to. Handler routines + * are set on a per-process basis - not per-thread. + * When signalled, a thread will check it's sigmask + * and, if the signal is not being ignored, call the + * handler routine associated with the signal. The + * thread must then (except for some signals) return to + * the point where it was interrupted. + * + * Ideally the system itself would check the target thread's + * mask before possibly needlessly bothering the thread + * itself. This could be done by pthread_kill(), that is, + * in the signaling thread since it has access to + * all pthread_t structures. It could also retrieve + * the handler routine address to minimise the target + * threads response overhead. This may also simplify + * serialisation of the access to the per-thread signal + * structures. + * + * pthread_kill() eventually calls a routine similar to + * ptw32_cancel_thread() which manipulates the target + * threads processor context to cause the thread to + * run the handler launcher routine. pthread_kill() must + * save the target threads current context so that the + * handler launcher routine can restore the context after + * the signal handler has returned. Some handlers will not + * return, eg. the default SIGKILL handler may simply + * call pthread_exit(). + * + * The current context is saved in the target threads + * pthread_t structure. + */  #include "pthread.h"  #include "implement.h"  #if HAVE_SIGSET_T + +static void +ptw32_signal_thread() +{ +} + +static void +ptw32_signal_callhandler() +{ +} +  int  pthread_sigmask(int how, sigset_t const *set, sigset_t *oset)  { @@ -97,4 +152,21 @@ pthread_sigmask(int how, sigset_t const *set, sigset_t *oset)    return 0;  } + +int pthread_kill(pthread_t thread, +		 int signo) +{ +} + +int sigwait(const sigset_t *set, +	    int *sig) +{ +} + +int sigaction(int signum, +	      const  struct  sigaction  *act, +	      struct sigaction *oldact) +{ +} +  #endif /* HAVE_SIGSET_T */ diff --git a/tests/mutex4.c b/tests/mutex4.c new file mode 100644 index 0000000..5290f2a --- /dev/null +++ b/tests/mutex4.c @@ -0,0 +1,62 @@ +/*  + * mutex4.c + * + * Thread A locks mutex - thread B tries to unlock. + * + * Depends on API functions:  + *	pthread_mutex_lock() + *	pthread_mutex_trylock() + *	pthread_mutex_unlock() + */ + +#include "test.h" +  +pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t locker_done = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t unlocker_done = PTHREAD_MUTEX_INITIALIZER; + +static int washere = 0; + +void * locker(void * arg) +{ +  assert(pthread_mutex_lock(&locker_start) == 0); +  assert(pthread_mutex_lock(&mutex1) == 0); +  assert(pthread_mutex_unlock(&locker_start) == 0); + +  /* Wait for unlocker to finish */ +  assert(pthread_mutex_lock(&unlocker_end) == 0); +  assert(pthread_mutex_unlock(&mutex1) == 0); + +  return 0; +} +  +void * unlocker(void * arg) +{ +  /* Wait for locker to lock mutex1 */ +  assert(pthread_mutex_lock(&unlocker_start) == 0); + +  assert(pthread_mutex_unlock(&mutex1) == EPERM); + +  assert(pthread_mutex_unlock(&unlocker_start) == 0); + +  return 0; +} +  +int +main() +{ +  pthread_t t; + +  assert(pthread_mutex_lock(&locker_start) == 0); +  assert(pthread_mutex_lock(&unlocker_start) == 0); + +  assert(pthread_create(&t, NULL, locker, NULL) == 0); +  assert(pthread_mutex_unlock(&locker_start) == 0); +  Sleep(0); + +  assert(pthread_create(&t, NULL, unlocker, NULL) == 0); +  assert(pthread_mutex_unlock(&unlocker_start) == 0); +  Sleep(0); + +  return 0; +} | 
