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 *      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
41static void
42ptw32_cancel_self (void)
43{
44  ptw32_throw (PTW32_EPS_CANCEL);
45
46  /* Never reached */
47}
48
49static void CALLBACK
50ptw32_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 */
62DWORD
63ptw32_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
74int
75pthread_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