xref: /freebsd/lib/libthr/thread/thr_cancel.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
1 /*
2  * David Leonard <d@openbsd.org>, 1999. Public domain.
3  * $FreeBSD$
4  */
5 #include <sys/errno.h>
6 #include <pthread.h>
7 #include <stdlib.h>
8 #include "thr_private.h"
9 
10 /*
11  * Static prototypes
12  */
13 static void	testcancel(void);
14 
15 __weak_reference(_pthread_cancel, pthread_cancel);
16 __weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
17 __weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
18 __weak_reference(_pthread_testcancel, pthread_testcancel);
19 
20 int
21 _pthread_cancel(pthread_t pthread)
22 {
23 	int ret;
24 	pthread_t joined;
25 
26 	/*
27 	 * When canceling a thread that has joined another thread, this
28 	 * routine breaks the normal lock order of locking first the
29 	 * joined and then the joiner. Therefore, it is necessary that
30 	 * if it can't obtain the second lock, that it release the first
31 	 * one and restart from the top.
32 	 */
33 retry:
34 	if ((ret = _find_thread(pthread)) != 0)
35 		/* The thread is not on the list of active threads */
36 		goto out;
37 
38 	_thread_critical_enter(pthread);
39 
40 	if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK
41 	    || (pthread->flags & PTHREAD_EXITING) != 0) {
42 		/*
43 		 * The thread is in the process of (or has already) exited
44 		 * or is deadlocked.
45 		 */
46 		_thread_critical_exit(pthread);
47 		ret = 0;
48 		goto out;
49 	}
50 
51 	/*
52 	 * The thread is on the active thread list and is not in the process
53 	 * of exiting.
54 	 */
55 
56 	if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
57 	    (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) &&
58 	    ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0)))
59 		/* Just mark it for cancellation: */
60 		pthread->cancelflags |= PTHREAD_CANCELLING;
61 	else {
62 		/*
63 		 * Check if we need to kick it back into the
64 		 * run queue:
65 		 */
66 		switch (pthread->state) {
67 		case PS_RUNNING:
68 			/* No need to resume: */
69 			pthread->cancelflags |= PTHREAD_CANCELLING;
70 			break;
71 
72 		case PS_SLEEP_WAIT:
73 		case PS_WAIT_WAIT:
74 			pthread->cancelflags |= PTHREAD_CANCELLING;
75 			PTHREAD_NEW_STATE(pthread, PS_RUNNING);
76 			break;
77 
78 		case PS_JOIN:
79 			/*
80 			 * Disconnect the thread from the joinee:
81 			 */
82 			if ((joined = pthread->join_status.thread) != NULL) {
83 				UMTX_TRYLOCK(&joined->lock, ret);
84 				if (ret == EBUSY) {
85 					_thread_critical_exit(pthread);
86 					goto retry;
87 				}
88 				pthread->join_status.thread->joiner = NULL;
89 				UMTX_UNLOCK(&joined->lock);
90 				joined = pthread->join_status.thread = NULL;
91 			}
92 			pthread->cancelflags |= PTHREAD_CANCELLING;
93 			PTHREAD_NEW_STATE(pthread, PS_RUNNING);
94 			break;
95 
96 		case PS_MUTEX_WAIT:
97 		case PS_COND_WAIT:
98 			/*
99 			 * Threads in these states may be in queues.
100 			 * In order to preserve queue integrity, the
101 			 * cancelled thread must remove itself from the
102 			 * queue.  When the thread resumes, it will
103 			 * remove itself from the queue and call the
104 			 * cancellation routine.
105 			 */
106 			pthread->cancelflags |= PTHREAD_CANCELLING;
107 			PTHREAD_NEW_STATE(pthread, PS_RUNNING);
108 			break;
109 
110 		case PS_DEAD:
111 		case PS_DEADLOCK:
112 		case PS_STATE_MAX:
113 			/* Ignore - only here to silence -Wall: */
114 			break;
115 		}
116 	}
117 
118 	/* Unprotect the scheduling queues: */
119 	_thread_critical_exit(pthread);
120 
121 	ret = 0;
122 out:
123 	return (ret);
124 }
125 
126 int
127 _pthread_setcancelstate(int state, int *oldstate)
128 {
129 	int ostate, ret;
130 
131 	ret = 0;
132 
133 	_thread_critical_enter(curthread);
134 
135 	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
136 
137 	switch (state) {
138 	case PTHREAD_CANCEL_ENABLE:
139 		if (oldstate != NULL)
140 			*oldstate = ostate;
141 		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
142 		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)
143 			break;
144 		testcancel();
145 		break;
146 	case PTHREAD_CANCEL_DISABLE:
147 		if (oldstate != NULL)
148 			*oldstate = ostate;
149 		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
150 		break;
151 	default:
152 		ret = EINVAL;
153 	}
154 
155 	_thread_critical_exit(curthread);
156 	return (ret);
157 }
158 
159 int
160 _pthread_setcanceltype(int type, int *oldtype)
161 {
162 	int otype;
163 
164 	_thread_critical_enter(curthread);
165 	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
166 	switch (type) {
167 	case PTHREAD_CANCEL_ASYNCHRONOUS:
168 		if (oldtype != NULL)
169 			*oldtype = otype;
170 		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
171 		testcancel();
172 		break;
173 	case PTHREAD_CANCEL_DEFERRED:
174 		if (oldtype != NULL)
175 			*oldtype = otype;
176 		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
177 		break;
178 	default:
179 		return (EINVAL);
180 	}
181 
182 	_thread_critical_exit(curthread);
183 	return (0);
184 }
185 
186 void
187 _pthread_testcancel(void)
188 {
189 	_thread_critical_enter(curthread);
190 	testcancel();
191 	_thread_critical_exit(curthread);
192 }
193 
194 static void
195 testcancel()
196 {
197 	/*
198 	 * This pthread should already be locked by the caller.
199 	 */
200 
201 	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
202 	    ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) &&
203 	    ((curthread->flags & PTHREAD_EXITING) == 0)) {
204 		/*
205 		 * It is possible for this thread to be swapped out
206 		 * while performing cancellation; do not allow it
207 		 * to be cancelled again.
208 		 */
209 		curthread->cancelflags &= ~PTHREAD_CANCELLING;
210 		_thread_critical_exit(curthread);
211 		_thread_exit_cleanup();
212 		pthread_exit(PTHREAD_CANCELED);
213 		PANIC("cancel");
214 	}
215 }
216 
217 void
218 _thread_enter_cancellation_point(void)
219 {
220 	_thread_critical_enter(curthread);
221 	testcancel();
222 	curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
223 	_thread_critical_exit(curthread);
224 }
225 
226 void
227 _thread_leave_cancellation_point(void)
228 {
229 	_thread_critical_enter(curthread);
230 	curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
231 	testcancel();
232 	_thread_critical_exit(curthread);
233 
234 }
235