summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS3
-rw-r--r--ChangeLog97
-rw-r--r--FAQ76
-rw-r--r--GNUmakefile69
-rw-r--r--Makefile112
-rw-r--r--Makefile.in4
-rw-r--r--Makefile.vc59
-rw-r--r--README13
-rw-r--r--cancel.c254
-rw-r--r--cleanup.c4
-rw-r--r--configure.in2
-rw-r--r--create.c10
-rw-r--r--dll.c25
-rw-r--r--global.c3
-rw-r--r--implement.h22
-rw-r--r--misc.c44
-rw-r--r--private.c34
-rw-r--r--pthread.def8
-rw-r--r--pthread.h50
-rw-r--r--sync.c58
-rw-r--r--tests/ChangeLog19
-rw-r--r--tests/Makefile11
-rw-r--r--tests/cancel2.c216
-rw-r--r--tests/cancel3.c180
-rw-r--r--tests/cancel4.c183
-rw-r--r--tests/ccl.bat6
-rw-r--r--tests/condvar2.c4
-rw-r--r--tests/condvar3.c4
-rw-r--r--tests/condvar4.c4
-rw-r--r--tests/condvar5.c4
-rw-r--r--tests/condvar6.c4
-rw-r--r--tests/condvar7.c4
-rw-r--r--tests/condvar8.c4
-rw-r--r--tests/condvar9.c4
-rw-r--r--tests/context1.c110
-rw-r--r--tests/count1.c4
-rw-r--r--tests/join2.c4
-rw-r--r--tests/loadfree.c38
-rw-r--r--tests/runall.bat5
-rw-r--r--tests/test.h35
40 files changed, 1540 insertions, 250 deletions
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index c9818bd..38482ef 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -18,3 +18,6 @@ Graham Dumpleton Graham.Dumpleton@ra.pad.otc.telstra.com.au
Tristan Savatier tristan@mpegtv.com
Erik Hensema erik.hensema@group2000.nl
Rich Peters rpeters@micro-magic.com
+Todd Owen towen@lucidcalm.dropbear.id.au
+Jason Nye jnye@nbnet.nb.ca
+Fred Forester fforest@eticomm.net
diff --git a/ChangeLog b/ChangeLog
index c4dff53..e0d0b3e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,100 @@
+2000-01-04 Ross Johnson <rpj@special.ise.canberra.edu.au>
+
+ * private.c (_pthread_get_exception_services_code): New; returns
+ value of EXCEPTION_PTHREAD_SERVICES.
+ (_pthread_processInitialize): Remove initialisation of
+ _pthread_exception_services which is no longer needed.
+
+ * pthread.h (_pthread_exception_services): Remove extern.
+ (_pthread_get_exception_services_code): Add function prototype;
+ use this to return EXCEPTION_PTHREAD_SERVICES value instead of
+ using the _pthread_exception_services variable which I had
+ trouble exporting through pthread.def.
+
+ * global.c (_pthread_exception_services): Remove declaration.
+
+1999-11-22 Ross Johnson <rpj@special.ise.canberra.edu.au>
+
+ * implement.h: Forward declare _pthread_new();
+
+ * misc.c (_pthread_new): New; alloc and initialise a new pthread_t.
+ (pthread_self): New thread struct is generated by new routine
+ _pthread_new().
+
+ * create.c (pthread_create): New thread struct is generated
+ by new routine_pthread_new().
+
+1999-11-21 Ross Johnson <rpj@special.ise.canberra.edu.au>
+
+ * global.c (_pthread_exception_services): Declare new variable.
+
+ * private.c (_pthread_threadStart): Destroy thread's
+ cancelLock mutex; make 'catch' and '__except' usageimmune to
+ redfinitions in pthread.h.
+ (_pthread_processInitialize): Init new constant _pthread_exception_services.
+
+ * create.c (pthread_create): Initialise thread's cancelLock
+ mutex.
+
+ * cleanup.c (pthread_pop_cleanup): Make 'catch' and '__except'
+ usage immune to redfinition s in pthread.h.
+
+ * private.c: Ditto.
+
+ * pthread.h (catch): Redefine 'catch' so that C++ applications
+ won't catch our internal exceptions.
+ (__except): ditto for __except.
+
+ * implement.h (_pthread_catch): Define internal version
+ of 'catch' because 'catch' is redefined by pthread.h.
+ (__except): ditto for __except.
+ (struct pthread_t_): Add cancelLock mutex for async cancel
+ safety.
+
+ * cancel.c (_pthread_cancel_self): New; part of the async
+ cancellation implementation.
+ (_pthread_cancel_thread): Ditto; this function is X86
+ processor specific.
+ (pthread_setcancelstate): Add check for pending async
+ cancel request and cancel the calling thread if
+ required; add async-cancel safety lock.
+ (pthread_setcanceltype): Ditto.
+ - Jason Nye <jnye@nbnet.nb.ca>
+ - Erik Hensema <erik.hensema@group2000.nl>
+
+1999-11-13 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
+
+ * configure.in (AC_OUTPUT): Put generated output into GNUmakefile
+ rather than Makefile. Makefile will become the MSC nmake compatible
+ version
+ - Erik Hensema <erik.hensema@group2000.nl>
+
+ * misc.c (pthread_self): Add a note about GetCurrentThread
+ returning a pseudo-handle
+ - John Bossom (John.Bossom@cognos.com>
+
+1999-11-10 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
+
+ * dll.c (dllMain): Free kernel32 ASAP.
+ If TryEnterCriticalSection is not being used, then free
+ the kernel32.dll handle now, rather than leaving it until
+ DLL_PROCESS_DETACH.
+
+ Note: this is not a pedantic exercise in freeing unused
+ resources! It is a work-around for a bug in Windows 95
+ (see microsoft knowledge base article, Q187684) which
+ does Bad Things when FreeLibrary is called within
+ the DLL_PROCESS_DETACH code, in certain situations.
+ Since w95 just happens to be a platform which does not
+ provide TryEnterCriticalSection, the bug will be
+ effortlessly avoided.
+ - Todd Owen <towen@lucidcalm.dropbear.id.au>
+
+ * sync.c (pthread_join): Make it a deferred cancelation point.
+
+ * misc.c (pthread_self): Explicitly initialise implicitly
+ created thread state to default values.
+
1999-11-05 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
* pthread.h (winsock.h): Include unconditionally.
diff --git a/FAQ b/FAQ
index 885835d..5df87ac 100644
--- a/FAQ
+++ b/FAQ
@@ -12,6 +12,8 @@ Q 2 Now that pthreads-win32 builds under Mingw32, why do I get
Q 3 How do I use pthread.dll for Win32 (Visual C++ 5.0)
+Q 4 Cancelation doesn't work for me, why?
+
=============================================================================
Q 1 Should I use Cygwin or Mingw32 as a development environment?
@@ -177,3 +179,77 @@ is included for information.
Cheers.
Ross
+
+------------------------------------------------------------------------------
+
+Q 4 Cancelation doesn't work for me, why?
+---
+
+A 4
+---
+
+> I'm investigating a problem regarding thread cancelation. The thread I want
+> to cancel has PTHREAD_CANCEL_ASYNCHRONOUS, however, this piece of code
+> blocks on the join():
+>
+> if ((retv = Pthread_cancel( recvThread )) == 0)
+> {
+> retv = Pthread_join( recvThread, 0 );
+> }
+>
+> Pthread_* are just macro's; they call pthread_*.
+>
+> The thread recvThread seems to block on a select() call. It doesn't get
+> cancelled.
+>
+> Two questions:
+>
+> 1) is this normal behaviour?
+>
+> 2) if not, how does the cancel mechanism work? I'm not very familliar to
+> win32 programming, so I don't really understand how the *Event() family of
+> calls work.
+
+The answer to your first question is, normal POSIX behaviour would
+be to asynchronously cancel the thread.
+
+However ...
+
+Pthreads-win32 doesn't support asynchronous cancelation, only
+deferred, which is also very limited. The reason there is no async
+cancelation is that it's too hard, if not impossible, to implement
+on top of Win32. Incidently, Butenhof "Programming with POSIX
+Threads" recommends not using async cancelation because the
+possible side effects are unpredictable, especially if you're
+trying to write portable code.
+
+Using deferred cancelation would normally be the way to go, however,
+even though the POSIX threads standard lists a number of C library
+functions that are defined as deferred cancelation points, there is
+no hookup between those which are provided by Windows and the
+pthreads-win32 library.
+
+Incidently, it's worth noting for code portability that the POSIX
+threads standard list doesn't include "select" because (as I read in
+Butenhof) it isn't recognised by POSIX.
+
+Effectively, the only cancelation points that pthreads-win32 can
+recognise are those the library implements itself, ie.
+
+ pthread_testcancel
+ pthread_cond_wait
+ pthread_cond_timedwait
+ pthread_join
+ sem_wait
+
+Pthreads-win32 also provides two functions that allow you to create
+cancelation points within your application, but only for cases where
+a thread is going to block on a Win32 handle. These are:
+
+ pthreadCancelableWait(HANDLE waitHandle) /* Infinite wait */
+
+ pthreadCancelableTimedWait(HANDLE waitHandle, DWORD timeout)
+
+Regards.
+Ross
+
diff --git a/GNUmakefile b/GNUmakefile
new file mode 100644
index 0000000..f0d8982
--- /dev/null
+++ b/GNUmakefile
@@ -0,0 +1,69 @@
+#
+# 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
+#
+
+RM = erase
+
+CC = g++
+
+AR = ar
+
+LD = gcc -mdll
+
+OPT = -g -O2
+
+## Mingw32
+CFLAGS = $(OPT) -I. -DHAVE_CONFIG_H -Wall
+
+## Cygwin G++
+#CFLAGS = $(OPT) -fhandle-exceptions -I. -DHAVE_CONFIG_H -Wall
+
+OBJS = attr.o cancel.o cleanup.o condvar.o create.o dll.o errno.o \
+ exit.o fork.o global.o misc.o mutex.o private.o rwlock.o \
+ sched.o semaphore.o signal.o sync.o tsd.o
+
+INCL = implement.h semaphore.h pthread.h windows.h
+
+DLL = pthread.dll
+
+LIB = libpthread32.a
+
+
+all: $(LIB)
+
+$(LIB): $(DLL)
+ dlltool --def $(DLL:.dll=.def) --output-lib $@ --dllname $(DLL)
+
+.SUFFIXES: .dll
+
+$(DLL): $(OBJS)
+ $(LD) -o $@ $^ -Wl,--base-file,$*.base
+ dlltool --base-file=$*.base --def $*.def --output-exp $*.exp --dllname $@
+ $(LD) -o $@ $^ -Wl,--base-file,$*.base,$*.exp
+ dlltool --base-file=$*.base --def $*.def --output-exp $*.exp --dllname $@
+ $(LD) -o $@ $^ -Wl,$*.exp
+
+clean:
+ -$(RM) *~
+ -$(RM) $(LIB)
+ -$(RM) *.o
+ -$(RM) *.exe
+ -$(RM) $(DLL)
+ -$(RM) $(DLL:.dll=.base)
+ -$(RM) $(DLL:.dll=.exp)
diff --git a/Makefile b/Makefile
index f0d8982..4b87074 100644
--- a/Makefile
+++ b/Makefile
@@ -1,69 +1,59 @@
-#
-# 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
-#
-RM = erase
+# This makefile is compatible with MS nmake and can be used as a
+# replacement for buildlib.bat. I've changed the target from an ordinary dll
+# (/LD) to a debugging dll (/LDd).
+#
+# The variables $DLLDEST and $LIBDEST hold the destination directories for the
+# dll and the lib, respectively. Probably all that needs to change is $DEVROOT.
+
+DEVROOT=e:
+
+DLLDEST=$(DEVROOT)\dll
+LIBDEST=$(DEVROOT)\lib
+
+OBJ=attr.obj \
+ cancel.obj \
+ cleanup.obj \
+ condvar.obj \
+ create.obj \
+ dll.obj \
+ errno.obj \
+ exit.obj \
+ fork.obj \
+ global.obj \
+ misc.obj \
+ mutex.obj \
+ private.obj \
+ rwlock.obj \
+ sched.obj \
+ semaphore.obj \
+ signal.obj \
+ sync.obj \
+ tsd.obj
+
+all: pthread.dll
-CC = g++
-
-AR = ar
-
-LD = gcc -mdll
-
-OPT = -g -O2
-
-## Mingw32
-CFLAGS = $(OPT) -I. -DHAVE_CONFIG_H -Wall
-
-## Cygwin G++
-#CFLAGS = $(OPT) -fhandle-exceptions -I. -DHAVE_CONFIG_H -Wall
-
-OBJS = attr.o cancel.o cleanup.o condvar.o create.o dll.o errno.o \
- exit.o fork.o global.o misc.o mutex.o private.o rwlock.o \
- sched.o semaphore.o signal.o sync.o tsd.o
-
-INCL = implement.h semaphore.h pthread.h windows.h
-
-DLL = pthread.dll
-
-LIB = libpthread32.a
+clean:
+ del pthread.dll \
+ pthread.lib \
+ *.obj
-all: $(LIB)
+install: all
+ copy pthread.dll $(DLLDEST)
+ copy pthread.lib $(LIBDEST)
-$(LIB): $(DLL)
- dlltool --def $(DLL:.dll=.def) --output-lib $@ --dllname $(DLL)
+pthread.dll: $(OBJ) pthread.def
+ cl /LDd /Zi *.obj /Fepthread.dll \
+ pthread.def \
+ /link /nodefaultlib:libcmt \
+ msvcrt.lib
-.SUFFIXES: .dll
+.c.obj::
+ cl /W3 /MT /nologo /Yd /Zi /I. \
+ /D_WIN32_WINNT=0x400 \
+ /DSTDCALL=_stdcall \
+ -c $<
-$(DLL): $(OBJS)
- $(LD) -o $@ $^ -Wl,--base-file,$*.base
- dlltool --base-file=$*.base --def $*.def --output-exp $*.exp --dllname $@
- $(LD) -o $@ $^ -Wl,--base-file,$*.base,$*.exp
- dlltool --base-file=$*.base --def $*.def --output-exp $*.exp --dllname $@
- $(LD) -o $@ $^ -Wl,$*.exp
+$(OBJ):
-clean:
- -$(RM) *~
- -$(RM) $(LIB)
- -$(RM) *.o
- -$(RM) *.exe
- -$(RM) $(DLL)
- -$(RM) $(DLL:.dll=.base)
- -$(RM) $(DLL:.dll=.exp)
diff --git a/Makefile.in b/Makefile.in
index f0d8982..40214e8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -24,12 +24,12 @@ CC = g++
AR = ar
-LD = gcc -mdll
+LD = gcc -mdll -mthreads
OPT = -g -O2
## Mingw32
-CFLAGS = $(OPT) -I. -DHAVE_CONFIG_H -Wall
+CFLAGS = $(OPT) -I. -DHAVE_CONFIG_H -Wall -mthreads
## Cygwin G++
#CFLAGS = $(OPT) -fhandle-exceptions -I. -DHAVE_CONFIG_H -Wall
diff --git a/Makefile.vc b/Makefile.vc
new file mode 100644
index 0000000..4b87074
--- /dev/null
+++ b/Makefile.vc
@@ -0,0 +1,59 @@
+
+# This makefile is compatible with MS nmake and can be used as a
+# replacement for buildlib.bat. I've changed the target from an ordinary dll
+# (/LD) to a debugging dll (/LDd).
+#
+# The variables $DLLDEST and $LIBDEST hold the destination directories for the
+# dll and the lib, respectively. Probably all that needs to change is $DEVROOT.
+
+DEVROOT=e:
+
+DLLDEST=$(DEVROOT)\dll
+LIBDEST=$(DEVROOT)\lib
+
+OBJ=attr.obj \
+ cancel.obj \
+ cleanup.obj \
+ condvar.obj \
+ create.obj \
+ dll.obj \
+ errno.obj \
+ exit.obj \
+ fork.obj \
+ global.obj \
+ misc.obj \
+ mutex.obj \
+ private.obj \
+ rwlock.obj \
+ sched.obj \
+ semaphore.obj \
+ signal.obj \
+ sync.obj \
+ tsd.obj
+
+all: pthread.dll
+
+clean:
+ del pthread.dll \
+ pthread.lib \
+ *.obj
+
+
+install: all
+ copy pthread.dll $(DLLDEST)
+ copy pthread.lib $(LIBDEST)
+
+pthread.dll: $(OBJ) pthread.def
+ cl /LDd /Zi *.obj /Fepthread.dll \
+ pthread.def \
+ /link /nodefaultlib:libcmt \
+ msvcrt.lib
+
+.c.obj::
+ cl /W3 /MT /nologo /Yd /Zi /I. \
+ /D_WIN32_WINNT=0x400 \
+ /DSTDCALL=_stdcall \
+ -c $<
+
+$(OBJ):
+
diff --git a/README b/README
index 4e4be15..0b6db0c 100644
--- a/README
+++ b/README
@@ -20,11 +20,18 @@ Acknowledgements
Pthreads-win32 is based substantially on a Win32 Pthreads implementation
contributed by John E. Bossom <jebossom@cognos.com>.
-See the 'MAINTAINERS' file for the list of contributors.
+See the 'CONTRIBUTORS' file for the list of contributors.
-Why you cannot build the library with Cygwin or Mingw32 yet
------------------------------------------------------------
+Building under Mingw32
+----------------------
+
+pthread.dll can be build with the current development version of mingw32.
+Use the GNUmakefile with GNU make should do the right thing.
+
+
+Why you cannot build the library with Cygwin yet
+------------------------------------------------
The DLL pthread.dll still cannot be built using g++ due to non thread-safe
exception handling in g++. Thanks to Kevin Ruland for researching this
diff --git a/cancel.c b/cancel.c
index a5d923d..b687c78 100644
--- a/cancel.c
+++ b/cancel.c
@@ -26,6 +26,64 @@
#include "pthread.h"
#include "implement.h"
+
+static void
+_pthread_cancel_self(void)
+{
+#ifdef _MSC_VER
+
+ DWORD exceptionInformation[3];
+
+ exceptionInformation[0] = (DWORD) (_PTHREAD_EPS_CANCEL);
+ exceptionInformation[1] = (DWORD) (0);
+ exceptionInformation[2] = (DWORD) (0);
+
+ RaiseException (
+ EXCEPTION_PTHREAD_SERVICES,
+ 0,
+ 3,
+ exceptionInformation);
+
+#else /* _MSC_VER */
+
+# ifdef __cplusplus
+
+ throw Pthread_exception_cancel();
+
+# endif /* __cplusplus */
+
+#endif /* _MSC_VER */
+
+ /* Never reached */
+}
+
+
+/*
+ * _pthread_cancel_thread implements asynchronous cancellation.
+ */
+static void
+_pthread_cancel_thread(pthread_t thread)
+{
+ HANDLE threadH = thread->threadH;
+
+ (void) pthread_mutex_lock(&thread->cancelLock);
+
+ SuspendThread(threadH);
+
+ if (WaitForSingleObject(threadH, 0) == WAIT_TIMEOUT)
+ {
+ CONTEXT context;
+ context.ContextFlags = CONTEXT_CONTROL;
+ GetThreadContext(threadH, &context);
+ context.Eip = (DWORD) _pthread_cancel_self;
+ SetThreadContext(threadH, &context);
+ ResumeThread(threadH);
+ }
+
+ (void) pthread_mutex_unlock(&thread->cancelLock);
+}
+
+
int
pthread_setcancelstate (int state, int *oldstate)
/*
@@ -37,8 +95,8 @@ pthread_setcancelstate (int state, int *oldstate)
* 'oldstate'
*
* PARAMETERS
- * type,
- * oldtype
+ * state,
+ * oldstate
* PTHREAD_CANCEL_ENABLE
* cancellation is enabled,
*
@@ -67,28 +125,44 @@ pthread_setcancelstate (int state, int *oldstate)
* ------------------------------------------------------
*/
{
- pthread_t self;
- int result;
+ int result = 0;
+ pthread_t self = (pthread_t) pthread_getspecific (_pthread_selfThreadKey);
- if (((self = pthread_self ()) != NULL) &&
- (state == PTHREAD_CANCEL_ENABLE ||
- state == PTHREAD_CANCEL_DISABLE))
+ if (self == NULL
+ || (state != PTHREAD_CANCEL_ENABLE
+ && state != PTHREAD_CANCEL_DISABLE))
{
+ return EINVAL;
+ }
- if (oldstate != NULL)
- {
- *oldstate = self->cancelState;
- }
-
- self->cancelState = state;
- result = 0;
+ /*
+ * Lock for async-cancel safety.
+ */
+ (void) pthread_mutex_lock(&self->cancelLock);
+ if (oldstate != NULL)
+ {
+ *oldstate = self->cancelState;
}
- else
+
+ self->cancelState = state;
+
+ /*
+ * Check if there is a pending asynchronous cancel
+ */
+ if (self->cancelState == PTHREAD_CANCEL_ENABLE
+ && self->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS
+ && WaitForSingleObject(self->cancelEvent, 0) == WAIT_OBJECT_0)
{
- result = EINVAL;
+ ResetEvent(self->cancelEvent);
+ (void) pthread_mutex_unlock(&self->cancelLock);
+ _pthread_cancel_self();
+
+ /* Never reached */
}
+ (void) pthread_mutex_unlock(&self->cancelLock);
+
return (result);
} /* pthread_setcancelstate */
@@ -135,28 +209,44 @@ pthread_setcanceltype (int type, int *oldtype)
* ------------------------------------------------------
*/
{
- pthread_t self;
- int result;
+ int result = 0;
+ pthread_t self = (pthread_t) pthread_getspecific (_pthread_selfThreadKey);
- if (((self = pthread_self ()) != NULL) &&
- (type == PTHREAD_CANCEL_DEFERRED ||
- type == PTHREAD_CANCEL_ASYNCHRONOUS))
+ if (self == NULL
+ || (type != PTHREAD_CANCEL_DEFERRED
+ && type != PTHREAD_CANCEL_ASYNCHRONOUS))
{
+ return EINVAL;
+ }
- if (oldtype != NULL)
- {
- *oldtype = self->cancelType;
- }
-
- self->cancelType = type;
- result = 0;
+ /*
+ * Lock for async-cancel safety.
+ */
+ (void) pthread_mutex_lock(&self->cancelLock);
+ if (oldtype != NULL)
+ {
+ *oldtype = self->cancelType;
}
- else
+
+ self->cancelType = type;
+
+ /*
+ * Check if there is a pending asynchronous cancel
+ */
+ if (self->cancelState == PTHREAD_CANCEL_ENABLE
+ && self->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS
+ && WaitForSingleObject(self->cancelEvent, 0) == WAIT_OBJECT_0)
{
- result = EINVAL;
+ ResetEvent(self->cancelEvent);
+ (void) pthread_mutex_unlock(&self->cancelLock);
+ _pthread_cancel_self();
+
+ /* Never reached */
}
+ (void) pthread_mutex_unlock(&self->cancelLock);
+
return (result);
} /* pthread_setcanceltype */
@@ -191,50 +281,42 @@ pthread_testcancel (void)
* ------------------------------------------------------
*/
{
- pthread_t self;
+ pthread_t self = (pthread_t) pthread_getspecific (_pthread_selfThreadKey);
- if ((self = (pthread_t) pthread_getspecific (_pthread_selfThreadKey))
- != NULL)
+ if (self != NULL
+ && self->cancelState == PTHREAD_CANCEL_ENABLE
+ && WaitForSingleObject (self->cancelEvent, 0) == WAIT_OBJECT_0
+ )
{
-
- if (self->cancelState == PTHREAD_CANCEL_ENABLE)
- {
-
- if (WaitForSingleObject (self->cancelEvent, 0) ==
- WAIT_OBJECT_0)
- {
- /*
- * Canceling!
- */
+ /*
+ * Canceling!
+ */
#ifdef _MSC_VER
- DWORD exceptionInformation[3];
+ DWORD exceptionInformation[3];
- exceptionInformation[0] = (DWORD) (_PTHREAD_EPS_CANCEL);
- exceptionInformation[1] = (DWORD) (0);
- exceptionInformation[2] = (DWORD) (0);
+ exceptionInformation[0] = (DWORD) (_PTHREAD_EPS_CANCEL);
+ exceptionInformation[1] = (DWORD) (0);
+ exceptionInformation[2] = (DWORD) (0);
- RaiseException (
- EXCEPTION_PTHREAD_SERVICES,
- 0,
- 3,
- exceptionInformation);
+ RaiseException (
+ EXCEPTION_PTHREAD_SERVICES,
+ 0,
+ 3,
+ exceptionInformation);
#else /* _MSC_VER */
#ifdef __cplusplus
- throw Pthread_exception_cancel();
+ throw Pthread_exception_cancel();
#endif /* __cplusplus */
#endif /* _MSC_VER */
- }
- }
}
-
} /* pthread_testcancel */
int
@@ -262,8 +344,52 @@ pthread_cancel (pthread_t thread)
*/
{
int result;
+ int cancel_self;
+ pthread_t self;
+
+ if (thread == NULL )
+ {
+ return ESRCH;
+ }
- if (thread != NULL)
+ result = 0;
+ self = (pthread_t) pthread_getspecific (_pthread_selfThreadKey);
+
+ /*
+ * FIXME!!
+ *
+ * Can a thread cancel itself?
+ *
+ * The standard doesn't
+ * specify an error to be returned if the target
+ * thread is itself.
+ *
+ * If it may, then we need to ensure that a thread can't
+ * deadlock itself trying to cancel itself asyncronously
+ * (pthread_cancel is required to be an async-cancel
+ * safe function).
+ */
+ cancel_self = pthread_equal(thread, self);
+
+ /*
+ * Lock for async-cancel safety.
+ */
+ (void) pthread_mutex_lock(&self->cancelLock);
+
+ if (thread->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS
+ && thread->cancelState == PTHREAD_CANCEL_ENABLE )
+ {
+ if (cancel_self)
+ {
+ (void) pthread_mutex_unlock(&self->cancelLock);
+ _pthread_cancel_self();
+
+ /* Never reached */
+ }
+
+ _pthread_cancel_thread(thread);
+ }
+ else
{
/*
* Set for deferred cancellation.
@@ -272,17 +398,13 @@ pthread_cancel (pthread_t thread)
{
result = ESRCH;
}
- else
- {
- result = 0;
- }
-
- }
- else
- {
- result = ESRCH;
}
+ (void) pthread_mutex_unlock(&self->cancelLock);
+
return (result);
+
}
+
+
diff --git a/cleanup.c b/cleanup.c
index c46f1e9..b92d502 100644
--- a/cleanup.c
+++ b/cleanup.c
@@ -78,7 +78,7 @@ pthread_pop_cleanup (int execute)
*/
(*cleanup->routine) (cleanup->arg);
}
- __except (EXCEPTION_EXECUTE_HANDLER)
+ _pthread__except (EXCEPTION_EXECUTE_HANDLER)
{
/*
* A system unexpected exception had occurred
@@ -98,7 +98,7 @@ pthread_pop_cleanup (int execute)
*/
(*cleanup->routine) (cleanup->arg);
}
- catch(...)
+ _pthread_catch(...)
{
/*
* A system unexpected exception had occurred
diff --git a/configure.in b/configure.in
index 5c4dfac..114891e 100644
--- a/configure.in
+++ b/configure.in
@@ -20,4 +20,4 @@ then
fi
fi
-AC_OUTPUT(Makefile)
+AC_OUTPUT(GNUmakefile)
diff --git a/create.c b/create.c
index 0575643..79085bf 100644
--- a/create.c
+++ b/create.c
@@ -73,19 +73,11 @@ pthread_create (pthread_t * tid,
ThreadParms *parms = NULL;
long stackSize;
- if ((thread = (pthread_t) calloc (1, sizeof (*thread))) ==
- NULL)
+ if ((thread = _pthread_new()) == NULL)
{
goto FAIL0;
}
- /*
- * Setup standard default state.
- */
- thread->detachState = PTHREAD_CREATE_JOINABLE;
- thread->cancelState = PTHREAD_CANCEL_ENABLE;
- thread->cancelType = PTHREAD_CANCEL_DEFERRED;
-
thread->cancelEvent =
CreateEvent (
0,
diff --git a/dll.c b/dll.c
index add5832..9d59379 100644
--- a/dll.c
+++ b/dll.c
@@ -107,6 +107,26 @@ DllMain (
}
DeleteCriticalSection(&cs);
}
+
+ if (_pthread_try_enter_critical_section == NULL)
+ {
+ /*
+ * If TryEnterCriticalSection is not being used, then free
+ * the kernel32.dll handle now, rather than leaving it until
+ * DLL_PROCESS_DETACH.
+ *
+ * Note: this is not a pedantic exercise in freeing unused
+ * resources! It is a work-around for a bug in Windows 95
+ * (see microsoft knowledge base article, Q187684) which
+ * does Bad Things when FreeLibrary is called within
+ * the DLL_PROCESS_DETACH code, in certain situations.
+ * Since w95 just happens to be a platform which does not
+ * provide TryEnterCriticalSection, the bug will be
+ * effortlessly avoided.
+ */
+ (void) FreeLibrary(_pthread_h_kernel32);
+ _pthread_h_kernel32 = 0;
+ }
break;
case DLL_THREAD_ATTACH:
@@ -150,7 +170,10 @@ DllMain (
*/
_pthread_processTerminate ();
- (void) FreeLibrary(_pthread_h_kernel32);
+ if (_pthread_h_kernel32)
+ {
+ (void) FreeLibrary(_pthread_h_kernel32);
+ }
}
}
diff --git a/global.c b/global.c
index c0173bb..147ca13 100644
--- a/global.c
+++ b/global.c
@@ -49,6 +49,3 @@ CRITICAL_SECTION _pthread_cond_test_init_lock;
* created read/write locks.
*/
CRITICAL_SECTION _pthread_rwlock_test_init_lock;
-
-
-
diff --git a/implement.h b/implement.h
index bc76dec..4012301 100644
--- a/implement.h
+++ b/implement.h
@@ -73,6 +73,7 @@ struct pthread_t_ {
void *parms;
int ptErrno;
int detachState;
+ pthread_mutex_t cancelLock; /* Used for async-cancel safety */
int cancelState;
int cancelType;
HANDLE cancelEvent;
@@ -287,19 +288,28 @@ struct ThreadKeyAssoc {
#define _PTHREAD_EPS_CANCEL 0
#define _PTHREAD_EPS_EXIT 1
+/*
+ * '__except' was redefined in pthread.h. We use the real one internally.
+ */
+#ifdef __except
+#undef __except
+#define _pthread__except __except
+#endif
+
#else
#ifdef __cplusplus
-
/*
- * Exceptions similar to the SEH exceptions above.
+ * 'catch' was redefined in pthread.h. We use the real one internally.
*/
-class Pthread_exception_cancel {};
-class Pthread_exception_exit {};
+#ifdef catch
+#undef catch
+#define _pthread_catch catch
+#endif
#else /* __cplusplus */
-#warning File __FILE__, Line __LINE__: Cancellation not supported under C.
+#warning File __FILE__, Line __LINE__: Cancellation not supported if library compiled as C.
#endif /* __cplusplus */
@@ -342,6 +352,8 @@ void _pthread_threadDestroy (pthread_t tid);
void _pthread_cleanupStack (void);
+pthread_t _pthread_new (void);
+
#if ! defined (__MINGW32__) || defined (__MSVCRT__)
unsigned PT_STDCALL
#else
diff --git a/misc.c b/misc.c
index 84dc6eb..6087a6b 100644
--- a/misc.c
+++ b/misc.c
@@ -154,17 +154,30 @@ pthread_self (void)
* Need to create an implicit 'self' for the currently
* executing thread.
*/
- self = (pthread_t) calloc (1, sizeof (*self));
+ self = _pthread_new();
if (self != NULL)
{
+ /*
+ * This is a non-POSIX thread which has chosen to call
+ * a POSIX threads function for some reason. We assume that
+ * it isn't joinable, but we do assume that it's
+ * (deferred) cancelable.
+ */
self->implicit = 1;
self->detachState = PTHREAD_CREATE_DETACHED;
-
self->thread = GetCurrentThreadId ();
#ifdef NEED_DUPLICATEHANDLE
- /* DuplicateHandle does not exist on WinCE */
+ /*
+ * DuplicateHandle does not exist on WinCE.
+ *
+ * NOTE:
+ * GetCurrentThread only returns a pseudo-handle
+ * which is only valid in the current thread context.
+ * Therefore, you should not use pass the handle to
+ * other threads for whatever purpose.
+ */
self->threadH = GetCurrentThread();
#else
if( !DuplicateHandle(
@@ -366,9 +379,30 @@ pthreadCancelableTimedWait (HANDLE waitHandle, DWORD timeout)
return (CancelableWait(waitHandle, timeout));
}
+
+pthread_t
+_pthread_new (void)
+{
+ pthread_t new;
+
+ new = (pthread_t) calloc (1, sizeof (*new));
+
+ if (new != NULL)
+ {
+ new->detachState = PTHREAD_CREATE_JOINABLE;
+ new->cancelState = PTHREAD_CANCEL_ENABLE;
+ new->cancelType = PTHREAD_CANCEL_DEFERRED;
+ new->cancelLock = PTHREAD_MUTEX_INITIALIZER;
+ }
+
+ return new;
+
+}
+
+
#ifdef NEED_CALLOC
-void
-*_pthread_calloc(size_t n, size_t s) {
+void *
+_pthread_calloc(size_t n, size_t s) {
unsigned int m = n*s;
void *p;
diff --git a/private.c b/private.c
index 2e0f53f..5af936b 100644
--- a/private.c
+++ b/private.c
@@ -215,7 +215,7 @@ _pthread_threadStart (ThreadParms * threadParms)
*/
status = (*start) (arg);
}
- __except (ExceptionFilter(GetExceptionInformation(), ei))
+ _pthread__except (ExceptionFilter(GetExceptionInformation(), ei))
{
DWORD ec = GetExceptionCode();
@@ -254,21 +254,21 @@ _pthread_threadStart (ThreadParms * threadParms)
*/
status = self->exitStatus = (*start) (arg);
}
- catch (Pthread_exception_cancel)
+ _pthread_catch (Pthread_exception_cancel)
{
/*
* Thread was cancelled.
*/
status = PTHREAD_CANCELED;
}
- catch (Pthread_exception_exit)
+ _pthread_catch (Pthread_exception_exit)
{
/*
* Thread was exited via pthread_exit().
*/
status = self->exitStatus;
}
- catch (...)
+ _pthread_catch (...)
{
/*
* A system unexpected exception had occurred running the user's
@@ -289,6 +289,7 @@ _pthread_threadStart (ThreadParms * threadParms)
#endif /* _MSC_VER */
+ (void) pthread_mutex_destroy(&self->cancelLock);
_pthread_callUserDestroyRoutines(self);
#if ! defined (__MINGW32__) || defined (__MSVCRT__)
@@ -536,7 +537,7 @@ _pthread_callUserDestroyRoutines (pthread_t thread)
*/
(*(k->destructor)) (value);
}
- __except (EXCEPTION_EXECUTE_HANDLER)
+ _pthread__except (EXCEPTION_EXECUTE_HANDLER)
{
/*
* A system unexpected exception had occurred
@@ -555,7 +556,7 @@ _pthread_callUserDestroyRoutines (pthread_t thread)
*/
(*(k->destructor)) (value);
}
- catch (...)
+ _pthread_catch (...)
{
/*
* A system unexpected exception had occurred
@@ -686,15 +687,9 @@ _pthread_sem_timedwait (sem_t * sem, const struct timespec * abstime)
struct timespec currSysTime;
#else /* NEED_FTIME */
-#if defined(__MINGW32__)
-
- struct timeb currSysTime;
-
-#else /* __MINGW32__ */
struct _timeb currSysTime;
-#endif /* __MINGW32__ */
#endif /* NEED_FTIME */
const DWORD NANOSEC_PER_MILLISEC = 1000000;
@@ -787,3 +782,18 @@ _pthread_sem_timedwait (sem_t * sem, const struct timespec * abstime)
return 0;
} /* _pthread_sem_timedwait */
+
+
+DWORD
+_pthread_get_exception_services_code(void)
+{
+#ifdef _MSC_VER
+
+ return EXCEPTION_PTHREAD_SERVICES;
+
+#else
+
+ return NULL;
+
+#endif
+}
diff --git a/pthread.def b/pthread.def
index 9a4efac..80b6290 100644
--- a/pthread.def
+++ b/pthread.def
@@ -1,5 +1,5 @@
; pthread.def
-; Last updated: $Date: 1999/11/04 17:18:43 $
+; Last updated: $Date: 2000/01/04 10:19:28 $
; Currently unimplemented functions are commented out.
@@ -109,4 +109,8 @@ pthreadCancelableTimedWait
;
pthread_push_cleanup
pthread_pop_cleanup
-
+;
+; Not for use directly. Needed by macros in pthread.h
+; to return internal SEH code.
+;
+_pthread_get_exception_services_code
diff --git a/pthread.h b/pthread.h
index c8076be..bed1ae3 100644
--- a/pthread.h
+++ b/pthread.h
@@ -285,16 +285,6 @@ struct timespec {
#define PT_STDCALL __stdcall
#endif
-
-/*
- * This should perhaps be in autoconf or
- * possibly fixed in Mingw32 to
- * correspond to the Windows headers.
- */
-#ifdef __MINGW32__
-#define _timeb timeb
-#endif
-
#ifdef __cplusplus
extern "C"
{
@@ -968,6 +958,46 @@ int * _errno( void );
rand()
+#ifdef _MSC_VER
+/*
+ * Get internal SEH tag
+ */
+DWORD _pthread_get_exception_services_code(void);
+
+/*
+ * Redefine the SEH __except keyword to ensure that applications
+ * propagate our internal exceptions up to the library's internal handlers.
+ */
+#define __except(E) \
+ __except((GetExceptionCode() == _pthread_get_exception_services_code()) \
+ ? EXCEPTION_CONTINUE_SEARCH : (E))
+
+#endif
+
+#ifdef __cplusplus
+/*
+ * Internal exceptions
+ */
+class Pthread_exception_cancel {};
+class Pthread_exception_exit {};
+
+/*
+ * Redefine the C++ catch keyword to ensure that applications
+ * propagate our internal exceptions up to the library's internal handlers.
+ */
+#define catch(E) \
+ catch(Pthread_exception_cancel) \
+ { \
+ throw; \
+ } \
+ catch(Pthread_exception_exit) \
+ { \
+ throw; \
+ } \
+ catch(E)
+
+#endif
+
#ifdef __cplusplus
} /* End of extern "C" */
#endif /* __cplusplus */
diff --git a/sync.c b/sync.c
index 06dffd9..f2d1b06 100644
--- a/sync.c
+++ b/sync.c
@@ -124,14 +124,32 @@ pthread_join (pthread_t thread, void **value_ptr)
}
else
{
+#if 0
DWORD stat;
stat = WaitForSingleObject (thread->threadH, INFINITE);
+#else
+ /*
+ * Pthread_join is a cancelation point.
+ * If we are cancelled then our target thread must not be
+ * detached (destroyed). This is guarranteed because
+ * pthreadCancelableWait will not return if we
+ * are cancelled.
+ */
+ result = pthreadCancelableWait(thread->threadH);
+#endif
+
+ if (
+#if 0
+ stat == WAIT_OBJECT_0
+#else
+ result == 0
+#endif
+ )
+ {
#if ! defined (__MINGW32__) || defined (__MSVCRT__)
- if (stat == WAIT_OBJECT_0)
- {
if (value_ptr != NULL
&& !GetExitCodeThread (thread->threadH, (LPDWORD) value_ptr))
{
@@ -145,31 +163,33 @@ pthread_join (pthread_t thread, void **value_ptr)
*/
_pthread_threadDestroy (thread);
}
- }
- else
- {
- result = ESRCH;
- }
#else /* __MINGW32__ && ! __MSVCRT__ */
- /*
- * If using CRTDLL, the thread may have exited, and endthread
- * will have closed the handle.
- */
- if (value_ptr != NULL)
- *value_ptr = self->exitStatus;
+ /*
+ * If using CRTDLL, the thread may have exited, and endthread
+ * will have closed the handle.
+ */
+ if (value_ptr != NULL)
+ {
+ *value_ptr = self->exitStatus;
+ }
- /*
- * The result of making multiple simultaneous calls to
- * pthread_join() specifying the same target is undefined.
- */
- _pthread_threadDestroy (thread);
+ /*
+ * The result of making multiple simultaneous calls to
+ * pthread_join() specifying the same target is undefined.
+ */
+ _pthread_threadDestroy (thread);
#endif /* __MINGW32__ && ! __MSVCRT__ */
+
+ }
+ else
+ {
+ result = ESRCH;
+ }
}
return (result);
} /* pthread_join */
-
diff --git a/tests/ChangeLog b/tests/ChangeLog
index 5e8fca9..cd155f8 100644
--- a/tests/ChangeLog
+++ b/tests/ChangeLog
@@ -1,3 +1,22 @@
+2000-01-04 Ross Johnson <rpj@special.ise.canberra.edu.au>
+
+ * cancel4.c: New; Test cancelation does not occur in deferred
+ cancelation threads with no cancelation points.
+
+ * cancel3.c: New; Test asynchronous cancelation.
+
+ * context1.c: New; Test context switching method for async
+ cancelation.
+
+1999-11-23 Ross Johnson <rpj@special.ise.canberra.edu.au>
+
+ * test.h: Add header includes; include local header versions rather
+ than system versions; rearrange the assert macro defines.
+
+1999-11-07 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
+
+ * loadfree.c: New. Test loading and freeing the library (DLL).
+
1999-10-30 Ross Johnson <rpj@ixobrychus.canberra.edu.au>
* cancel1.c: New. Test pthread_setcancelstate and
diff --git a/tests/Makefile b/tests/Makefile
index 7056d2b..8820aac 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -40,9 +40,10 @@ TESTS = mutex1 condvar1 condvar2 exit1 create1 equal1 \
exit2 exit3 \
join1 join2 mutex2 mutex3 \
count1 once1 tsd1 self1 self2 cancel1 eyal1 \
- condvar3 condvar4 condvar5 condvar6 condvar7 condvar8 \
+ condvar3 condvar4 condvar5 condvar6 condvar7 condvar8 condvar9 \
errno1 \
- rwlock1 rwlock2 rwlock3 rwlock4 rwlock5 rwlock6
+ rwlock1 rwlock2 rwlock3 rwlock4 rwlock5 rwlock6 \
+ context1 cancel3 cancel4
PASSES = $(TESTS:%=%.pass)
@@ -73,6 +74,7 @@ condvar5.pass: condvar4.pass
condvar6.pass: condvar5.pass
condvar7.pass: condvar6.pass
condvar8.pass: condvar6.pass
+condvar9.pass: condvar6.pass
errno1.pass: mutex3.pass
rwlock1.pass: condvar6.pass
rwlock2.pass: rwlock1.pass
@@ -80,6 +82,9 @@ rwlock3.pass: rwlock2.pass
rwlock4.pass: rwlock3.pass
rwlock5.pass: rwlock4.pass
rwlock6.pass: rwlock5.pass
+context1.pass: cancel2.pass
+cancel3.pass: context1.pass
+cancel4.pass: cancel3.pass
%.pass: %.exe $(LIB) $(DLL) $(HDR)
$*
@@ -99,5 +104,7 @@ clean:
- $(RM) semaphore.h
- $(RM) sched.h
- $(RM) *.a
+ - $(RM) *.e
- $(RM) *.exe
- $(RM) *.pass
+
diff --git a/tests/cancel2.c b/tests/cancel2.c
new file mode 100644
index 0000000..1a10c64
--- /dev/null
+++ b/tests/cancel2.c
@@ -0,0 +1,216 @@
+/*
+ * File: cancel2.c
+ *
+ * Test Synopsis: Test SEH or C++ cancel exception handling within
+ * application exception blocks.
+ *
+ * Test Method (Validation or Falsification):
+ * -
+ *
+ * Requirements Tested:
+ * -
+ *
+ * Features Tested:
+ * -
+ *
+ * Cases Tested:
+ * -
+ *
+ * Description:
+ * -
+ *
+ * Environment:
+ * -
+ *
+ * Input:
+ * - None.
+ *
+ * Output:
+ * - File name, Line number, and failed expression on failure.
+ * - No output on success.
+ *
+ * Assumptions:
+ * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock
+ * pthread_testcancel, pthread_cancel, pthread_join
+ *
+ * Pass Criteria:
+ * - Process returns zero exit status.
+ *
+ * Fail Criteria:
+ * - Process returns non-zero exit status.
+ */
+
+#if defined(_MSC_VER) || defined(__cplusplus)
+
+#include "test.h"
+
+/*
+ * Create NUMTHREADS threads in addition to the Main thread.
+ */
+enum {
+ NUMTHREADS = 4
+};
+
+typedef struct bag_t_ bag_t;
+struct bag_t_ {
+ int threadnum;
+ int started;
+ /* Add more per-thread state variables here */
+};
+
+static bag_t threadbag[NUMTHREADS + 1];
+
+static pthread_mutex_t waitLock = PTHREAD_MUTEX_INITIALIZER;
+
+void *
+mythread(void * arg)
+{
+ int result = 0;
+ bag_t * bag = (bag_t *) arg;
+
+ assert(bag == &threadbag[bag->threadnum]);
+ assert(bag->started == 0);
+ bag->started = 1;
+
+ /* Set to known state and type */
+
+ assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);
+
+ switch (bag->threadnum % 2)
+ {
+ case 0:
+ assert(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);
+ result = 0;
+ break;
+ case 1:
+ assert(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) == 0);
+ result = 1;
+ break;
+ }
+
+#ifdef _MSC_VER
+ __try
+#else
+ try
+#endif
+ {
+ /* Wait for go from main */
+ assert(pthread_mutex_lock(&waitLock) == 0);
+ assert(pthread_mutex_unlock(&waitLock) == 0);
+ sched_yield();
+
+ for (;;)
+ {
+ pthread_testcancel();
+ }
+ }
+#ifdef _MSC_VER
+ __except(EXCEPTION_EXECUTE_HANDLER)
+#else
+ catch(...)
+#endif
+ {
+ /*
+ * Should not get into here.
+ */
+ printf("SEH code=%x, MyCode=%x\n", GetExceptionCode(), _pthread_get_exception_services_code());
+ result += 100;
+ }
+
+ /*
+ * Should not get to here either.
+ */
+ result += 1000;
+
+ return (void *) result;
+}
+
+int
+main()
+{
+ int failed = 0;
+ int i;
+ pthread_t t[NUMTHREADS + 1];
+
+ assert((t[0] = pthread_self()) != NULL);
+ assert(pthread_mutex_lock(&waitLock) == 0);
+
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ threadbag[i].started = 0;
+ threadbag[i].threadnum = i;
+ assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);
+ }
+
+ /*
+ * Code to control or munipulate child threads should probably go here.
+ */
+ Sleep(500);
+
+ assert(pthread_mutex_unlock(&waitLock) == 0);
+
+ Sleep(500);
+
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ assert(pthread_cancel(t[i]) == 0);
+ }
+
+ /*
+ * Give threads time to run.
+ */
+ Sleep(NUMTHREADS * 100);
+
+ /*
+ * Standard check that all threads started.
+ */
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ if (!threadbag[i].started)
+ {
+ failed |= !threadbag[i].started;
+ fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started);
+ }
+ }
+
+ assert(!failed);
+
+ /*
+ * Check any results here. Set "failed" and only print output on failure.
+ */
+ failed = 0;
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ int fail = 0;
+ int result = 0;
+
+ assert(pthread_join(t[i], (void *) &result) == 0);
+ fail = (result != (int) PTHREAD_CANCELED);
+ if (fail)
+ {
+ fprintf(stderr, "Thread %d: started %d: location %d: cancel type %s\n",
+ i,
+ threadbag[i].started,
+ result,
+ ((result % 2) == 0) ? "ASYNCHRONOUS" : "DEFERRED");
+ }
+ failed |= fail;
+ }
+
+ assert(!failed);
+
+ /*
+ * Success.
+ */
+ return 0;
+}
+
+#else /* defined(_MSC_VER) || defined(__cplusplus) */
+
+int
+main()
+{
+ return 0;
+}
+
+#endif /* defined(_MSC_VER) || defined(__cplusplus) */
diff --git a/tests/cancel3.c b/tests/cancel3.c
new file mode 100644
index 0000000..ff1d286
--- /dev/null
+++ b/tests/cancel3.c
@@ -0,0 +1,180 @@
+/*
+ * File: cancel3.c
+ *
+ * Test Synopsis: Test asynchronous cancelation.
+ *
+ * Test Method (Validation or Falsification):
+ * -
+ *
+ * Requirements Tested:
+ * -
+ *
+ * Features Tested:
+ * -
+ *
+ * Cases Tested:
+ * -
+ *
+ * Description:
+ * -
+ *
+ * Environment:
+ * -
+ *
+ * Input:
+ * - None.
+ *
+ * Output:
+ * - File name, Line number, and failed expression on failure.
+ * - No output on success.
+ *
+ * Assumptions:
+ * - have working pthread_create, pthread_self, pthread_mutex_lock/unlock
+ * pthread_testcancel, pthread_cancel, pthread_join
+ *
+ * Pass Criteria:
+ * - Process returns zero exit status.
+ *
+ * Fail Criteria:
+ * - Process returns non-zero exit status.
+ */
+
+#if defined(_MSC_VER) || defined(__cplusplus)
+
+#include "test.h"
+
+/*
+ * Create NUMTHREADS threads in addition to the Main thread.
+ */
+enum {
+ NUMTHREADS = 4
+};
+
+typedef struct bag_t_ bag_t;
+struct bag_t_ {
+ int threadnum;
+ int started;
+ /* Add more per-thread state variables here */
+ int count;
+};
+
+static bag_t threadbag[NUMTHREADS + 1];
+
+static pthread_mutex_t waitLock = PTHREAD_MUTEX_INITIALIZER;
+
+void *
+mythread(void * arg)
+{
+ int result = 0;
+ bag_t * bag = (bag_t *) arg;
+
+ assert(bag == &threadbag[bag->threadnum]);
+ assert(bag->started == 0);
+ bag->started = 1;
+
+ /* Set to known state and type */
+
+ assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);
+
+ assert(pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL) == 0);
+
+ /*
+ * We wait up to 10 seconds, waking every 0.1 seconds,
+ * for a cancelation to be applied to us.
+ */
+ for (bag->count = 0; bag->count < 100; bag->count++)
+ Sleep(100);
+
+ return result;
+}
+
+int
+main()
+{
+ int failed = 0;
+ int i;
+ pthread_t t[NUMTHREADS + 1];
+
+ assert((t[0] = pthread_self()) != NULL);
+
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ threadbag[i].started = 0;
+ threadbag[i].threadnum = i;
+ assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);
+ }
+
+ /*
+ * Code to control or munipulate child threads should probably go here.
+ */
+ Sleep(500);
+
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ assert(pthread_cancel(t[i]) == 0);
+ }
+
+ /*
+ * Give threads time to run.
+ */
+ Sleep(NUMTHREADS * 100);
+
+ /*
+ * Standard check that all threads started.
+ */
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ if (!threadbag[i].started)
+ {
+ failed |= !threadbag[i].started;
+ fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started);
+ }
+ }
+
+ assert(!failed);
+
+ /*
+ * Check any results here. Set "failed" and only print output on failure.
+ */
+ failed = 0;
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ int fail = 0;
+ int result = 0;
+
+ /*
+ * The thread does not contain any cancelation points, so
+ * a return value of PTHREAD_CANCELED confirms that async
+ * cancelation succeeded.
+ */
+ assert(pthread_join(t[i], (void *) &result) == 0);
+
+ fail = (result != (int) PTHREAD_CANCELED);
+
+ if (fail)
+ {
+ fprintf(stderr, "Thread %d: started %d: count %d\n",
+ i,
+ threadbag[i].started,
+ threadbag[i].count);
+ }
+ failed = (failed || fail);
+ }
+
+ assert(!failed);
+
+ /*
+ * Success.
+ */
+ return 0;
+}
+
+#else /* defined(_MSC_VER) || defined(__cplusplus) */
+
+int
+main()
+{
+ return 0;
+}
+
+#endif /* defined(_MSC_VER) || defined(__cplusplus) */
diff --git a/tests/cancel4.c b/tests/cancel4.c
new file mode 100644
index 0000000..9ce6880
--- /dev/null
+++ b/tests/cancel4.c
@@ -0,0 +1,183 @@
+/*
+ * File: cancel4.c
+ *
+ * Test Synopsis: Test cancelation does not occur in deferred
+ * cancelation threads with no cancelation points.
+ *
+ * Test Method (Validation or Falsification):
+ * -
+ *
+ * Requirements Tested:
+ * -
+ *
+ * Features Tested:
+ * -
+ *
+ * Cases Tested:
+ * -
+ *
+ * Description:
+ * -
+ *
+ * Environment:
+ * -
+ *
+ * Input:
+ * - None.
+ *
+ * Output:
+ * - File name, Line number, and failed expression on failure.
+ * - No output on success.
+ *
+ * Assumptions:
+ * - pthread_create
+ * pthread_self
+ * pthread_cancel
+ * pthread_join
+ * pthread_setcancelstate
+ * pthread_setcanceltype
+ *
+ * Pass Criteria:
+ * - Process returns zero exit status.
+ *
+ * Fail Criteria:
+ * - Process returns non-zero exit status.
+ */
+
+#if defined(_MSC_VER) || defined(__cplusplus)
+
+#include "test.h"
+
+/*
+ * Create NUMTHREADS threads in addition to the Main thread.
+ */
+enum {
+ NUMTHREADS = 4
+};
+
+typedef struct bag_t_ bag_t;
+struct bag_t_ {
+ int threadnum;
+ int started;
+ /* Add more per-thread state variables here */
+ int count;
+};
+
+static bag_t threadbag[NUMTHREADS + 1];
+
+void *
+mythread(void * arg)
+{
+ int result = 0;
+ bag_t * bag = (bag_t *) arg;
+
+ assert(bag == &threadbag[bag->threadnum]);
+ assert(bag->started == 0);
+ bag->started = 1;
+
+ /* Set to known state and type */
+
+ assert(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) == 0);
+
+ assert(pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL) == 0);
+
+ /*
+ * We wait up to 10 seconds, waking every 0.1 seconds,
+ * for a cancelation to be applied to us.
+ */
+ for (bag->count = 0; bag->count < 100; bag->count++)
+ Sleep(100);
+
+ return result;
+}
+
+int
+main()
+{
+ int failed = 0;
+ int i;
+ pthread_t t[NUMTHREADS + 1];
+
+ assert((t[0] = pthread_self()) != NULL);
+
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ threadbag[i].started = 0;
+ threadbag[i].threadnum = i;
+ assert(pthread_create(&t[i], NULL, mythread, (void *) &threadbag[i]) == 0);
+ }
+
+ /*
+ * Code to control or munipulate child threads should probably go here.
+ */
+ Sleep(500);
+
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ assert(pthread_cancel(t[i]) == 0);
+ }
+
+ /*
+ * Give threads time to run.
+ */
+ Sleep(NUMTHREADS * 100);
+
+ /*
+ * Standard check that all threads started.
+ */
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ if (!threadbag[i].started)
+ {
+ failed |= !threadbag[i].started;
+ fprintf(stderr, "Thread %d: started %d\n", i, threadbag[i].started);
+ }
+ }
+
+ assert(!failed);
+
+ /*
+ * Check any results here. Set "failed" and only print output on failure.
+ */
+ failed = 0;
+ for (i = 1; i <= NUMTHREADS; i++)
+ {
+ int fail = 0;
+ int result = 0;
+
+ /*
+ * The thread does not contain any cancelation points, so
+ * a return value of PTHREAD_CANCELED indicates that async
+ * cancelation occurred.
+ */
+ assert(pthread_join(t[i], (void *) &result) == 0);
+
+ fail = (result == (int) PTHREAD_CANCELED);
+
+ if (fail)
+ {
+ fprintf(stderr, "Thread %d: started %d: count %d\n",
+ i,
+ threadbag[i].started,
+ threadbag[i].count);
+ }
+ failed = (failed || fail);
+ }
+
+ assert(!failed);
+
+ /*
+ * Success.
+ */
+ return 0;
+}
+
+#else /* defined(_MSC_VER) || defined(__cplusplus) */
+
+int
+main()
+{
+ return 0;
+}
+
+#endif /* defined(_MSC_VER) || defined(__cplusplus) */
diff --git a/tests/ccl.bat b/tests/ccl.bat
index 9dc72d9..473ff42 100644
--- a/tests/ccl.bat
+++ b/tests/ccl.bat
@@ -1,3 +1,9 @@
+REM Generate preprocessor output
+REM cl /E /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c ..\%1.c > ..\%1.e
+
+REM Generate object file
cl /W3 /MT /nologo /Yd /Zi -I. -D_WIN32_WINNT=0x400 -DSTDCALL=_stdcall -c ..\%1.c
+
+REM Generate executable
cl /Feaout.exe /Zi %1.obj .\pthread.lib
del %1.obj > nul:
diff --git a/tests/condvar2.c b/tests/condvar2.c
index b636a62..beae323 100644
--- a/tests/condvar2.c
+++ b/tests/condvar2.c
@@ -51,11 +51,7 @@ int
main()
{
struct timespec abstime = { 0, 0 };
-#if defined(__MINGW32__)
- struct timeb currSysTime;
-#else
struct _timeb currSysTime;
-#endif
const DWORD NANOSEC_PER_MILLISEC = 1000000;
assert(pthread_cond_init(&cv, NULL) == 0);
diff --git a/tests/condvar3.c b/tests/condvar3.c
index ab1080e..deb130a 100644
--- a/tests/condvar3.c
+++ b/tests/condvar3.c
@@ -75,11 +75,7 @@ main()
{
pthread_t t[NUMTHREADS];
struct timespec abstime = { 0, 0 };
-#if defined(__MINGW32__)
- struct timeb currSysTime;
-#else
struct _timeb currSysTime;
-#endif
const DWORD NANOSEC_PER_MILLISEC = 1000000;
assert((t[0] = pthread_self()) != NULL);
diff --git a/tests/condvar4.c b/tests/condvar4.c
index ecb5e2d..3feaebb 100644
--- a/tests/condvar4.c
+++ b/tests/condvar4.c
@@ -82,11 +82,7 @@ main()
{
pthread_t t[NUMTHREADS];
struct timespec abstime = { 0, 0 };
-#if defined(__MINGW32__)
- struct timeb currSysTime;
-#else
struct _timeb currSysTime;
-#endif
const DWORD NANOSEC_PER_MILLISEC = 1000000;
cvthing.shared = 0;
diff --git a/tests/condvar5.c b/tests/condvar5.c
index e3b5e94..d406a2b 100644
--- a/tests/condvar5.c
+++ b/tests/condvar5.c
@@ -81,11 +81,7 @@ main()
{
pthread_t t[NUMTHREADS];
struct timespec abstime = { 0, 0 };
-#if defined(__MINGW32__)
- struct timeb currSysTime;
-#else
struct _timeb currSysTime;
-#endif
const DWORD NANOSEC_PER_MILLISEC = 1000000;
cvthing.shared = 0;
diff --git a/tests/condvar6.c b/tests/condvar6.c
index 7518ca6..6acc666 100644
--- a/tests/condvar6.c
+++ b/tests/condvar6.c
@@ -112,11 +112,7 @@ main()
int i;
pthread_t t[NUMTHREADS + 1];
-#if defined(__MINGW32__)
- struct timeb currSysTime;
-#else
struct _timeb currSysTime;
-#endif
const DWORD NANOSEC_PER_MILLISEC = 1000000;
cvthing.shared = 0;
diff --git a/tests/condvar7.c b/tests/condvar7.c
index d81dd78..ae51a10 100644
--- a/tests/condvar7.c
+++ b/tests/condvar7.c
@@ -116,11 +116,7 @@ main()
int i;
pthread_t t[NUMTHREADS + 1];
-#if defined(__MINGW32__)
- struct timeb currSysTime;
-#else
struct _timeb currSysTime;
-#endif
const DWORD NANOSEC_PER_MILLISEC = 1000000;
cvthing.shared = 0;
diff --git a/tests/condvar8.c b/tests/condvar8.c
index 65da040..3522546 100644
--- a/tests/condvar8.c
+++ b/tests/condvar8.c
@@ -121,11 +121,7 @@ main()
int first, last;
pthread_t t[NUMTHREADS + 1];
-#if defined(__MINGW32__)
- struct timeb currSysTime;
-#else
struct _timeb currSysTime;
-#endif
const DWORD NANOSEC_PER_MILLISEC = 1000000;
assert((t[0] = pthread_self()) != NULL);
diff --git a/tests/condvar9.c b/tests/condvar9.c
index 49a8cab..9b4f2f8 100644
--- a/tests/condvar9.c
+++ b/tests/condvar9.c
@@ -123,11 +123,7 @@ main()
int canceledThreads = 0;
pthread_t t[NUMTHREADS + 1];
-#if defined(__MINGW32__)
- struct timeb currSysTime;
-#else
struct _timeb currSysTime;
-#endif
const DWORD NANOSEC_PER_MILLISEC = 1000000;
assert((t[0] = pthread_self()) != NULL);
diff --git a/tests/context1.c b/tests/context1.c
new file mode 100644
index 0000000..8f615e1
--- /dev/null
+++ b/tests/context1.c
@@ -0,0 +1,110 @@
+/*
+ * File: context1.c
+ *
+ * Test Synopsis: Test context switching method.
+ *
+ * Test Method (Validation or Falsification):
+ * -
+ *
+ * Requirements Tested:
+ * -
+ *
+ * Features Tested:
+ * -
+ *
+ * Cases Tested:
+ * -
+ *
+ * Description:
+ * -
+ *
+ * Environment:
+ * -
+ *
+ * Input:
+ * - None.
+ *
+ * Output:
+ * - File name, Line number, and failed expression on failure.
+ * - No output on success.
+ *
+ * Assumptions:
+ * - pthread_create
+ * pthread_exit
+ *
+ * Pass Criteria:
+ * - Process returns zero exit status.
+ *
+ * Fail Criteria:
+ * - Process returns non-zero exit status.
+ */
+
+#include "test.h"
+#include "../implement.h"
+
+static int washere = 0;
+
+static void * func(void * arg)
+{
+ washere = 1;
+
+ Sleep(1000);
+
+ return 0;
+}
+
+static void
+anotherEnding ()
+{
+ /*
+ * Switched context
+ */
+ washere++;
+
+ pthread_exit(0);
+}
+
+int
+main()
+{
+ pthread_t t;
+ HANDLE hThread;
+
+ assert(pthread_create(&t, NULL, func, NULL) == 0);
+
+ hThread = t->threadH;
+
+ Sleep(500);
+
+ SuspendThread(hThread);
+
+ if (WaitForSingleObject(hThread, 0) == WAIT_TIMEOUT)
+ {
+ /*
+ * Ok, thread did not exit before we got to it.
+ */
+ CONTEXT context;
+
+ context.ContextFlags = CONTEXT_CONTROL;
+
+ GetThreadContext(hThread, &context);
+ /*
+ *_x86 only!!!
+ */
+ context.Eip = (DWORD) anotherEnding;
+ SetThreadContext(hThread, &context);
+ ResumeThread(hThread);
+ }
+ else
+ {
+ printf("Exited early\n");
+ fflush(stdout);
+ }
+
+ Sleep(1000);
+
+ assert(washere == 2);
+
+ return 0;
+}
+
diff --git a/tests/count1.c b/tests/count1.c
index 9783fcc..e88a955 100644
--- a/tests/count1.c
+++ b/tests/count1.c
@@ -7,7 +7,11 @@
#include "test.h"
+#if ! defined (__MINGW32__) || defined (__MSVCRT__)
#define NUMTHREADS (60)
+#else
+#define NUMTHREADS (59)
+#endif
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_t threads[NUMTHREADS];
diff --git a/tests/join2.c b/tests/join2.c
index 281a0df..8b1636c 100644
--- a/tests/join2.c
+++ b/tests/join2.c
@@ -29,7 +29,11 @@ main(int argc, char * argv[])
for (i = 0; i < 4; i++)
{
assert(pthread_join(id[i], (void *) &result) == 0);
+#if ! defined (__MINGW32__) || defined (__MSVCRT__)
+ /* CRTDLL _beginthread doesn't support return value, so
+ the assertion is guaranteed to fail. */
assert(result == i);
+#endif
}
/* Success. */
diff --git a/tests/loadfree.c b/tests/loadfree.c
new file mode 100644
index 0000000..3aba61b
--- /dev/null
+++ b/tests/loadfree.c
@@ -0,0 +1,38 @@
+/*
+ * From: Todd Owen <towen@lucidcalm.dropbear.id.au>
+ * To: pthreads-win32@sourceware.cygnus.com
+ * Subject: invalid page fault when using LoadLibrary/FreeLibrary
+ *
+ * hi,
+ * for me, pthread.dll consistently causes an "invalid page fault in
+ * kernel32.dll" when I load it "explicitly"...to be precise, loading (with
+ * LoadLibrary) isn't a problem, it gives the error when I call FreeLibrary.
+ * I guess that the dll's cleanup must be causing the error.
+ *
+ * Implicit linkage of the dll has never given me this problem. Here's a
+ * program (console application) that gives me the error.
+ *
+ * I compile with: mingw32 (gcc-2.95 release), with the MSVCRT add-on (not
+ * that the compiler should make much difference in this case).
+ * PTHREAD.DLL: is the precompiled 1999-11-02 one (I tried an older one as
+ * well, with the same result).
+ *
+ * Fascinatingly, if you have your own dll (mycode.dll) which implicitly
+ * loads pthread.dll, and then do LoadLibrary/FreeLibrary on _this_ dll, the
+ * same thing happens.
+ *
+ */
+
+#include "test.h"
+
+int main() {
+ HINSTANCE hinst;
+
+ assert((hinst = LoadLibrary("pthread")) != 0);
+
+ Sleep(100);
+
+ FreeLibrary(hinst);
+ return 0;
+}
+
diff --git a/tests/runall.bat b/tests/runall.bat
index 5c2d783..d8336c3 100644
--- a/tests/runall.bat
+++ b/tests/runall.bat
@@ -24,6 +24,7 @@ call runtest cl once1 create1
call runtest cl tsd1 join1
call runtest cl self2 create1
call runtest cl cancel1 self2
+call runtest cl cancel2 cancel1
call runtest cl eyal1 tsd1
call runtest cl condvar3 create1
call runtest cl condvar4 create1
@@ -39,6 +40,10 @@ call runtest cl rwlock3 rwlock2
call runtest cl rwlock4 rwlock3
call runtest cl rwlock5 rwlock4
call runtest cl rwlock6 rwlock5
+call runtest cl context1 cancel2
+call runtest cl cancel3 context1
+call runtest cl cancel4 cancel3
+call runtest cl loadfree _
if NOT EXIST *.notrun goto skip1
echo The following tests did not run (because prerequisite didn't pass?):
diff --git a/tests/test.h b/tests/test.h
index 36dc397..ffb8180 100644
--- a/tests/test.h
+++ b/tests/test.h
@@ -7,7 +7,9 @@
#ifndef _PTHREAD_TEST_H_
#define _PTHREAD_TEST_H_
-#include <pthread.h>
+#include "pthread.h"
+#include "sched.h"
+#include "semaphore.h"
#include <stdio.h>
char * error_string[] = {
@@ -61,28 +63,31 @@ char * error_string[] = {
* which pops up a dialog. We want to run in batch mode so
* we define our own assert macro.
*/
+#ifdef assert
+# undef assert
+#endif
+
#ifdef NDEBUG
-#define assert(e) ((void)0)
+# define assert(e) ((void)0)
#else /* NDEBUG */
-#ifdef assert
-# undef assert
-#endif
-
#ifndef ASSERT_TRACE
-#define ASSERT_TRACE 0
+# define ASSERT_TRACE 0
+#else
+# undef ASSERT_TRACE
+# define ASSERT_TRACE 1
#endif
-#define assert(e) \
- ((e) ? ((ASSERT_TRACE) ? fprintf(stderr, \
- "Assertion succeeded: (%s), file %s, line %d\n", \
- #e, __FILE__, (int) __LINE__), \
- fflush(stderr) : \
- (void) 0) : \
- (fprintf(stderr, "Assertion failed: (%s), file %s, line %d\n", \
- #e, __FILE__, (int) __LINE__), exit(1)))
+# define assert(e) \
+ ((e) ? ((ASSERT_TRACE) ? fprintf(stderr, \
+ "Assertion succeeded: (%s), file %s, line %d\n", \
+ #e, __FILE__, (int) __LINE__), \
+ fflush(stderr) : \
+ (void) 0) : \
+ (fprintf(stderr, "Assertion failed: (%s), file %s, line %d\n", \
+ #e, __FILE__, (int) __LINE__), exit(1)))
#endif /* NDEBUG */