xref: /freebsd/lib/libthr/thread/thr_cancel.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /*
2  * Copyright (c) 2005, David Xu <davidxu@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  *
28  */
29 
30 #include "namespace.h"
31 #include <pthread.h>
32 #include "un-namespace.h"
33 
34 #include "thr_private.h"
35 
36 __weak_reference(_pthread_cancel, pthread_cancel);
37 __weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
38 __weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
39 __weak_reference(_pthread_testcancel, pthread_testcancel);
40 
41 static inline void
42 testcancel(struct pthread *curthread)
43 {
44 	if (__predict_false(SHOULD_CANCEL(curthread) &&
45 	    !THR_IN_CRITICAL(curthread) && curthread->cancel_defer == 0))
46 		_pthread_exit(PTHREAD_CANCELED);
47 }
48 
49 void
50 _thr_testcancel(struct pthread *curthread)
51 {
52 	testcancel(curthread);
53 }
54 
55 int
56 _pthread_cancel(pthread_t pthread)
57 {
58 	struct pthread *curthread = _get_curthread();
59 	int ret;
60 
61 	/*
62 	 * POSIX says _pthread_cancel should be async cancellation safe.
63 	 * _thr_ref_add and _thr_ref_delete will enter and leave critical
64 	 * region automatically.
65 	 */
66 	if ((ret = _thr_ref_add(curthread, pthread, 0)) == 0) {
67 		THR_THREAD_LOCK(curthread, pthread);
68 		if (!pthread->cancel_pending) {
69 			pthread->cancel_pending = 1;
70 			if (pthread->cancel_enable)
71 				_thr_send_sig(pthread, SIGCANCEL);
72 		}
73 		THR_THREAD_UNLOCK(curthread, pthread);
74 		_thr_ref_delete(curthread, pthread);
75 	}
76 	return (ret);
77 }
78 
79 int
80 _pthread_setcancelstate(int state, int *oldstate)
81 {
82 	struct pthread *curthread = _get_curthread();
83 	int oldval;
84 
85 	oldval = curthread->cancel_enable;
86 	switch (state) {
87 	case PTHREAD_CANCEL_DISABLE:
88 		THR_LOCK(curthread);
89 		curthread->cancel_enable = 0;
90 		THR_UNLOCK(curthread);
91 		break;
92 	case PTHREAD_CANCEL_ENABLE:
93 		THR_LOCK(curthread);
94 		curthread->cancel_enable = 1;
95 		THR_UNLOCK(curthread);
96 		break;
97 	default:
98 		return (EINVAL);
99 	}
100 
101 	if (oldstate) {
102 		*oldstate = oldval ? PTHREAD_CANCEL_ENABLE :
103 			PTHREAD_CANCEL_DISABLE;
104 	}
105 	return (0);
106 }
107 
108 int
109 _pthread_setcanceltype(int type, int *oldtype)
110 {
111 	struct pthread	*curthread = _get_curthread();
112 	int oldval;
113 
114 	oldval = curthread->cancel_async;
115 	switch (type) {
116 	case PTHREAD_CANCEL_ASYNCHRONOUS:
117 		curthread->cancel_async = 1;
118 		testcancel(curthread);
119 		break;
120 	case PTHREAD_CANCEL_DEFERRED:
121 		curthread->cancel_async = 0;
122 		break;
123 	default:
124 		return (EINVAL);
125 	}
126 
127 	if (oldtype) {
128 		*oldtype = oldval ? PTHREAD_CANCEL_ASYNCHRONOUS :
129 		 	PTHREAD_CANCEL_DEFERRED;
130 	}
131 	return (0);
132 }
133 
134 void
135 _pthread_testcancel(void)
136 {
137 	struct pthread *curthread = _get_curthread();
138 
139 	_thr_cancel_enter(curthread);
140 	_thr_cancel_leave(curthread);
141 }
142 
143 void
144 _thr_cancel_enter(struct pthread *curthread)
145 {
146 	if (curthread->cancel_enable) {
147 		curthread->cancel_point++;
148 		testcancel(curthread);
149 	}
150 }
151 
152 void
153 _thr_cancel_leave(struct pthread *curthread)
154 {
155 	if (curthread->cancel_enable)
156 		curthread->cancel_point--;
157 }
158 
159 void
160 _thr_cancel_enter_defer(struct pthread *curthread)
161 {
162 	if (curthread->cancel_enable) {
163 		curthread->cancel_point++;
164 		testcancel(curthread);
165 		curthread->cancel_defer++;
166 	}
167 }
168 
169 void
170 _thr_cancel_leave_defer(struct pthread *curthread, int check)
171 {
172 	if (curthread->cancel_enable) {
173 		if (!check) {
174 			curthread->cancel_point--;
175 			curthread->cancel_defer--;
176 		} else {
177 			curthread->cancel_defer--;
178 			testcancel(curthread);
179 			curthread->cancel_point--;
180 		}
181 	}
182 }
183