This patch provides a pthread cancellation implementation.

Rob, please mail me if you can't read the attachment.

Thomas

2002-06-25  Thomas Pfaff  <[EMAIL PROTECTED]>

        * 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.
diff -urp src.old/winsup/cygwin/include/pthread.h src/winsup/cygwin/include/pthread.h
--- src.old/winsup/cygwin/include/pthread.h     Mon Jun 24 10:51:48 2002
+++ src/winsup/cygwin/include/pthread.h Mon Jun 24 10:51:17 2002
@@ -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 -urp src.old/winsup/cygwin/pthread.cc src/winsup/cygwin/pthread.cc
--- src.old/winsup/cygwin/pthread.cc    Mon Jun 24 10:51:48 2002
+++ src/winsup/cygwin/pthread.cc        Mon Jun 24 10:53:04 2002
@@ -140,7 +140,7 @@ pthread_attr_getstackaddr (const pthread
 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 -urp src.old/winsup/cygwin/thread.cc src/winsup/cygwin/thread.cc
--- src.old/winsup/cygwin/thread.cc     Mon Jun 24 10:51:48 2002
+++ src/winsup/cygwin/thread.cc Mon Jun 24 11:02:39 2002
@@ -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 *),
       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);
@@ -414,6 +426,304 @@ pthread::create (void *(*func) (void *),
 }
 
 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)
 {
   if (this != self ())
@@ -430,6 +740,8 @@ pthread::pop_cleanup_handler (int const 
     // 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;
@@ -438,6 +750,8 @@ pthread::pop_cleanup_handler (int const 
         (*handler->function) (handler->arg);
       cleanup_stack = handler->next;
     }
+
+  mutex.UnLock ();
 }
 
 void
@@ -964,11 +1278,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)
@@ -981,7 +1295,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.
@@ -1035,251 +1349,13 @@ __pthread_once (pthread_once_t *once_con
   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 ();
 }
 
 /*
@@ -1551,32 +1627,6 @@ __pthread_attr_destroy (pthread_attr_t *
   delete (*attr);
   *attr = NULL;
   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
diff -urp src.old/winsup/cygwin/thread.h src/winsup/cygwin/thread.h
--- src.old/winsup/cygwin/thread.h      Mon Jun 24 10:51:49 2002
+++ src/winsup/cygwin/thread.h  Mon Jun 24 11:07:11 2002
@@ -267,6 +267,7 @@ public:
   void *return_ptr;
   bool suspended;
   int cancelstate, canceltype;
+  HANDLE cancel_event;
   pthread_t joiner;
   // int joinable;
 
@@ -288,6 +289,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);
 
@@ -299,7 +313,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);
     
@@ -407,8 +420,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);
 
@@ -505,10 +520,6 @@ int __pthread_setschedparam (pthread_t t
                             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);

Reply via email to