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
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: stable/11/lib/libthr/thread/thr_cancel.c 358141 2020-02-20 01:24:45Z kib $");
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
41static inline void
42testcancel(struct pthread *curthread)
43{
44	if (__predict_false(SHOULD_CANCEL(curthread) &&
45	    !THR_IN_CRITICAL(curthread)))
46		_pthread_exit(PTHREAD_CANCELED);
47}
48
49void
50_thr_testcancel(struct pthread *curthread)
51{
52	testcancel(curthread);
53}
54
55int
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_find_thread and THR_THREAD_UNLOCK will enter and leave critical
64	 * region automatically.
65	 */
66	if ((ret = _thr_find_thread(curthread, pthread, 1)) == 0) {
67		if (!pthread->cancel_pending) {
68			pthread->cancel_pending = 1;
69			if (pthread->state != PS_DEAD)
70				_thr_send_sig(pthread, SIGCANCEL);
71		}
72		THR_THREAD_UNLOCK(curthread, pthread);
73	}
74	return (ret);
75}
76
77int
78_pthread_setcancelstate(int state, int *oldstate)
79{
80	struct pthread *curthread = _get_curthread();
81	int oldval;
82
83	oldval = curthread->cancel_enable;
84	switch (state) {
85	case PTHREAD_CANCEL_DISABLE:
86		curthread->cancel_enable = 0;
87		break;
88	case PTHREAD_CANCEL_ENABLE:
89		curthread->cancel_enable = 1;
90		if (curthread->cancel_async)
91			testcancel(curthread);
92		break;
93	default:
94		return (EINVAL);
95	}
96
97	if (oldstate) {
98		*oldstate = oldval ? PTHREAD_CANCEL_ENABLE :
99			PTHREAD_CANCEL_DISABLE;
100	}
101	return (0);
102}
103
104int
105_pthread_setcanceltype(int type, int *oldtype)
106{
107	struct pthread	*curthread = _get_curthread();
108	int oldval;
109
110	oldval = curthread->cancel_async;
111	switch (type) {
112	case PTHREAD_CANCEL_ASYNCHRONOUS:
113		curthread->cancel_async = 1;
114		testcancel(curthread);
115		break;
116	case PTHREAD_CANCEL_DEFERRED:
117		curthread->cancel_async = 0;
118		break;
119	default:
120		return (EINVAL);
121	}
122
123	if (oldtype) {
124		*oldtype = oldval ? PTHREAD_CANCEL_ASYNCHRONOUS :
125		 	PTHREAD_CANCEL_DEFERRED;
126	}
127	return (0);
128}
129
130void
131_pthread_testcancel(void)
132{
133	struct pthread *curthread;
134
135	_thr_check_init();
136	curthread = _get_curthread();
137	testcancel(curthread);
138}
139
140void
141_thr_cancel_enter(struct pthread *curthread)
142{
143	curthread->cancel_point = 1;
144	testcancel(curthread);
145}
146
147void
148_thr_cancel_enter2(struct pthread *curthread, int maycancel)
149{
150	curthread->cancel_point = 1;
151	if (__predict_false(SHOULD_CANCEL(curthread) &&
152	    !THR_IN_CRITICAL(curthread))) {
153		if (!maycancel)
154			thr_wake(curthread->tid);
155		else
156			_pthread_exit(PTHREAD_CANCELED);
157	}
158}
159
160void
161_thr_cancel_leave(struct pthread *curthread, int maycancel)
162{
163	curthread->cancel_point = 0;
164	if (__predict_false(SHOULD_CANCEL(curthread) &&
165	    !THR_IN_CRITICAL(curthread) && maycancel))
166		_pthread_exit(PTHREAD_CANCELED);
167}
168
169void
170_pthread_cancel_enter(int maycancel)
171{
172	_thr_cancel_enter2(_get_curthread(), maycancel);
173}
174
175void
176_pthread_cancel_leave(int maycancel)
177{
178	_thr_cancel_leave(_get_curthread(), maycancel);
179}
180