1 /*	$OpenBSD: uthread_cancel.c,v 1.13 2002/05/07 05:13:17 pvalchev Exp $	*/
2 /*
3  * David Leonard <d@openbsd.org>, 1999. Public domain.
4  */
5 #include <sys/errno.h>
6 #include <pthread.h>
7 #include "pthread_private.h"
8 
9 static void	finish_cancellation(void *arg);
10 
11 int
pthread_cancel(pthread_t pthread)12 pthread_cancel(pthread_t pthread)
13 {
14 	int ret;
15 
16 	if ((ret = _find_thread(pthread)) != 0) {
17 		/* NOTHING */
18 	} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK
19 	    || (pthread->flags & PTHREAD_EXITING) != 0) {
20 		ret = 0;
21 	} else {
22 		/* Protect the scheduling queues: */
23 		_thread_kern_sig_defer();
24 
25 		if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
26 		    (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) &&
27 		    ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0)))
28 			/* Just mark it for cancellation: */
29 			pthread->cancelflags |= PTHREAD_CANCELLING;
30 		else {
31 			/*
32 			 * Check if we need to kick it back into the
33 			 * run queue:
34 			 */
35 			switch (pthread->state) {
36 			case PS_RUNNING:
37 				/* No need to resume: */
38 				pthread->cancelflags |= PTHREAD_CANCELLING;
39 				break;
40 
41 			case PS_SPINBLOCK:
42 			case PS_FDR_WAIT:
43 			case PS_FDW_WAIT:
44 			case PS_POLL_WAIT:
45 			case PS_SELECT_WAIT:
46 				/* Remove these threads from the work queue: */
47 				if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
48 				    != 0)
49 					PTHREAD_WORKQ_REMOVE(pthread);
50 				/* Fall through: */
51 			case PS_SIGTHREAD:
52 			case PS_SLEEP_WAIT:
53 			case PS_WAIT_WAIT:
54 			case PS_SIGSUSPEND:
55 			case PS_SIGWAIT:
56 				/* Interrupt and resume: */
57 				pthread->interrupted = 1;
58 				pthread->cancelflags |= PTHREAD_CANCELLING;
59 				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
60 				break;
61 
62 			case PS_JOIN:
63 				/*
64 				 * Disconnect the thread from the joinee:
65 				 */
66 				if (pthread->join_status.thread != NULL) {
67 					pthread->join_status.thread->joiner
68 					    = NULL;
69 					pthread->join_status.thread = NULL;
70 				}
71 				pthread->cancelflags |= PTHREAD_CANCELLING;
72 				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
73 				break;
74 
75 			case PS_SUSPENDED:
76 				if (pthread->suspended == SUSP_NO ||
77 				    pthread->suspended == SUSP_YES ||
78 				    pthread->suspended == SUSP_JOIN ||
79 				    pthread->suspended == SUSP_NOWAIT) {
80 					/*
81 					 * This thread isn't in any scheduling
82 					 * queues; just change it's state:
83 					 */
84 					pthread->cancelflags |=
85 					    PTHREAD_CANCELLING;
86 					PTHREAD_SET_STATE(pthread, PS_RUNNING);
87 					break;
88 				}
89 				/* FALLTHROUGH */
90 			case PS_MUTEX_WAIT:
91 			case PS_COND_WAIT:
92 			case PS_FDLR_WAIT:
93 			case PS_FDLW_WAIT:
94 			case PS_FILE_WAIT:
95 				/*
96 				 * Threads in these states may be in queues.
97 				 * In order to preserve queue integrity, the
98 				 * cancelled thread must remove itself from the
99 				 * queue.  Mark the thread as interrupted and
100 				 * needing cancellation, and set the state to
101 				 * running.  When the thread resumes, it will
102 				 * remove itself from the queue and call the
103 				 * cancellation completion routine.
104 				 */
105 				pthread->interrupted = 1;
106 				pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
107 				PTHREAD_NEW_STATE(pthread,PS_RUNNING);
108 				pthread->continuation = finish_cancellation;
109 				break;
110 
111 			case PS_DEAD:
112 			case PS_DEADLOCK:
113 			case PS_STATE_MAX:
114 				/* Ignore - only here to silence -Wall: */
115 				break;
116 			}
117 		}
118 
119 		/* Unprotect the scheduling queues: */
120 		_thread_kern_sig_undefer();
121 
122 		ret = 0;
123 	}
124 	return (ret);
125 }
126 
127 int
pthread_setcancelstate(int state,int * oldstate)128 pthread_setcancelstate(int state, int *oldstate)
129 {
130 	struct pthread	*curthread = _get_curthread();
131 	int ostate;
132 	int ret;
133 
134 	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
135 
136 	switch (state) {
137 	case PTHREAD_CANCEL_ENABLE:
138 		if (oldstate != NULL)
139 			*oldstate = ostate;
140 		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
141 		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
142 			pthread_testcancel();
143 		ret = 0;
144 		break;
145 	case PTHREAD_CANCEL_DISABLE:
146 		if (oldstate != NULL)
147 			*oldstate = ostate;
148 		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
149 		ret = 0;
150 		break;
151 	default:
152 		ret = EINVAL;
153 	}
154 
155 	return (ret);
156 }
157 
158 
159 int
pthread_setcanceltype(int type,int * oldtype)160 pthread_setcanceltype(int type, int *oldtype)
161 {
162 	struct pthread	*curthread = _get_curthread();
163 	int otype;
164 	int ret;
165 
166 	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
167 	switch (type) {
168 	case PTHREAD_CANCEL_ASYNCHRONOUS:
169 		if (oldtype != NULL)
170 			*oldtype = otype;
171 		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
172 		pthread_testcancel();
173 		ret = 0;
174 		break;
175 	case PTHREAD_CANCEL_DEFERRED:
176 		if (oldtype != NULL)
177 			*oldtype = otype;
178 		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
179 		ret = 0;
180 		break;
181 	default:
182 		ret = EINVAL;
183 	}
184 
185 	return (ret);
186 }
187 
188 void
pthread_testcancel(void)189 pthread_testcancel(void)
190 {
191 	struct pthread	*curthread = _get_curthread();
192 
193 	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
194 	    ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) &&
195 	    ((curthread->flags & PTHREAD_EXITING) == 0)) {
196 		/*
197 		 * It is possible for this thread to be swapped out
198 		 * while performing cancellation; do not allow it
199 		 * to be cancelled again.
200 		 */
201 		curthread->cancelflags &= ~PTHREAD_CANCELLING;
202 #ifdef notyet
203 		_thread_exit_cleanup();
204 #endif
205 		pthread_exit(PTHREAD_CANCELED);
206 		PANIC("cancel");
207 	}
208 }
209 
210 void
_thread_enter_cancellation_point(void)211 _thread_enter_cancellation_point(void)
212 {
213 	struct pthread	*curthread = _get_curthread();
214 
215 	/* Look for a cancellation before we block: */
216 	pthread_testcancel();
217 	curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
218 }
219 
220 void
_thread_leave_cancellation_point(void)221 _thread_leave_cancellation_point(void)
222 {
223 	struct pthread	*curthread = _get_curthread();
224 
225 	curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
226 	/* Look for a cancellation after we unblock: */
227 	pthread_testcancel();
228 }
229 
230 static void
finish_cancellation(void * arg)231 finish_cancellation(void *arg)
232 {
233 	struct pthread	*curthread = _get_curthread();
234 
235 	curthread->continuation = NULL;
236 	curthread->interrupted = 0;
237 
238 	if ((curthread->cancelflags & PTHREAD_CANCEL_NEEDED) != 0) {
239 		curthread->cancelflags &= ~PTHREAD_CANCEL_NEEDED;
240 #ifdef notyet
241 		_thread_exit_cleanup();
242 #endif
243 		pthread_exit(PTHREAD_CANCELED);
244 	}
245 }
246