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@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  *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
35  */
36 
37 #include "pthread.h"
38 #include "implement.h"
39 #include "context.h"
40 
41 static void
ptw32_cancel_self(void)42 ptw32_cancel_self (void)
43 {
44   ptw32_throw (PTW32_EPS_CANCEL);
45 
46   /* Never reached */
47 }
48 
49 static void CALLBACK
ptw32_cancel_callback(ULONG_PTR unused)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
ptw32_RegisterCancelation(PAPCFUNC unused1,HANDLE threadH,DWORD unused2)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
pthread_cancel(pthread_t thread)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