Home | History | Annotate | Download | only in pthreads-win32
      1 /*
      2  * pthread_cancel.c
      3  *
      4  * Description:
      5  * POSIX thread functions related to thread cancellation.
      6  *
      7  * --------------------------------------------------------------------------
      8  *
      9  *      Pthreads-win32 - POSIX Threads Library for Win32
     10  *      Copyright(C) 1998 John E. Bossom
     11  *      Copyright(C) 1999,2005 Pthreads-win32 contributors
     12  *
     13  *      Contact Email: rpj (at) callisto.canberra.edu.au
     14  *
     15  *      The current list of contributors is contained
     16  *      in the file CONTRIBUTORS included with the source
     17  *      code distribution. The list can also be seen at the
     18  *      following World Wide Web location:
     19  *      http://sources.redhat.com/pthreads-win32/contributors.html
     20  *
     21  *      This library is free software; you can redistribute it and/or
     22  *      modify it under the terms of the GNU Lesser General Public
     23  *      License as published by the Free Software Foundation; either
     24  *      version 2 of the License, or (at your option) any later version.
     25  *
     26  *      This library is distributed in the hope that it will be useful,
     27  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
     28  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     29  *      Lesser General Public License for more details.
     30  *
     31  *      You should have received a copy of the GNU Lesser General Public
     32  *      License along with this library in the file COPYING.LIB;
     33  *      if not, write to the Free Software Foundation, Inc.,
     34  *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
     35  */
     36 
     37 #include "pthread.h"
     38 #include "implement.h"
     39 #include "context.h"
     40 
     41 static void
     42 ptw32_cancel_self (void)
     43 {
     44   ptw32_throw (PTW32_EPS_CANCEL);
     45 
     46   /* Never reached */
     47 }
     48 
     49 static void CALLBACK
     50 ptw32_cancel_callback (ULONG_PTR unused)
     51 {
     52   ptw32_throw (PTW32_EPS_CANCEL);
     53 
     54   /* Never reached */
     55 }
     56 
     57 /*
     58  * ptw32_RegisterCancelation() -
     59  * Must have args of same type as QueueUserAPCEx because this function
     60  * is a substitute for QueueUserAPCEx if it's not available.
     61  */
     62 DWORD
     63 ptw32_RegisterCancelation (PAPCFUNC unused1, HANDLE threadH, DWORD unused2)
     64 {
     65   CONTEXT context;
     66 
     67   context.ContextFlags = CONTEXT_CONTROL;
     68   GetThreadContext (threadH, &context);
     69   PTW32_PROGCTR (context) = (DWORD_PTR) ptw32_cancel_self;
     70   SetThreadContext (threadH, &context);
     71   return 0;
     72 }
     73 
     74 int
     75 pthread_cancel (pthread_t thread)
     76      /*
     77       * ------------------------------------------------------
     78       * DOCPUBLIC
     79       *      This function requests cancellation of 'thread'.
     80       *
     81       * PARAMETERS
     82       *      thread
     83       *              reference to an instance of pthread_t
     84       *
     85       *
     86       * DESCRIPTION
     87       *      This function requests cancellation of 'thread'.
     88       *      NOTE: cancellation is asynchronous; use pthread_join to
     89       *                wait for termination of 'thread' if necessary.
     90       *
     91       * RESULTS
     92       *              0               successfully requested cancellation,
     93       *              ESRCH           no thread found corresponding to 'thread',
     94       *              ENOMEM          implicit self thread create failed.
     95       * ------------------------------------------------------
     96       */
     97 {
     98   int result;
     99   int cancel_self;
    100   pthread_t self;
    101   ptw32_thread_t * tp;
    102   ptw32_mcs_local_node_t stateLock;
    103 
    104   result = pthread_kill (thread, 0);
    105 
    106   if (0 != result)
    107     {
    108       return result;
    109     }
    110 
    111   if ((self = pthread_self ()).p == NULL)
    112     {
    113       return ENOMEM;
    114     };
    115 
    116   /*
    117    * For self cancellation we need to ensure that a thread can't
    118    * deadlock itself trying to cancel itself asynchronously
    119    * (pthread_cancel is required to be an async-cancel
    120    * safe function).
    121    */
    122   cancel_self = pthread_equal (thread, self);
    123 
    124   tp = (ptw32_thread_t *) thread.p;
    125 
    126   /*
    127    * Lock for async-cancel safety.
    128    */
    129   ptw32_mcs_lock_acquire (&tp->stateLock, &stateLock);
    130 
    131   if (tp->cancelType == PTHREAD_CANCEL_ASYNCHRONOUS
    132       && tp->cancelState == PTHREAD_CANCEL_ENABLE
    133       && tp->state < PThreadStateCanceling)
    134     {
    135       if (cancel_self)
    136 	{
    137 	  tp->state = PThreadStateCanceling;
    138 	  tp->cancelState = PTHREAD_CANCEL_DISABLE;
    139 
    140 	  ptw32_mcs_lock_release (&stateLock);
    141 	  ptw32_throw (PTW32_EPS_CANCEL);
    142 
    143 	  /* Never reached */
    144 	}
    145       else
    146 	{
    147 	  HANDLE threadH = tp->threadH;
    148 
    149 	  SuspendThread (threadH);
    150 
    151 	  if (WaitForSingleObject (threadH, 0) == WAIT_TIMEOUT)
    152 	    {
    153 	      tp->state = PThreadStateCanceling;
    154 	      tp->cancelState = PTHREAD_CANCEL_DISABLE;
    155 	      /*
    156 	       * If alertdrv and QueueUserAPCEx is available then the following
    157 	       * will result in a call to QueueUserAPCEx with the args given, otherwise
    158 	       * this will result in a call to ptw32_RegisterCancelation and only
    159 	       * the threadH arg will be used.
    160 	       */
    161 	      ptw32_register_cancelation ((PAPCFUNC)ptw32_cancel_callback, threadH, 0);
    162 	      ptw32_mcs_lock_release (&stateLock);
    163 	      ResumeThread (threadH);
    164 	    }
    165 	}
    166     }
    167   else
    168     {
    169       /*
    170        * Set for deferred cancellation.
    171        */
    172       if (tp->state < PThreadStateCancelPending)
    173 	{
    174 	  tp->state = PThreadStateCancelPending;
    175 	  if (!SetEvent (tp->cancelEvent))
    176 	    {
    177 	      result = ESRCH;
    178 	    }
    179 	}
    180       else if (tp->state >= PThreadStateCanceling)
    181 	{
    182 	  result = ESRCH;
    183 	}
    184 
    185       ptw32_mcs_lock_release (&stateLock);
    186     }
    187 
    188   return (result);
    189 }
    190