1/* 2 * Copyright (c) 2010 Apple Inc. All rights reserved. 3 * Copyright (c) 2008 Likewise Software, Inc. All rights reserved. 4 * 5 * @APPLE_LICENSE_HEADER_START@ 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of Apple Inc. ("Apple") nor the names of its 17 * contributors may be used to endorse or promote products derived from 18 * this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 21 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 24 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 * 31 * Portions of this software have been released under the following terms: 32 * 33 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC. 34 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY 35 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION 36 * 37 * To anyone who acknowledges that this file is provided "AS IS" 38 * without any express or implied warranty: 39 * permission to use, copy, modify, and distribute this file for any 40 * purpose is hereby granted without fee, provided that the above 41 * copyright notices and this notice appears in all source code copies, 42 * and that none of the names of Open Software Foundation, Inc., Hewlett- 43 * Packard Company or Digital Equipment Corporation be used 44 * in advertising or publicity pertaining to distribution of the software 45 * without specific, written prior permission. Neither Open Software 46 * Foundation, Inc., Hewlett-Packard Company nor Digital 47 * Equipment Corporation makes any representations about the suitability 48 * of this software for any purpose. 49 * 50 * Copyright (c) 2007, Novell, Inc. All rights reserved. 51 * Redistribution and use in source and binary forms, with or without 52 * modification, are permitted provided that the following conditions 53 * are met: 54 * 55 * 1. Redistributions of source code must retain the above copyright 56 * notice, this list of conditions and the following disclaimer. 57 * 2. Redistributions in binary form must reproduce the above copyright 58 * notice, this list of conditions and the following disclaimer in the 59 * documentation and/or other materials provided with the distribution. 60 * 3. Neither the name of Novell Inc. nor the names of its contributors 61 * may be used to endorse or promote products derived from this 62 * this software without specific prior written permission. 63 * 64 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 65 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 66 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 67 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY 68 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 69 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 70 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 71 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 72 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 73 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 74 * 75 * @APPLE_LICENSE_HEADER_END@ 76 */ 77 78#include "dcethread-private.h" 79#include "dcethread-debug.h" 80#include "dcethread-exception.h" 81 82#include <config.h> 83#include <stdlib.h> 84#include <signal.h> 85#include <string.h> 86#include <time.h> 87#include <sys/time.h> 88 89static pthread_once_t dcethread_init_once = DCETHREAD_ONCE_INIT; 90static pthread_key_t dcethread_self_key; 91static pthread_attr_t dcethread_attr_default; 92static pthread_mutexattr_t dcethread_mutexattr_default; 93static pthread_condattr_t dcethread_condattr_default; 94 95#ifdef SIGRTMIN 96# define INTERRUPT_SIGNAL (SIGRTMIN + 5) 97#else 98# define INTERRUPT_SIGNAL (SIGXCPU) 99#endif 100 101static void 102interrupt_signal_handler(int sig ATTRIBUTE_UNUSED) 103{ 104 dcethread* thread = dcethread__self(); 105 106 /* In asynchronous interrupt mode all bets 107 are off anyway, so don't bother to lock */ 108 if (thread->flag.async) 109 { 110 dcethread__dispatchinterrupt(thread); 111 } 112} 113 114static void 115self_destructor(void* data) 116{ 117 if (data) 118 { 119 dcethread* self = (dcethread*) data; 120 dcethread__lock(self); 121 dcethread__change_state(self, DCETHREAD_STATE_DEAD); 122 dcethread__release(self); 123 dcethread__unlock(self); 124 } 125} 126 127static void 128init(void) 129{ 130 int cancelstate, oldstate; 131 struct sigaction act; 132 133 pthread_key_create(&dcethread_self_key, self_destructor); 134 135 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancelstate); 136 pthread_attr_init(&dcethread_attr_default); 137 pthread_mutexattr_init(&dcethread_mutexattr_default); 138 139#if defined(RPC_MUTEX_DEBUG) && defined(__USE_UNIX98) 140 pthread_mutexattr_settype(&dcethread_mutexattr_default, PTHREAD_MUTEX_ERRORCHECK); 141#endif 142 pthread_condattr_init(&dcethread_condattr_default); 143 dcethread__init_exceptions(); 144 pthread_setcancelstate(cancelstate, &oldstate); 145 146 sigemptyset(&act.sa_mask); 147 act.sa_handler = interrupt_signal_handler; 148 act.sa_flags = 0; 149 sigaction(INTERRUPT_SIGNAL, &act, NULL); 150 if (getenv("DCETHREAD_DEBUG")) 151 dcethread__debug_set_callback(dcethread__default_log_callback, NULL); 152} 153 154#ifdef __SUNPRO_C 155#pragma init dcethread__init 156#else 157void dcethread__init(void); 158#endif 159 160void dcethread__init(void) 161{ 162 pthread_once(&dcethread_init_once, init); 163} 164 165int 166dcethread__interrupt_syscall(dcethread* thread, void* data ATTRIBUTE_UNUSED) 167{ 168 pthread_kill(thread->pthread, INTERRUPT_SIGNAL); 169 return 0; 170} 171 172static int 173my_clock_gettime(struct timespec* tp) 174{ 175#ifdef CLOCK_REALTIME 176 return clock_gettime(CLOCK_REALTIME, tp); 177#else 178 int result; 179 struct timeval tv; 180 181 if ((result = gettimeofday(&tv, NULL))) 182 return result; 183 184 tp->tv_sec = tv.tv_sec; 185 tp->tv_nsec = tv.tv_usec * 1000; 186 187 return 0; 188#endif 189} 190 191int 192dcethread__interrupt_condwait(dcethread* thread, void* data) 193{ 194 condwait_info* info = (condwait_info*) data; 195 196 if (pthread_equal(info->mutex->owner, pthread_self())) 197 { 198 DCETHREAD_TRACE("Thread %p: already owned mutex used for interrupt", thread); 199 if (pthread_cond_broadcast(info->cond)) 200 { 201 DCETHREAD_ERROR("Thread %p: broadcast failed", thread); 202 return 0; 203 } 204 else 205 { 206 DCETHREAD_TRACE("Thread %p: broadcast to interrupt condwait", thread); 207 return 1; 208 } 209 } 210 else if (!pthread_mutex_trylock((pthread_mutex_t*) &info->mutex->mutex)) 211 { 212 info->mutex->owner = pthread_self(); 213 if (pthread_cond_broadcast(info->cond)) 214 { 215 DCETHREAD_ERROR("Thread %p: broadcast failed", thread); 216 info->mutex->owner = (pthread_t) -1; 217 pthread_mutex_unlock((pthread_mutex_t*) &info->mutex->mutex); 218 return 0; 219 } 220 else 221 { 222 DCETHREAD_TRACE("Thread %p: broadcast to interrupt condwait", thread); 223 info->mutex->owner = (pthread_t) -1; 224 pthread_mutex_unlock((pthread_mutex_t*) &info->mutex->mutex); 225 return 1; 226 } 227 } 228 else 229 { 230 DCETHREAD_VERBOSE("Thread %p: could not acquire lock to interrupt condwait", thread); 231 return 0; 232 } 233} 234 235dcethread* 236dcethread__new (void) 237{ 238 dcethread* thread; 239 240 /* Ensure thread system is initialized */ 241 dcethread__init(); 242 243 thread = calloc(1, sizeof(dcethread)); 244 245 thread->refs = 1; 246 thread->flag.interruptible = 1; 247 thread->flag.joinable = 0; 248 thread->flag.async = 0; 249 thread->state = DCETHREAD_STATE_CREATED; 250 251 pthread_mutex_init((pthread_mutex_t*) &thread->lock, NULL); 252 pthread_cond_init((pthread_cond_t*) &thread->state_change, NULL); 253 254 /* Set default interrupt handler that throws an interrupt exception */ 255 thread->handle_interrupt = dcethread__exc_handle_interrupt; 256 thread->handle_interrupt_data = (void*) &dcethread_interrupt_e; 257 258 /* Set default interrupt method that pokes the thread with a signal */ 259 thread->interrupt = dcethread__interrupt_syscall; 260 thread->interrupt_data = NULL; 261 262 return thread; 263} 264 265dcethread* 266dcethread__self(void) 267{ 268 dcethread* thread; 269 270 /* Ensure thread system is initialized */ 271 dcethread__init(); 272 273 /* Get self pointer from TLS */ 274 thread = (dcethread*) pthread_getspecific(dcethread_self_key); 275 if (!thread) 276 { 277 /* Lazily create it if it didn't already exist */ 278 thread = dcethread__new(); 279 thread->pthread = pthread_self(); 280 pthread_setspecific(dcethread_self_key, (void*) thread); 281 thread->state = DCETHREAD_STATE_ACTIVE; 282 } 283 284 return thread; 285} 286 287void 288dcethread__init_self(dcethread* thread) 289{ 290 /* Ensure thread system is initialized */ 291 dcethread__init(); 292 293 pthread_setspecific(dcethread_self_key, (void*) thread); 294 dcethread__lock(thread); 295 dcethread__change_state(thread, DCETHREAD_STATE_ACTIVE); 296 dcethread__unlock(thread); 297} 298 299static void 300dcethread__sanity(dcethread* thread) 301{ 302 if (!thread) { 303 DCETHREAD_ERROR("NULL thread encountered"); 304 return; 305 } 306 if ((int)thread->refs < 0) 307 DCETHREAD_ERROR("Thread %p: ref count < 0", thread); 308 if (!thread->flag.locked) 309 DCETHREAD_ERROR("Thread %p: not locked when expected", thread); 310 switch (thread->state) 311 { 312 case DCETHREAD_STATE_CREATED: 313 case DCETHREAD_STATE_ACTIVE: 314 case DCETHREAD_STATE_BLOCKED: 315 case DCETHREAD_STATE_INTERRUPT: 316 if (thread->refs == 0) 317 DCETHREAD_ERROR("Thread %p: ref count = 0 in living thread", 318 dcethread__self()); 319 break; 320 case DCETHREAD_STATE_DEAD: 321 break; 322 } 323} 324 325void 326dcethread__delete(dcethread* thread) 327{ 328 DCETHREAD_TRACE("Thread %p: deleted", thread); 329 pthread_mutex_destroy((pthread_mutex_t*) &thread->lock); 330 pthread_cond_destroy((pthread_cond_t*) &thread->state_change); 331 if (thread->flag.joinable) 332 pthread_detach(thread->pthread); 333 free((void*) thread); 334} 335 336void 337dcethread__retain(dcethread* thread) 338{ 339 dcethread__sanity(thread); 340 if (thread->refs == 0) 341 { 342 DCETHREAD_ERROR("Attempted to retain freed thread %p", thread); 343 } 344 else 345 { 346 thread->refs++; 347 DCETHREAD_TRACE("Thread %p: ref count increased to %i", thread, thread->refs); 348 } 349} 350 351void 352dcethread__release(dcethread* thread) 353{ 354 dcethread__sanity(thread); 355 if (thread->refs <= 0) 356 { 357 DCETHREAD_ERROR("Thread %p: attempted to release freed thread", thread); 358 } 359 else 360 { 361 thread->refs--; 362 DCETHREAD_TRACE("Thread %p: ref count decreased to %i", thread, thread->refs); 363 } 364} 365 366void 367dcethread__lock(dcethread* thread) 368{ 369 if (pthread_mutex_lock((pthread_mutex_t*) &thread->lock)) 370 DCETHREAD_ERROR("Thread %p: failed to lock mutex", thread); 371 thread->flag.locked = 1; 372 dcethread__sanity(thread); 373 DCETHREAD_TRACE("Thread %p: locked", thread); 374} 375 376void 377dcethread__unlock(dcethread* thread) 378{ 379 unsigned int refs; 380 381 dcethread__sanity(thread); 382 383 /* Access reference count while thread is still locked 384 in order to avoid race conditions */ 385 refs = thread->refs; 386 387 thread->flag.locked = 0; 388 if (pthread_mutex_unlock((pthread_mutex_t*) &thread->lock)) 389 DCETHREAD_ERROR("Thread %p: failed to unlock mutex", thread); 390 DCETHREAD_TRACE("Thread %p: unlocked", thread); 391 392 if (refs == 0) 393 { 394 dcethread__delete(thread); 395 } 396} 397 398void 399dcethread__wait(dcethread* thread) 400{ 401 dcethread__sanity(thread); 402 thread->flag.locked = 0; 403 pthread_cond_wait((pthread_cond_t*) &thread->state_change, 404 (pthread_mutex_t*) &thread->lock); 405 thread->flag.locked = 1; 406} 407 408void 409dcethread__timedwait(dcethread* thread, struct timespec* ts) 410{ 411 dcethread__sanity(thread); 412 thread->flag.locked = 0; 413 pthread_cond_timedwait((pthread_cond_t*) &thread->state_change, 414 (pthread_mutex_t*) &thread->lock, ts); 415 thread->flag.locked = 1; 416} 417 418static const char* 419state_name(int state) 420{ 421#define CASE(token) case token: return #token 422 switch (state) 423 { 424 CASE(DCETHREAD_STATE_CREATED); 425 CASE(DCETHREAD_STATE_ACTIVE); 426 CASE(DCETHREAD_STATE_BLOCKED); 427 CASE(DCETHREAD_STATE_INTERRUPT); 428 CASE(DCETHREAD_STATE_DEAD); 429 } 430 return "UNKNOWN"; 431#undef CASE 432} 433 434void 435dcethread__change_state(dcethread* thread, int state) 436{ 437 DCETHREAD_TRACE("Thread %p: state transition %s -> %s", 438 thread, 439 state_name(thread->state), 440 state_name(state)); 441 thread->state = state; 442 pthread_cond_broadcast((pthread_cond_t*) &thread->state_change); 443} 444 445#ifndef HAVE_PTHREAD_LOCK_GLOBAL_NP 446static pthread_mutex_t dcethread_g_global_lock = PTHREAD_MUTEX_INITIALIZER; 447#endif /* HAVE_PTHREAD_LOCK_GLOBAL_NP */ 448 449void dcethread__lock_global(void) 450{ 451#ifdef HAVE_PTHREAD_LOCK_GLOBAL_NP 452 pthread_lock_global_np(); 453#else 454 pthread_mutex_lock(&dcethread_g_global_lock); 455#endif /* HAVE_PTHREAD_LOCK_GLOBAL_NP */ 456} 457 458void dcethread__unlock_global(void) 459{ 460#ifdef HAVE_PTHREAD_UNLOCK_GLOBAL_NP 461 pthread_unlock_global_np(); 462#else 463 pthread_mutex_unlock(&dcethread_g_global_lock); 464#endif /* HAVE_PTHREAD_UNLOCK_GLOBAL_NP */ 465} 466 467void 468dcethread__dispatchinterrupt(dcethread* thread) 469{ 470 DCETHREAD_TRACE("Thread %p: interrupt acknowledged", thread); 471 thread->handle_interrupt(thread, thread->handle_interrupt_data); 472} 473 474void 475dcethread__interrupt(dcethread* thread) 476{ 477 int count = 0; 478 int old_state = thread->state; 479 480 if (old_state == DCETHREAD_STATE_INTERRUPT || 481 old_state == DCETHREAD_STATE_DEAD) 482 { 483 /* Don't bother */ 484 return; 485 } 486 487 DCETHREAD_TRACE("Thread %p: interrupt posted", thread); 488 dcethread__change_state(thread, DCETHREAD_STATE_INTERRUPT); 489 490 /* We need to poke the thread and wait for an acknowledgement of the interrupt if: */ 491 if (thread != dcethread__self() && /* The interrupted thread is not us, and */ 492 thread->flag.interruptible && /* The thread can be interrupted, and */ 493 old_state == DCETHREAD_STATE_BLOCKED) /* The thread was blocked */ 494 { 495 /* FIXME: potential livelock here if another thread 496 re-interrupts when the lock is released */ 497 while (thread->state == DCETHREAD_STATE_INTERRUPT) 498 { 499 struct timespec waittime; 500 501 if (count > 2) 502 DCETHREAD_WARNING("Thread %p: still not interrupted after %i ms", thread, count * 100); 503 504 if (thread->interrupt(thread, thread->interrupt_data)) 505 { 506 /* Interrupt is guaranteed to have succeeded, so 507 leave state change wait loop */ 508 break; 509 } 510 511 count++; 512 513 my_clock_gettime(&waittime); 514 waittime.tv_nsec += 100000000; 515 516 if (waittime.tv_nsec > 1000000000) 517 { 518 waittime.tv_nsec -= 1000000000; 519 waittime.tv_sec += 1; 520 } 521 522 /* Wait for state change */ 523 dcethread__timedwait(thread, &waittime); 524 } 525 } 526} 527 528void 529dcethread__set_interrupt_handler(dcethread* thread, void (*handle_interrupt)(dcethread*, void*), void* data) 530{ 531 thread->handle_interrupt = handle_interrupt; 532 thread->handle_interrupt_data = data; 533} 534 535int 536dcethread__begin_block(dcethread* thread, int (*interrupt)(dcethread*, void*), void* data, 537 int (**old_interrupt)(dcethread*, void*), void** old_data) 538{ 539 int state; 540 int interruptible; 541 542 dcethread__lock(thread); 543 state = thread->state; 544 interruptible = thread->flag.interruptible; 545 /* If thread is currently active */ 546 if (state == DCETHREAD_STATE_ACTIVE) 547 { 548 /* Set up interruption callbacks */ 549 if (old_interrupt) 550 *old_interrupt = thread->interrupt; 551 if (old_data) 552 *old_data = thread->interrupt_data; 553 if (interrupt) 554 thread->interrupt = interrupt; 555 if (data) 556 thread->interrupt_data = data; 557 558 /* Change to blocked state */ 559 dcethread__change_state(thread, DCETHREAD_STATE_BLOCKED); 560 } 561 /* If an interrupt request has been posted (and we can be interrupted) */ 562 else if (state == DCETHREAD_STATE_INTERRUPT && interruptible) 563 { 564 /* Clear request */ 565 dcethread__change_state(thread, DCETHREAD_STATE_ACTIVE); 566 } 567 dcethread__unlock(thread); 568 569 return state == DCETHREAD_STATE_INTERRUPT && interruptible; 570} 571 572int 573dcethread__poll_end_block(dcethread* thread, int (*interrupt)(dcethread*, void*), void* data) 574{ 575 int state; 576 int interruptible; 577 578 dcethread__lock(thread); 579 state = thread->state; 580 interruptible = thread->flag.interruptible; 581 582 if (state == DCETHREAD_STATE_INTERRUPT) 583 { 584 if (interrupt) 585 thread->interrupt = interrupt; 586 if (data) 587 thread->interrupt_data = data; 588 if ((interruptible && state == DCETHREAD_STATE_INTERRUPT) || 589 (state == DCETHREAD_STATE_BLOCKED)) 590 dcethread__change_state(thread, DCETHREAD_STATE_ACTIVE); 591 } 592 593 dcethread__unlock(thread); 594 595 return state == DCETHREAD_STATE_INTERRUPT && interruptible; 596} 597 598int 599dcethread__end_block(dcethread* thread, int (*interrupt)(dcethread*, void*), void* data) 600{ 601 int state; 602 int interruptible; 603 604 dcethread__lock(thread); 605 state = thread->state; 606 interruptible = thread->flag.interruptible; 607 608 /* Switch state back to active if: 609 - We were not interrupted during the block 610 - We were interrupted and the thread is interruptible 611 612 If we were interrupted but are not currently interruptible, 613 we want to leave the state alone so that is is caught when 614 interruptiblility is enabled again. 615 */ 616 if ((interruptible && state == DCETHREAD_STATE_INTERRUPT) || 617 (state == DCETHREAD_STATE_BLOCKED)) 618 { 619 if (interrupt) 620 thread->interrupt = interrupt; 621 if (data) 622 thread->interrupt_data = data; 623 dcethread__change_state(thread, DCETHREAD_STATE_ACTIVE); 624 } 625 626 dcethread__unlock(thread); 627 628 return state == DCETHREAD_STATE_INTERRUPT && interruptible; 629} 630 631void 632dcethread__cleanup_self(dcethread* self) 633{ 634 /* Ensure thread system is initialized */ 635 dcethread__init(); 636 637 dcethread__change_state(self, DCETHREAD_STATE_DEAD); 638 dcethread__release(self); 639 pthread_setspecific(dcethread_self_key, NULL); 640} 641