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; +} |