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 */ |