summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog23
-rw-r--r--Makefile20
-rw-r--r--implement.h1
-rw-r--r--misc.c31
-rw-r--r--mutex.c48
-rw-r--r--pthread.h4
-rw-r--r--signal.c76
-rw-r--r--tests/mutex4.c62
8 files changed, 250 insertions, 15 deletions
diff --git a/ChangeLog b/ChangeLog
index 01dbd10..28ff9d4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/Makefile b/Makefile
index 976d9f2..1f63c31 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
};
diff --git a/misc.c b/misc.c
index e79819d..ec5f9fa 100644
--- a/misc.c
+++ b/misc.c
@@ -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
diff --git a/mutex.c b/mutex.c
index 091c61e..d31b3b2 100644
--- a/mutex.c
+++ b/mutex.c
@@ -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);
}
diff --git a/pthread.h b/pthread.h
index e5a3a3e..21f4be9 100644
--- a/pthread.h
+++ b/pthread.h
@@ -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
diff --git a/signal.c b/signal.c
index ba0edeb..75bec84 100644
--- a/signal.c
+++ b/signal.c
@@ -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;
+}