1 /*********************************************************************/
2 // dar - disk archive - a backup/restoration program
3 // Copyright (C) 2002-2052 Denis Corbin
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 //
19 // to contact the author : http://dar.linux.free.fr/email.html
20 /*********************************************************************/
21 
22 #include "../my_config.h"
23 
24 extern "C"
25 {
26 #if HAVE_ERRNO_H
27 #include <errno.h>
28 #endif
29 
30 #if HAVE_STRING_H
31 #include <string.h>
32 #endif
33 }
34 
35 #include "erreurs.hpp"
36 #include "thread_cancellation.hpp"
37 #include "tools.hpp"
38 
39 #define CRITICAL_START						\
40     sigset_t Critical_section_mask_memory;			\
41     tools_block_all_signals(Critical_section_mask_memory);	\
42     pthread_mutex_lock(&access)
43 
44 #define CRITICAL_END pthread_mutex_unlock(&access);			\
45     tools_set_back_blocked_signals(Critical_section_mask_memory)
46 
47 using namespace std;
48 
49 namespace libdar
50 {
51 
52 	// class static variables
53 #if MUTEX_WORKS
54     pthread_mutex_t thread_cancellation::access = PTHREAD_MUTEX_INITIALIZER;
55     list<thread_cancellation *> thread_cancellation::info;
56     list<thread_cancellation::fields> thread_cancellation::preborn;
57     multimap<pthread_t, pthread_t> thread_cancellation::thread_asso;
58 #endif
59 
thread_cancellation()60     thread_cancellation::thread_cancellation()
61     {
62 #if MUTEX_WORKS
63 	bool bug = false;
64 
65 	status.tid = pthread_self();
66 	list<thread_cancellation *>::iterator ptr;
67 
68 	CRITICAL_START;
69 	ptr = info.begin();
70 	while(ptr != info.end() && *ptr != nullptr && (*ptr)->status.tid != status.tid)
71 	    ptr++;
72 	if(ptr == info.end()) // first object in that thread
73 	{
74 	    list<fields>::iterator it = preborn.begin();
75 	    while(it != preborn.end() && it->tid != status.tid)
76 		it++;
77 	    if(it == preborn.end()) // no pending cancellation for that thread
78 	    {
79 		status.block_delayed = false;
80 		status.immediate = true;
81 		status.cancellation = false;
82 		status.flag = 0;
83 	    }
84 	    else // pending cancellation for that thread
85 	    {
86 		status = *it;
87 		preborn.erase(it);
88 	    }
89 	}
90 	else  // an object already exist for that thread
91 	    if(*ptr == nullptr) // bug
92 		bug = true;
93 	    else  // an object already exists for that thread
94 		status = (*ptr)->status;
95 
96 	if(!bug)
97 	    info.push_back(this);
98 	CRITICAL_END;
99 
100 	if(bug)
101 	    throw SRC_BUG;
102 #endif
103     }
104 
~thread_cancellation()105     thread_cancellation::~thread_cancellation() throw(Ebug)
106     {
107 #if MUTEX_WORKS
108 	list<thread_cancellation *>::iterator ptr;
109 	bool bug = false;
110 
111 	CRITICAL_START;
112 	ptr = info.begin();
113 	while(ptr != info.end() && *ptr != this)
114 	    ptr++;
115 	if(ptr == info.end())
116 	    bug = true;
117 	else
118 	    if(*ptr == nullptr)
119 		bug = true;
120 	    else
121 	    {
122 		if((*ptr)->status.cancellation) // cancellation for that thread
123 		    preborn.push_back((*ptr)->status);
124 		info.erase(ptr);
125 	    }
126 	CRITICAL_END;
127 
128 	if(bug)
129 	    throw SRC_BUG;
130 #endif
131     }
132 
check_self_cancellation() const133     void thread_cancellation::check_self_cancellation() const
134     {
135 #if MUTEX_WORKS
136 	if(status.cancellation && (status.immediate || !status.block_delayed))
137 	{
138 	    (void)clear_pending_request(status.tid); // avoid other object of that thread to throw exception
139 	    throw Ethread_cancel(status.immediate, status.flag); // we can throw the exception now
140 	}
141 #endif
142     }
143 
block_delayed_cancellation(bool mode)144     void thread_cancellation::block_delayed_cancellation(bool mode)
145     {
146 #if MUTEX_WORKS
147 	list<thread_cancellation *>::iterator ptr;
148 
149 	    // we update all object of the current thread
150 	CRITICAL_START;
151 	ptr = info.begin();
152 	while(ptr != info.end())
153 	{
154 	    if(*ptr == nullptr)
155 		throw SRC_BUG;
156 	    if((*ptr)->status.tid == status.tid)
157 		(*ptr)->status.block_delayed = mode;
158 	    ptr++;
159 	}
160 	CRITICAL_END;
161 
162 	if(status.block_delayed != mode)
163 	    throw SRC_BUG;
164 	if(!mode)
165 	    check_self_cancellation();
166 #endif
167     }
168 
169 
170 #if MUTEX_WORKS
cancel(pthread_t tid,bool x_immediate,U_64 x_flag)171     void thread_cancellation::cancel(pthread_t tid, bool x_immediate, U_64 x_flag)
172     {
173 	bool found = false, bug = false, notused = false;
174 	multimap<pthread_t, pthread_t>::iterator debut;
175 	multimap<pthread_t, pthread_t>::iterator fin;
176 
177 	CRITICAL_START;
178 	set_cancellation_in_info_for(tid, true, x_immediate, x_flag, found, notused, bug);
179 
180 	if(!found && !bug)  // no thread_cancellation object exist for that thread
181 	    add_to_preborn(tid, x_immediate, x_flag);
182 
183 	find_asso_tid_with(tid,
184 			   debut,
185 			   fin);
186 	while(debut != fin && !bug)
187 	{
188 	    set_cancellation_in_info_for(debut->second, true, x_immediate, x_flag, found, notused, bug);
189 	    if(!found && !bug)
190 		add_to_preborn(debut->second, x_immediate, x_flag);
191 	    ++debut;
192 	}
193 	CRITICAL_END;
194 
195 	if(bug)
196 	    throw SRC_BUG;
197     }
198 #endif
199 
200 #if MUTEX_WORKS
cancel_status(pthread_t tid)201     bool thread_cancellation::cancel_status(pthread_t tid)
202     {
203 	bool ret, bug = false;
204 	list<thread_cancellation *>::iterator ptr;
205 
206 	CRITICAL_START;
207 	ptr = info.begin();
208 	while(ptr != info.end() && (*ptr) != nullptr && (*ptr)->status.tid != tid)
209 	    ptr++;
210 	if(ptr == info.end())
211 	{
212 	    list<fields>::iterator it = preborn.begin();
213 	    while(it != preborn.end() && it->tid != tid)
214 		it++;
215 
216 	    if(it == preborn.end())
217 		ret = false;
218 	    else
219 		ret = it->cancellation;
220 	}
221 	else
222 	    if(*ptr == nullptr)
223 		bug = true;
224 	    else
225 		ret = (*ptr)->status.cancellation;
226 	CRITICAL_END;
227 
228 	if(bug)
229 	    throw SRC_BUG;
230 
231 	return ret;
232     }
233 
clear_pending_request(pthread_t tid)234     bool thread_cancellation::clear_pending_request(pthread_t tid)
235     {
236 	bool ret = false, bug = false, found = false;
237 	multimap<pthread_t, pthread_t>::iterator debut;
238 	multimap<pthread_t, pthread_t>::iterator fin;
239 
240 	CRITICAL_START;
241 	set_cancellation_in_info_for(tid, false, false, 0, found, ret, bug);
242 	if(!found && !bug)
243 	    remove_from_preborn(tid, found, ret);
244 
245 	find_asso_tid_with(tid,
246 			   debut,
247 			   fin);
248 
249 	while(debut != fin && !bug)
250 	{
251 	    set_cancellation_in_info_for(debut->second, false, false, 0, found, ret, bug);
252 	    if(!found && !bug)
253 		remove_from_preborn(debut->second, found, ret);
254 	    ++debut;
255 	}
256 	CRITICAL_END;
257 
258 	if(bug)
259 	    throw SRC_BUG;
260 
261 	return ret;
262     }
263 
associate_tid_to_tid(pthread_t src,pthread_t dst)264     void thread_cancellation::associate_tid_to_tid(pthread_t src, pthread_t dst)
265     {
266 	CRITICAL_START;
267 	thread_asso.insert(pair<pthread_t, pthread_t>(src,dst));
268 	CRITICAL_END;
269     }
270 
remove_association_for_tid(pthread_t src)271     void thread_cancellation::remove_association_for_tid(pthread_t src)
272     {
273 	CRITICAL_START;
274 	thread_asso.erase(src);
275 	CRITICAL_END;
276     }
277 
remove_association_targeted_at(pthread_t dst)278     void thread_cancellation::remove_association_targeted_at(pthread_t dst)
279     {
280 	CRITICAL_START;
281 	multimap<pthread_t, pthread_t>::iterator it = thread_asso.begin();
282 	multimap<pthread_t, pthread_t>::iterator next = it;
283 
284 	while(it != thread_asso.end())
285 	{
286 	    if(it->second == dst)
287 	    {
288 		next = it;
289 		++next;
290 		thread_asso.erase(it);
291 		it = next;
292 	    }
293 	    else
294 		++it;
295 	}
296 
297 	CRITICAL_END;
298     }
299 
dead_thread(pthread_t tid)300     void thread_cancellation::dead_thread(pthread_t tid)
301     {
302 	bool found, prev;
303 	remove_association_for_tid(tid);
304 	remove_association_targeted_at(tid);
305 	remove_from_preborn(tid, found, prev);
306     }
307 
set_cancellation_in_info_for(pthread_t tid,bool cancel_status,bool x_immediate,U_64 x_flag,bool & found,bool & previous_val,bool & bug)308     void thread_cancellation::set_cancellation_in_info_for(pthread_t tid,
309 							   bool cancel_status,
310 							   bool x_immediate,
311 							   U_64 x_flag,
312 							   bool & found,
313 							   bool & previous_val,
314 							   bool & bug)
315     {
316 	list<thread_cancellation *>::iterator ptr = info.begin();
317 
318 	found = false;
319 	bug = false;
320 	while(ptr != info.end() && !bug)
321 	{
322 	    if(*ptr == nullptr)
323 		bug = true;
324 	    else
325 		if((*ptr)->status.tid == tid)
326 		{
327 		    found = true;
328 		    (*ptr)->status.immediate = x_immediate;
329 		    previous_val = (*ptr)->status.cancellation;
330 		    (*ptr)->status.cancellation = cancel_status;
331 		    (*ptr)->status.flag = x_flag;
332 		}
333 	    ptr++;
334 	}
335     }
336 
add_to_preborn(pthread_t tid,bool x_immediate,U_64 x_flag)337     void thread_cancellation::add_to_preborn(pthread_t tid, bool x_immediate, U_64 x_flag)
338     {
339 	list<fields>::iterator it = preborn.begin();
340 	fields tmp;
341 
342 	tmp.tid = tid;
343 	tmp.block_delayed = false;
344 	tmp.immediate = x_immediate;
345 	tmp.cancellation = true;
346 	tmp.flag = x_flag;
347 
348 	while(it != preborn.end() && it->tid != tid)
349 	    it++;
350 
351 	if(it != preborn.end())
352 	    *it = tmp;
353 	else
354 	    preborn.push_back(tmp);
355     }
356 
remove_from_preborn(pthread_t tid,bool & found,bool & prev)357     void thread_cancellation::remove_from_preborn(pthread_t tid, bool & found, bool & prev)
358     {
359 	list<fields>::iterator it = preborn.begin();
360 	found = false;
361 
362 	while(it != preborn.end())
363 	{
364 	    if(it->tid == tid)
365 	    {
366 		found = true;
367 		prev = it->cancellation;
368 		preborn.erase(it);
369 		it = preborn.begin();
370 	    }
371 	    else
372 		it++;
373 	}
374     }
375 
find_asso_tid_with(pthread_t tid,multimap<pthread_t,pthread_t>::iterator & debut,multimap<pthread_t,pthread_t>::iterator & fin)376     void thread_cancellation::find_asso_tid_with(pthread_t tid,
377 						 multimap<pthread_t, pthread_t>::iterator & debut,
378 						 multimap<pthread_t, pthread_t>::iterator & fin)
379     {
380 	pair< multimap<pthread_t, pthread_t>::iterator, multimap<pthread_t, pthread_t>::iterator > tmp = thread_asso.equal_range(tid);
381 	debut = tmp.first;
382 	fin = tmp.second;
383     }
384 
385 #endif
386 
387 } // end of namespace
388