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