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