diff --git a/winsup/ChangeLog b/winsup/ChangeLog index b01edf1b3..da4792d41 100644 --- a/winsup/ChangeLog +++ b/winsup/ChangeLog @@ -1,3 +1,46 @@ +2002-06-25 Thomas Pfaff + + * include/pthread.h (PTHREAD_CANCELED): Defined a reasonable + value. + * pthread.cc (pthread_exit): Call method instead of function. + (pthread_setcancelstate): Ditto. + (pthread_setcanceltype): Ditto. + (pthread_testcancel): Ditto. + * thread.h (pthread::cancel_event): New member. + (__pthread_cancel_self): New prototype. + (pthread::exit): New Method. + (pthread::cancel): Ditto. + (pthread::testcancel): Ditto. + (pthread::cancel_self): Ditto. + (pthread::static_cancel_self): Ditto. + (pthread::setcancelstate): Ditto. + (pthread::setcanceltype): Ditto. + (__pthread_cancel): Give c++ linkage. + (__pthread_exit): Remove. + (__pthread_setcancelstate): Ditto. + (__pthread_setcanceltype): Ditto. + (__pthread_testcancel): Ditto. + * thread.cc (pthread::pthread): Inititialize cancel_event. + (pthread::~pthread): Close cancel_event if needed. + (pthread::create): Create cancel_event. + (pthread::exit): New method. Replacement for __pthread_exit. + (pthread::cancel): New method. + (pthread::testcancel): Ditto. + (pthread::static_cancel_self); New static method. + (pthread::setcancelstate): New method. Replacement for + __pthread_setcancelstate. + (pthread::setcanceltype): New method. Replacement for + __pthread_setcanceltype. + (pthread::pop_cleanup_handler): Added lock for async cancel safe + cancellation. + (pthread::thread_init_wrapper): Change __pthread_exit to + thread->exit(). + (__pthread_cancel): Call method thread->cancel(). + (__pthread_exit): Remove. + (__pthread_setcancelstate): Ditto. + (__pthread_setcanceltype): Ditto. + (__pthread_testcancel): Ditto. + 2002-06-02 Christopher Faylor * configure.in: Complain about lack of w32api directory. diff --git a/winsup/cygwin/include/pthread.h b/winsup/cygwin/include/pthread.h index acdde4fc3..96aa4deea 100644 --- a/winsup/cygwin/include/pthread.h +++ b/winsup/cygwin/include/pthread.h @@ -42,7 +42,7 @@ extern "C" #define PTHREAD_CANCEL_ENABLE 0 #define PTHREAD_CANCEL_DEFERRED 0 #define PTHREAD_CANCEL_DISABLE 1 -#define PTHREAD_CANCELED +#define PTHREAD_CANCELED ((void *)-1) /* this should be a value that can never be a valid address */ #define PTHREAD_COND_INITIALIZER (void *)21 #define PTHREAD_CREATE_DETACHED 1 diff --git a/winsup/cygwin/pthread.cc b/winsup/cygwin/pthread.cc index ab7fd1fbe..0484f0412 100644 --- a/winsup/cygwin/pthread.cc +++ b/winsup/cygwin/pthread.cc @@ -140,7 +140,7 @@ pthread_attr_getstackaddr (const pthread_attr_t * attr, void **stackaddr) void pthread_exit (void *value_ptr) { - return __pthread_exit (value_ptr); + return pthread::self()->exit (value_ptr); } int @@ -428,25 +428,25 @@ pthread_cancel (pthread_t thread) int pthread_setcancelstate (int state, int *oldstate) { - return __pthread_setcancelstate (state, oldstate); + return pthread::self()->setcancelstate (state, oldstate); } int pthread_setcanceltype (int type, int *oldtype) { - return __pthread_setcanceltype (type, oldtype); + return pthread::self()->setcanceltype (type, oldtype); } void pthread_testcancel (void) { - __pthread_testcancel (); + pthread::self()->testcancel (); } void _pthread_cleanup_push (__pthread_cleanup_handler *handler) { - pthread::self()->push_cleanup_handler(handler); + pthread::self()->push_cleanup_handler (handler); } void diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc index 39fad36b4..618fa7a9d 100644 --- a/winsup/cygwin/thread.cc +++ b/winsup/cygwin/thread.cc @@ -355,7 +355,8 @@ pthread::self () /* member methods */ pthread::pthread ():verifyable_object (PTHREAD_MAGIC), win32_obj_id (0), - cancelstate (0), canceltype (0), joiner (NULL), cleanup_stack(NULL) + cancelstate (0), canceltype (0), cancel_event(0), + joiner (NULL), cleanup_stack(NULL) { } @@ -363,6 +364,8 @@ pthread::~pthread () { if (win32_obj_id) CloseHandle (win32_obj_id); + if (cancel_event) + CloseHandle (cancel_event); } @@ -394,6 +397,15 @@ pthread::create (void *(*func) (void *), pthread_attr *newattr, return; } + cancel_event = ::CreateEvent (NULL,TRUE,FALSE,NULL); + if (!cancel_event) + { + system_printf ("couldn't create cancel event, this %p LastError %d", this, GetLastError () ); + /*we need the event for correct behaviour */ + magic = 0; + return; + } + win32_obj_id = ::CreateThread (&sec_none_nih, attr.stacksize, (LPTHREAD_START_ROUTINE) thread_init_wrapper, this, CREATE_SUSPENDED, &thread_id); @@ -416,6 +428,304 @@ pthread::create (void *(*func) (void *), pthread_attr *newattr, } } +void +pthread::exit (void *value_ptr) +{ + class pthread *thread = this; + + // run cleanup handlers + pop_all_cleanup_handlers (); + + MT_INTERFACE->destructors.IterateNull (); + + mutex.Lock (); + // cleanup if thread is in detached state and not joined + if( __pthread_equal(&joiner, &thread ) ) + delete this; + else + { + return_ptr = value_ptr; + mutex.UnLock (); + } + + if (InterlockedDecrement (&MT_INTERFACE->threadcount) == 0) + ::exit (0); + else + ExitThread (0); +} + +int +pthread::cancel (void) +{ + class pthread *thread = this; + class pthread *self = pthread::self (); + + mutex.Lock (); + + if (canceltype == PTHREAD_CANCEL_DEFERRED || + cancelstate == PTHREAD_CANCEL_DISABLE) + { + // cancel deferred + mutex.UnLock (); + SetEvent (cancel_event); + return 0; + } + + else if (__pthread_equal(&thread, &self)) + { + mutex.UnLock (); + cancel_self (); + return 0; // Never reached + } + + // cancel asynchronous + SuspendThread (win32_obj_id); + if (WaitForSingleObject (win32_obj_id, 0) == WAIT_TIMEOUT) + { + CONTEXT context; + context.ContextFlags = CONTEXT_CONTROL; + GetThreadContext (win32_obj_id, &context); + context.Eip = (DWORD) pthread::static_cancel_self; + SetThreadContext (win32_obj_id, &context); + } + mutex.UnLock (); + ResumeThread (win32_obj_id); + + return 0; +/* + TODO: insert pthread_testcancel into the required functions + the required function list is: *indicates done, X indicates not present in cygwin. +aio_suspend () +*close () +*creat () +fcntl () +fsync () +getmsg () +getpmsg () +lockf () +mq_receive () +mq_send () +msgrcv () +msgsnd () +msync () +nanosleep () +open () +pause () +poll () +pread () +pthread_cond_timedwait () +pthread_cond_wait () +*pthread_join () +pthread_testcancel () +putmsg () +putpmsg () +pwrite () +read () +readv () +select () +sem_wait () +sigpause () +sigsuspend () +sigtimedwait () +sigwait () +sigwaitinfo () +*sleep () +system () +tcdrain () +*usleep () +wait () +wait3() +waitid () +waitpid () +write () +writev () + +the optional list is: +catclose () +catgets () +catopen () +closedir () +closelog () +ctermid () +dbm_close () +dbm_delete () +dbm_fetch () +dbm_nextkey () +dbm_open () +dbm_store () +dlclose () +dlopen () +endgrent () +endpwent () +endutxent () +fclose () +fcntl () +fflush () +fgetc () +fgetpos () +fgets () +fgetwc () +fgetws () +fopen () +fprintf () +fputc () +fputs () +fputwc () +fputws () +fread () +freopen () +fscanf () +fseek () +fseeko () +fsetpos () +ftell () +ftello () +ftw () +fwprintf () +fwrite () +fwscanf () +getc () +getc_unlocked () +getchar () +getchar_unlocked () +getcwd () +getdate () +getgrent () +getgrgid () +getgrgid_r () +getgrnam () +getgrnam_r () +getlogin () +getlogin_r () +getpwent () +*getpwnam () +*getpwnam_r () +*getpwuid () +*getpwuid_r () +gets () +getutxent () +getutxid () +getutxline () +getw () +getwc () +getwchar () +getwd () +glob () +iconv_close () +iconv_open () +ioctl () +lseek () +mkstemp () +nftw () +opendir () +openlog () +pclose () +perror () +popen () +printf () +putc () +putc_unlocked () +putchar () +putchar_unlocked () +puts () +pututxline () +putw () +putwc () +putwchar () +readdir () +readdir_r () +remove () +rename () +rewind () +rewinddir () +scanf () +seekdir () +semop () +setgrent () +setpwent () +setutxent () +strerror () +syslog () +tmpfile () +tmpnam () +ttyname () +ttyname_r () +ungetc () +ungetwc () +unlink () +vfprintf () +vfwprintf () +vprintf () +vwprintf () +wprintf () +wscanf () + +Note, that for fcntl (), for any value of the cmd argument. + +And we must not introduce cancellation points anywhere else that's part of the posix or +opengroup specs. + */ +} + +void +pthread::testcancel (void) +{ + if (cancelstate == PTHREAD_CANCEL_DISABLE) + return; + + if( WAIT_OBJECT_0 == WaitForSingleObject (cancel_event, 0 ) ) + cancel_self (); +} + +void +pthread::static_cancel_self (void) +{ + pthread::self()->cancel_self (); +} + + +int +pthread::setcancelstate (int state, int *oldstate) +{ + int result = 0; + + mutex.Lock (); + + if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE) + result = EINVAL; + else + { + if (oldstate) + *oldstate = cancelstate; + cancelstate = state; + } + + mutex.UnLock (); + + return result; +} + +int +pthread::setcanceltype (int type, int *oldtype) +{ + int result = 0; + + mutex.Lock (); + + if (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS) + result = EINVAL; + else + { + if (oldtype) + *oldtype = canceltype; + canceltype = type; + } + + mutex.UnLock (); + + return result; +} + void pthread::push_cleanup_handler (__pthread_cleanup_handler *handler) { @@ -433,6 +743,8 @@ pthread::pop_cleanup_handler (int const execute) // TODO: send a signal or something to the thread ? api_fatal ("Attempt to execute a cleanup handler across threads"); + mutex.Lock (); + if (cleanup_stack != NULL) { __pthread_cleanup_handler *handler = cleanup_stack; @@ -441,6 +753,8 @@ pthread::pop_cleanup_handler (int const execute) (*handler->function) (handler->arg); cleanup_stack = handler->next; } + + mutex.UnLock (); } void @@ -967,11 +1281,11 @@ pthread::thread_init_wrapper (void *_arg) /*the OS doesn't check this for <= 64 Tls entries (pre win2k) */ TlsSetValue (MT_INTERFACE->thread_self_dwTlsIndex, thread); - thread->mutex.Lock(); + thread->mutex.Lock (); // if thread is detached force cleanup on exit if (thread->attr.joinable == PTHREAD_CREATE_DETACHED && thread->joiner == NULL) thread->joiner = pthread::self (); - thread->mutex.UnLock(); + thread->mutex.UnLock (); #ifdef _CYG_THREAD_FAILSAFE if (_REENT == _impure_ptr) @@ -984,7 +1298,7 @@ pthread::thread_init_wrapper (void *_arg) // call the user's thread void *ret = thread->function (thread->arg); - __pthread_exit (ret); + thread->exit (ret); #if 0 // ??? This code only runs if the thread exits by returning. @@ -1038,251 +1352,13 @@ __pthread_once (pthread_once_t *once_control, void (*init_routine) (void)) return 0; } -/*Cancelability states */ - - -/*Perform the actual cancel */ -void -__pthread_cleanup (pthread_t thread) -{ -} - - int __pthread_cancel (pthread_t thread) { if (verifyable_object_isvalid (&thread, PTHREAD_MAGIC) != VALID_OBJECT) return ESRCH; - if (thread->cancelstate == PTHREAD_CANCEL_ENABLE) - { -#if 0 - /*once all the functions call testcancel (), we will do this */ - if (thread->canceltype == PTHREAD_CANCEL_DEFERRED) - { - } - else - { - /*possible FIXME: this function is meant to return asynchronously - *from the cancellation routine actually firing. So we may need some sort - *of signal to be sent that is immediately recieved and acted on. - */ - __pthread_cleanup (thread); - } -#endif - } -/* return 0; -*/ - return ESRCH; -/* - we return ESRCH until all the required functions call testcancel (); - this will give applications predictable behaviour. - - the required function list is: *indicates done, X indicates not present in cygwin. -aio_suspend () -*close () -*creat () -fcntl () -fsync () -getmsg () -getpmsg () -lockf () -mq_receive () -mq_send () -msgrcv () -msgsnd () -msync () -nanosleep () -open () -pause () -poll () -pread () -pthread_cond_timedwait () -pthread_cond_wait () -*pthread_join () -pthread_testcancel () -putmsg () -putpmsg () -pwrite () -read () -readv () -select () -sem_wait () -sigpause () -sigsuspend () -sigtimedwait () -sigwait () -sigwaitinfo () -*sleep () -system () -tcdrain () -*usleep () -wait () -wait3() -waitid () -waitpid () -write () -writev () - -the optional list is: -catclose () -catgets () -catopen () -closedir () -closelog () -ctermid () -dbm_close () -dbm_delete () -dbm_fetch () -dbm_nextkey () -dbm_open () -dbm_store () -dlclose () -dlopen () -endgrent () -endpwent () -endutxent () -fclose () -fcntl () -fflush () -fgetc () -fgetpos () -fgets () -fgetwc () -fgetws () -fopen () -fprintf () -fputc () -fputs () -fputwc () -fputws () -fread () -freopen () -fscanf () -fseek () -fseeko () -fsetpos () -ftell () -ftello () -ftw () -fwprintf () -fwrite () -fwscanf () -getc () -getc_unlocked () -getchar () -getchar_unlocked () -getcwd () -getdate () -getgrent () -getgrgid () -getgrgid_r () -getgrnam () -getgrnam_r () -getlogin () -getlogin_r () -getpwent () -*getpwnam () -*getpwnam_r () -*getpwuid () -*getpwuid_r () -gets () -getutxent () -getutxid () -getutxline () -getw () -getwc () -getwchar () -getwd () -glob () -iconv_close () -iconv_open () -ioctl () -lseek () -mkstemp () -nftw () -opendir () -openlog () -pclose () -perror () -popen () -printf () -putc () -putc_unlocked () -putchar () -putchar_unlocked () -puts () -pututxline () -putw () -putwc () -putwchar () -readdir () -readdir_r () -remove () -rename () -rewind () -rewinddir () -scanf () -seekdir () -semop () -setgrent () -setpwent () -setutxent () -strerror () -syslog () -tmpfile () -tmpnam () -ttyname () -ttyname_r () -ungetc () -ungetwc () -unlink () -vfprintf () -vfwprintf () -vprintf () -vwprintf () -wprintf () -wscanf () - -Note, that for fcntl (), for any value of the cmd argument. - -And we must not introduce cancellation points anywhere else that's part of the posix or -opengroup specs. - */ -} - -/*no races in these three functions: they are all current-thread-only */ -int -__pthread_setcancelstate (int state, int *oldstate) -{ - class pthread *thread = pthread::self (); - if (state != PTHREAD_CANCEL_ENABLE && state != PTHREAD_CANCEL_DISABLE) - return EINVAL; - *oldstate = thread->cancelstate; - thread->cancelstate = state; - return 0; -} - -int -__pthread_setcanceltype (int type, int *oldtype) -{ - class pthread *thread = pthread::self (); - if (type != PTHREAD_CANCEL_DEFERRED && type != PTHREAD_CANCEL_ASYNCHRONOUS) - return EINVAL; - *oldtype = thread->canceltype; - thread->canceltype = type; - return 0; -} - -/*deferred cancellation request handler */ -void -__pthread_testcancel (void) -{ - class pthread *thread = pthread::self (); - if (thread->cancelstate == PTHREAD_CANCEL_DISABLE) - return; - /*check the cancellation event object here - not neededuntil pthread_cancel actually - *does something*/ + return thread->cancel (); } /* @@ -1556,32 +1632,6 @@ __pthread_attr_destroy (pthread_attr_t *attr) return 0; } -void -__pthread_exit (void *value_ptr) -{ - pthread * thread = pthread::self (); - - // run cleanup handlers - thread->pop_all_cleanup_handlers (); - - MT_INTERFACE->destructors.IterateNull (); - - thread->mutex.Lock(); - // cleanup if thread is in detached state and not joined - if( __pthread_equal(&thread->joiner, &thread ) ) - delete thread; - else - { - thread->return_ptr = value_ptr; - thread->mutex.UnLock(); - } - - if (InterlockedDecrement (&MT_INTERFACE->threadcount) == 0) - exit (0); - else - ExitThread (0); -} - int __pthread_join (pthread_t *thread, void **return_val) { diff --git a/winsup/cygwin/thread.h b/winsup/cygwin/thread.h index f926a3abd..a00ad903d 100644 --- a/winsup/cygwin/thread.h +++ b/winsup/cygwin/thread.h @@ -266,6 +266,7 @@ public: void *return_ptr; bool suspended; int cancelstate, canceltype; + HANDLE cancel_event; pthread_t joiner; // int joinable; @@ -287,6 +288,19 @@ public: pthread (); ~pthread (); + void exit (void *value_ptr); + + int cancel (); + void testcancel (); + void cancel_self () + { + exit (PTHREAD_CANCELED); + } + static void static_cancel_self (); + + int setcancelstate (int state, int *oldstate); + int setcanceltype (int type, int *oldtype); + void push_cleanup_handler (__pthread_cleanup_handler *handler); void pop_cleanup_handler (int const execute); @@ -298,7 +312,6 @@ private: __pthread_cleanup_handler *cleanup_stack; pthread_mutex mutex; - friend void __pthread_exit (void *value_ptr); friend int __pthread_join (pthread_t * thread, void **return_val); friend int __pthread_detach (pthread_t * thread); @@ -406,8 +419,10 @@ void __pthread_atforkprepare(void); void __pthread_atforkparent(void); void __pthread_atforkchild(void); +/* Cancellation */ +int __pthread_cancel (pthread_t thread); + /* Thread Exit */ -void __pthread_exit (void *value_ptr); int __pthread_join (pthread_t * thread, void **return_val); int __pthread_detach (pthread_t * thread); @@ -504,10 +519,6 @@ int __pthread_setschedparam (pthread_t thread, int policy, const struct sched_param *param); /* cancelability states */ -int __pthread_cancel (pthread_t thread); -int __pthread_setcancelstate (int state, int *oldstate); -int __pthread_setcanceltype (int type, int *oldtype); -void __pthread_testcancel (void); /* Semaphores */ int __sem_init (sem_t * sem, int pshared, unsigned int value);