1 | //------------------------------------------------------------------------------ |
2 | // Copyright (c) 2016 by Lukasz Janyst <lukasz@jany.st> |
3 | //------------------------------------------------------------------------------ |
4 | // This file is part of thread-bites. |
5 | // |
6 | // thread-bites is free software: you can redistribute it and/or modify |
7 | // it under the terms of the GNU General Public License as published by |
8 | // the Free Software Foundation, either version 3 of the License, or |
9 | // (at your option) any later version. |
10 | // |
11 | // thread-bites is distributed in the hope that it will be useful, |
12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | // GNU General Public License for more details. |
15 | // |
16 | // You should have received a copy of the GNU General Public License |
17 | // along with thread-bites. If not, see <http://www.gnu.org/licenses/>. |
18 | //------------------------------------------------------------------------------ |
19 | |
20 | #include "tb.h" |
21 | #include "tb-private.h" |
22 | |
23 | //------------------------------------------------------------------------------ |
24 | // Cancelation handler |
25 | //------------------------------------------------------------------------------ |
26 | void tb_cancel_handler(int sig, siginfo_t *si, void *ctx) |
27 | { |
28 | if(sig != SIGCANCEL || si->si_pid != tb_pid || si->si_code != SI_TKILL) |
29 | return; |
30 | |
31 | tbthread_t self = tbthread_self(); |
32 | if(self->cancel_status & TB_CANCEL_DEFERRED) |
33 | return; |
34 | |
35 | tbthread_testcancel(); |
36 | } |
37 | |
38 | //------------------------------------------------------------------------------ |
39 | // Cancel a thread |
40 | //------------------------------------------------------------------------------ |
41 | int tbthread_cancel(tbthread_t thread) |
42 | { |
43 | int ret = 0; |
44 | tbthread_mutex_lock(&desc_mutex); |
45 | |
46 | if(!list_find_elem(&used_desc, thread)) { |
47 | ret = -ESRCH; |
48 | goto exit; |
49 | } |
50 | |
51 | uint8_t val, newval; |
52 | while(1) { |
53 | newval = val = thread->cancel_status; |
54 | if(val & TB_CANCELING) |
55 | goto exit; |
56 | |
57 | newval |= TB_CANCELING; |
58 | if(__sync_bool_compare_and_swap(&thread->cancel_status, val, newval)) |
59 | break; |
60 | } |
61 | if((val & TB_CANCEL_ENABLED) && !(val & TB_CANCEL_DEFERRED)) |
62 | SYSCALL3(__NR_tgkill, tb_pid, thread->tid, SIGCANCEL); |
63 | |
64 | exit: |
65 | tbthread_mutex_unlock(&desc_mutex); |
66 | return ret; |
67 | } |
68 | |
69 | //------------------------------------------------------------------------------ |
70 | // Cleanup list element |
71 | //------------------------------------------------------------------------------ |
72 | struct cleanup_elem { |
73 | void (*func)(void *); |
74 | void *arg; |
75 | }; |
76 | |
77 | //------------------------------------------------------------------------------ |
78 | // Release a cleanup handler |
79 | //------------------------------------------------------------------------------ |
80 | static void release_cleanup_handler(void *element) |
81 | { |
82 | free(element); |
83 | } |
84 | |
85 | //------------------------------------------------------------------------------ |
86 | // Call a cleanup handler |
87 | //------------------------------------------------------------------------------ |
88 | static void call_cleanup_handler(void *element) |
89 | { |
90 | struct cleanup_elem *e = (struct cleanup_elem *)element; |
91 | (*e->func)(e->arg); |
92 | } |
93 | |
94 | //------------------------------------------------------------------------------ |
95 | // Clear the cleanup handlers |
96 | //------------------------------------------------------------------------------ |
97 | void tb_clear_cleanup_handlers() |
98 | { |
99 | tbthread_t self = tbthread_self(); |
100 | list_for_each_elem(&self->cleanup_handlers, release_cleanup_handler); |
101 | list_clear(&self->cleanup_handlers); |
102 | } |
103 | |
104 | //------------------------------------------------------------------------------ |
105 | // Call the cleanup handlers |
106 | //------------------------------------------------------------------------------ |
107 | void tb_call_cleanup_handlers() |
108 | { |
109 | tbthread_t self = tbthread_self(); |
110 | list_for_each_elem(&self->cleanup_handlers, call_cleanup_handler); |
111 | list_for_each_elem(&self->cleanup_handlers, release_cleanup_handler); |
112 | list_clear(&self->cleanup_handlers); |
113 | } |
114 | |
115 | //------------------------------------------------------------------------------ |
116 | // Install a cleanup handler |
117 | //------------------------------------------------------------------------------ |
118 | void tbthread_cleanup_push(void (*func)(void *), void *arg) |
119 | { |
120 | tbthread_t self = tbthread_self(); |
121 | struct cleanup_elem *e = malloc(sizeof(struct cleanup_elem)); |
122 | e->func = func; |
123 | e->arg = arg; |
124 | list_add_elem(&self->cleanup_handlers, e, 1); |
125 | } |
126 | |
127 | //------------------------------------------------------------------------------ |
128 | // Remove a cleanup handler |
129 | //------------------------------------------------------------------------------ |
130 | void tbthread_cleanup_pop(int execute) |
131 | { |
132 | tbthread_t self = tbthread_self(); |
133 | list_t *node = self->cleanup_handlers.next; |
134 | if(!node) |
135 | return; |
136 | list_rm(node); |
137 | struct cleanup_elem *e = (struct cleanup_elem*)node->element; |
138 | if(execute) |
139 | (*e->func)(e->arg); |
140 | free(e); |
141 | free(node); |
142 | } |
143 | |
144 | //------------------------------------------------------------------------------ |
145 | // Set the cancelation bit |
146 | //------------------------------------------------------------------------------ |
147 | static int set_cancelation_bit(int bitmask, int value) |
148 | { |
149 | tbthread_t thread = tbthread_self(); |
150 | int val, newval, oldbit; |
151 | while(1) { |
152 | newval = val = thread->cancel_status; |
153 | if(val & bitmask) oldbit = 1; |
154 | else oldbit = 0; |
155 | if(value) newval |= bitmask; |
156 | else newval &= ~bitmask; |
157 | |
158 | if(__sync_bool_compare_and_swap(&thread->cancel_status, val, newval)) |
159 | break; |
160 | } |
161 | return oldbit; |
162 | } |
163 | |
164 | //------------------------------------------------------------------------------ |
165 | // Set cancelation state |
166 | //------------------------------------------------------------------------------ |
167 | int tbthread_setcancelstate(int state, int *oldstate) |
168 | { |
169 | if(state != TBTHREAD_CANCEL_ENABLE && state != TBTHREAD_CANCEL_DISABLE) |
170 | return -EINVAL; |
171 | |
172 | int olds = set_cancelation_bit(TB_CANCEL_ENABLED, state); |
173 | if(oldstate) |
174 | *oldstate = olds; |
175 | |
176 | if(state == TBTHREAD_CANCEL_ENABLE) |
177 | tbthread_testcancel(); |
178 | |
179 | return 0; |
180 | } |
181 | |
182 | //------------------------------------------------------------------------------ |
183 | // Set cancelation type |
184 | //------------------------------------------------------------------------------ |
185 | int tbthread_setcanceltype(int type, int *oldtype) |
186 | { |
187 | if(type != TBTHREAD_CANCEL_DEFERRED && type != TBTHREAD_CANCEL_ASYNCHRONOUS) |
188 | return -EINVAL; |
189 | |
190 | int oldt = set_cancelation_bit(TB_CANCEL_DEFERRED, type); |
191 | if(oldtype) |
192 | *oldtype = oldt; |
193 | |
194 | tbthread_testcancel(); |
195 | |
196 | return 0; |
197 | } |
198 | |
199 | //------------------------------------------------------------------------------ |
200 | // Check if the thread should be canceled |
201 | //------------------------------------------------------------------------------ |
202 | void tbthread_testcancel() |
203 | { |
204 | tbthread_t thread = tbthread_self(); |
205 | uint8_t val, newval; |
206 | |
207 | while(1) { |
208 | newval = val = thread->cancel_status; |
209 | if(!(val & TB_CANCEL_ENABLED) || !(val & TB_CANCELING) || |
210 | (val & TB_CANCELED)) |
211 | return; |
212 | newval |= TB_CANCELED; |
213 | if(__sync_bool_compare_and_swap(&thread->cancel_status, val, newval)) |
214 | break; |
215 | } |
216 | tbthread_exit(TBTHREAD_CANCELED); |
217 | } |
218 | |