xref: /freebsd/lib/libthr/thread/thr_cancel.c (revision 77b7cdf1999ee965ad494fddd184b18f532ac91a)
1 /*
2  * David Leonard <d@openbsd.org>, 1999. Public domain.
3  * $FreeBSD$
4  */
5 #include <sys/errno.h>
6 #include <pthread.h>
7 #include "thr_private.h"
8 
9 __weak_reference(_pthread_cancel, pthread_cancel);
10 __weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
11 __weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
12 __weak_reference(_pthread_testcancel, pthread_testcancel);
13 
14 int
15 _pthread_cancel(pthread_t pthread)
16 {
17 	int ret;
18 
19 	if ((ret = _find_thread(pthread)) != 0) {
20 		/* NOTHING */
21 	} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK
22 	    || (pthread->flags & PTHREAD_EXITING) != 0) {
23 		ret = 0;
24 	} else {
25 		GIANT_LOCK(curthread);
26 
27 		if (((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) != 0) ||
28 		    (((pthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0) &&
29 		    ((pthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0)))
30 			/* Just mark it for cancellation: */
31 			pthread->cancelflags |= PTHREAD_CANCELLING;
32 		else {
33 			/*
34 			 * Check if we need to kick it back into the
35 			 * run queue:
36 			 */
37 			switch (pthread->state) {
38 			case PS_RUNNING:
39 				/* No need to resume: */
40 				pthread->cancelflags |= PTHREAD_CANCELLING;
41 				break;
42 
43 			case PS_SLEEP_WAIT:
44 			case PS_WAIT_WAIT:
45 				pthread->cancelflags |= PTHREAD_CANCELLING;
46 				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
47 				break;
48 
49 			case PS_JOIN:
50 				/*
51 				 * Disconnect the thread from the joinee:
52 				 */
53 				if (pthread->join_status.thread != NULL) {
54 					pthread->join_status.thread->joiner
55 					    = NULL;
56 					pthread->join_status.thread = NULL;
57 				}
58 				pthread->cancelflags |= PTHREAD_CANCELLING;
59 				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
60 				break;
61 
62 			case PS_MUTEX_WAIT:
63 			case PS_COND_WAIT:
64 				/*
65 				 * Threads in these states may be in queues.
66 				 * In order to preserve queue integrity, the
67 				 * cancelled thread must remove itself from the
68 				 * queue.  When the thread resumes, it will
69 				 * remove itself from the queue and call the
70 				 * cancellation routine.
71 				 */
72 				pthread->cancelflags |= PTHREAD_CANCELLING;
73 				PTHREAD_NEW_STATE(pthread, PS_RUNNING);
74 				break;
75 
76 			case PS_DEAD:
77 			case PS_DEADLOCK:
78 			case PS_STATE_MAX:
79 				/* Ignore - only here to silence -Wall: */
80 				break;
81 			}
82 		}
83 
84 		/* Unprotect the scheduling queues: */
85 		GIANT_UNLOCK(curthread);
86 
87 		ret = 0;
88 	}
89 	return (ret);
90 }
91 
92 int
93 _pthread_setcancelstate(int state, int *oldstate)
94 {
95 	int ostate;
96 
97 	GIANT_LOCK(curthread);
98 	ostate = curthread->cancelflags & PTHREAD_CANCEL_DISABLE;
99 
100 	switch (state) {
101 	case PTHREAD_CANCEL_ENABLE:
102 		if (oldstate != NULL)
103 			*oldstate = ostate;
104 		curthread->cancelflags &= ~PTHREAD_CANCEL_DISABLE;
105 		if ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) == 0)
106 			break;
107 		GIANT_UNLOCK(curthread);
108 		pthread_testcancel();
109 		break;
110 	case PTHREAD_CANCEL_DISABLE:
111 		if (oldstate != NULL)
112 			*oldstate = ostate;
113 		curthread->cancelflags |= PTHREAD_CANCEL_DISABLE;
114 		GIANT_UNLOCK(curthread);
115 		break;
116 	default:
117 		GIANT_UNLOCK(curthread);
118 		return (EINVAL);
119 	}
120 
121 	return (0);
122 }
123 
124 int
125 _pthread_setcanceltype(int type, int *oldtype)
126 {
127 	int otype;
128 
129 	GIANT_LOCK(curthread);
130 	otype = curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
131 	switch (type) {
132 	case PTHREAD_CANCEL_ASYNCHRONOUS:
133 		if (oldtype != NULL)
134 			*oldtype = otype;
135 		curthread->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
136 		GIANT_UNLOCK(curthread);
137 		pthread_testcancel();
138 		break;
139 	case PTHREAD_CANCEL_DEFERRED:
140 		if (oldtype != NULL)
141 			*oldtype = otype;
142 		curthread->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
143 		GIANT_UNLOCK(curthread);
144 		break;
145 	default:
146 		GIANT_UNLOCK(curthread);
147 		return (EINVAL);
148 	}
149 
150 	return (0);
151 }
152 
153 /*
154  * XXXTHR Make an internal locked version.
155  */
156 void
157 _pthread_testcancel(void)
158 {
159 	GIANT_LOCK(curthread);
160 	if (((curthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
161 	    ((curthread->cancelflags & PTHREAD_CANCELLING) != 0) &&
162 	    ((curthread->flags & PTHREAD_EXITING) == 0)) {
163 		/*
164 		 * It is possible for this thread to be swapped out
165 		 * while performing cancellation; do not allow it
166 		 * to be cancelled again.
167 		 */
168 		curthread->cancelflags &= ~PTHREAD_CANCELLING;
169 		GIANT_UNLOCK(curthread);
170 		_thread_exit_cleanup();
171 		pthread_exit(PTHREAD_CANCELED);
172 		PANIC("cancel");
173 	}
174 	GIANT_UNLOCK(curthread);
175 }
176 
177 void
178 _thread_enter_cancellation_point(void)
179 {
180 	pthread_testcancel();
181 
182 	GIANT_LOCK(curthread);
183 	curthread->cancelflags |= PTHREAD_AT_CANCEL_POINT;
184 	GIANT_UNLOCK(curthread);
185 }
186 
187 void
188 _thread_leave_cancellation_point(void)
189 {
190 	GIANT_LOCK(curthread);
191 	curthread->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
192 	GIANT_UNLOCK(curthread);
193 
194 	pthread_testcancel();
195 }
196