1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
|
/*
* David Leonard <d@openbsd.org>, 1999. Public domain.
* $FreeBSD$
*/
#include <sys/errno.h>
#include <pthread.h>
#include <stdlib.h>
#include "thr_private.h"
/*
* Static prototypes
*/
static void testcancel(void);
__weak_reference(_pthread_cancel, pthread_cancel);
__weak_reference(_pthread_setcancelstate, pthread_setcancelstate);
__weak_reference(_pthread_setcanceltype, pthread_setcanceltype);
__weak_reference(_pthread_testcancel, pthread_testcancel);
/*
* Posix requires this function to be async-cancel-safe, so it
* may not aquire any type of resource or call any functions
* that might do so.
*/
int
_pthread_cancel(pthread_t pthread)
{
/* Don't continue if cancellation has already been set. */
if (atomic_cmpset_int(&pthread->cancellation, (int)CS_NULL,
(int)CS_PENDING) != 1)
return (0);
/*
* Only wakeup threads that are in cancellation points or
* have set async cancel.
* XXX - access to pthread->flags is not safe. We should just
* unconditionally wake the thread and make sure that
* the the library correctly handles spurious wakeups.
*/
if ((pthread->cancellationpoint || pthread->cancelmode == M_ASYNC) &&
(pthread->flags & PTHREAD_FLAGS_NOT_RUNNING) != 0)
PTHREAD_WAKE(pthread);
return (0);
}
/*
* Posix requires this function to be async-cancel-safe, so it
* may not aquire any type of resource or call any functions
* that might do so.
*/
int
_pthread_setcancelstate(int state, int *oldstate)
{
int ostate;
ostate = (curthread->cancelmode == M_OFF) ? PTHREAD_CANCEL_DISABLE :
PTHREAD_CANCEL_ENABLE;
switch (state) {
case PTHREAD_CANCEL_ENABLE:
curthread->cancelmode = curthread->cancelstate;
break;
case PTHREAD_CANCEL_DISABLE:
if (curthread->cancelmode != M_OFF) {
curthread->cancelstate = curthread->cancelmode;
curthread->cancelmode = M_OFF;
}
break;
default:
return (EINVAL);
}
if (oldstate != NULL)
*oldstate = ostate;
return (0);
}
/*
* Posix requires this function to be async-cancel-safe, so it
* may not aquire any type of resource or call any functions that
* might do so.
*/
int
_pthread_setcanceltype(int type, int *oldtype)
{
enum cancel_mode omode;
omode = curthread->cancelstate;
switch (type) {
case PTHREAD_CANCEL_ASYNCHRONOUS:
if (curthread->cancelmode != M_OFF)
curthread->cancelmode = M_ASYNC;
curthread->cancelstate = M_ASYNC;
break;
case PTHREAD_CANCEL_DEFERRED:
if (curthread->cancelmode != M_OFF)
curthread->cancelmode = M_DEFERRED;
curthread->cancelstate = M_DEFERRED;
break;
default:
return (EINVAL);
}
if (oldtype != NULL) {
if (omode == M_DEFERRED)
*oldtype = PTHREAD_CANCEL_DEFERRED;
else if (omode == M_ASYNC)
*oldtype = PTHREAD_CANCEL_ASYNCHRONOUS;
}
return (0);
}
void
_pthread_testcancel(void)
{
testcancel();
}
static void
testcancel()
{
if (curthread->cancelmode != M_OFF) {
/* Cleanup a canceled thread only once. */
if (atomic_cmpset_int(&curthread->cancellation,
(int)CS_PENDING, (int)CS_SET) == 1) {
_thread_exit_cleanup();
pthread_exit(PTHREAD_CANCELED);
PANIC("cancel");
}
}
}
void
_thread_enter_cancellation_point(void)
{
testcancel();
curthread->cancellationpoint = 1;
}
void
_thread_leave_cancellation_point(void)
{
curthread->cancellationpoint = 0;
testcancel();
}
|