diff options
| author | rpj <rpj> | 2000-01-04 10:19:28 +0000 | 
|---|---|---|
| committer | rpj <rpj> | 2000-01-04 10:19:28 +0000 | 
| commit | a378d97dc9d9eadaef00a9f01816948db5f3a796 (patch) | |
| tree | 654435cc0a85156c2a9b4793ab7d8e0da8424e32 | |
| parent | 27d833666dfd72cc6e74c3900d3e8e66321bea3a (diff) | |
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
| -rw-r--r-- | CONTRIBUTORS | 3 | ||||
| -rw-r--r-- | ChangeLog | 97 | ||||
| -rw-r--r-- | FAQ | 76 | ||||
| -rw-r--r-- | GNUmakefile | 69 | ||||
| -rw-r--r-- | Makefile | 112 | ||||
| -rw-r--r-- | Makefile.in | 4 | ||||
| -rw-r--r-- | Makefile.vc | 59 | ||||
| -rw-r--r-- | README | 13 | ||||
| -rw-r--r-- | cancel.c | 254 | ||||
| -rw-r--r-- | cleanup.c | 4 | ||||
| -rw-r--r-- | configure.in | 2 | ||||
| -rw-r--r-- | create.c | 10 | ||||
| -rw-r--r-- | dll.c | 25 | ||||
| -rw-r--r-- | global.c | 3 | ||||
| -rw-r--r-- | implement.h | 22 | ||||
| -rw-r--r-- | misc.c | 44 | ||||
| -rw-r--r-- | private.c | 34 | ||||
| -rw-r--r-- | pthread.def | 8 | ||||
| -rw-r--r-- | pthread.h | 50 | ||||
| -rw-r--r-- | sync.c | 58 | ||||
| -rw-r--r-- | tests/ChangeLog | 19 | ||||
| -rw-r--r-- | tests/Makefile | 11 | ||||
| -rw-r--r-- | tests/cancel2.c | 216 | ||||
| -rw-r--r-- | tests/cancel3.c | 180 | ||||
| -rw-r--r-- | tests/cancel4.c | 183 | ||||
| -rw-r--r-- | tests/ccl.bat | 6 | ||||
| -rw-r--r-- | tests/condvar2.c | 4 | ||||
| -rw-r--r-- | tests/condvar3.c | 4 | ||||
| -rw-r--r-- | tests/condvar4.c | 4 | ||||
| -rw-r--r-- | tests/condvar5.c | 4 | ||||
| -rw-r--r-- | tests/condvar6.c | 4 | ||||
| -rw-r--r-- | tests/condvar7.c | 4 | ||||
| -rw-r--r-- | tests/condvar8.c | 4 | ||||
| -rw-r--r-- | tests/condvar9.c | 4 | ||||
| -rw-r--r-- | tests/context1.c | 110 | ||||
| -rw-r--r-- | tests/count1.c | 4 | ||||
| -rw-r--r-- | tests/join2.c | 4 | ||||
| -rw-r--r-- | tests/loadfree.c | 38 | ||||
| -rw-r--r-- | tests/runall.bat | 5 | ||||
| -rw-r--r-- | tests/test.h | 35 | 
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 @@ -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. @@ -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) @@ -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): + @@ -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 @@ -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); +  } + + @@ -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) @@ -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, @@ -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); +                   }  	      }  	  } @@ -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 @@ -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; @@ -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 @@ -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 */ @@ -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 */ | 
