From a378d97dc9d9eadaef00a9f01816948db5f3a796 Mon Sep 17 00:00:00 2001
From: rpj <rpj>
Date: Tue, 4 Jan 2000 10:19:28 +0000
Subject: Main changes (see ChangeLog diff for details and attributions):- 
 - asynchronous cancellation added 	- attempt to hide internal exceptions
 from applications 	- kernel32 load/free problem fixed 	- new tests 
 - changes only to comments in some tests

---
 CONTRIBUTORS     |   3 +
 ChangeLog        |  97 +++++++++++++++++++++
 FAQ              |  76 +++++++++++++++++
 GNUmakefile      |  69 +++++++++++++++
 Makefile         | 112 +++++++++++-------------
 Makefile.in      |   4 +-
 Makefile.vc      |  59 +++++++++++++
 README           |  13 ++-
 cancel.c         | 254 ++++++++++++++++++++++++++++++++++++++++---------------
 cleanup.c        |   4 +-
 configure.in     |   2 +-
 create.c         |  10 +--
 dll.c            |  25 +++++-
 global.c         |   3 -
 implement.h      |  22 +++--
 misc.c           |  44 ++++++++--
 private.c        |  34 +++++---
 pthread.def      |   8 +-
 pthread.h        |  50 ++++++++---
 sync.c           |  58 ++++++++-----
 tests/ChangeLog  |  19 +++++
 tests/Makefile   |  11 ++-
 tests/cancel2.c  | 216 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/cancel3.c  | 180 +++++++++++++++++++++++++++++++++++++++
 tests/cancel4.c  | 183 +++++++++++++++++++++++++++++++++++++++
 tests/ccl.bat    |   6 ++
 tests/condvar2.c |   4 -
 tests/condvar3.c |   4 -
 tests/condvar4.c |   4 -
 tests/condvar5.c |   4 -
 tests/condvar6.c |   4 -
 tests/condvar7.c |   4 -
 tests/condvar8.c |   4 -
 tests/condvar9.c |   4 -
 tests/context1.c | 110 ++++++++++++++++++++++++
 tests/count1.c   |   4 +
 tests/join2.c    |   4 +
 tests/loadfree.c |  38 +++++++++
 tests/runall.bat |   5 ++
 tests/test.h     |  35 ++++----
 40 files changed, 1540 insertions(+), 250 deletions(-)
 create mode 100644 GNUmakefile
 create mode 100644 Makefile.vc
 create mode 100644 tests/cancel2.c
 create mode 100644 tests/cancel3.c
 create mode 100644 tests/cancel4.c
 create mode 100644 tests/context1.c
 create mode 100644 tests/loadfree.c

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 */
 
-- 
cgit v1.2.3