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//------------------------------------------------------------------------------
26void 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//------------------------------------------------------------------------------
41int 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
64exit:
65 tbthread_mutex_unlock(&desc_mutex);
66 return ret;
67}
68
69//------------------------------------------------------------------------------
70// Cleanup list element
71//------------------------------------------------------------------------------
72struct cleanup_elem {
73 void (*func)(void *);
74 void *arg;
75};
76
77//------------------------------------------------------------------------------
78// Release a cleanup handler
79//------------------------------------------------------------------------------
80static void release_cleanup_handler(void *element)
81{
82 free(element);
83}
84
85//------------------------------------------------------------------------------
86// Call a cleanup handler
87//------------------------------------------------------------------------------
88static 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//------------------------------------------------------------------------------
97void 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//------------------------------------------------------------------------------
107void 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//------------------------------------------------------------------------------
118void 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//------------------------------------------------------------------------------
130void 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//------------------------------------------------------------------------------
147static 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//------------------------------------------------------------------------------
167int 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//------------------------------------------------------------------------------
185int 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//------------------------------------------------------------------------------
202void 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