1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ 2 /* dbus-threads.h D-Bus threads handling 3 * 4 * Copyright (C) 2002, 2003, 2006 Red Hat Inc. 5 * 6 * Licensed under the Academic Free License version 2.1 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 #include <config.h> 24 #include "dbus-threads.h" 25 #include "dbus-internals.h" 26 #include "dbus-threads-internal.h" 27 #include "dbus-list.h" 28 29 static int thread_init_generation = 0; 30 31 static DBusList *uninitialized_rmutex_list = NULL; 32 static DBusList *uninitialized_cmutex_list = NULL; 33 static DBusList *uninitialized_condvar_list = NULL; 34 35 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */ 36 #define _DBUS_DUMMY_MUTEX ((DBusMutex*)0xABCDEF) 37 #define _DBUS_DUMMY_RMUTEX ((DBusRMutex *) _DBUS_DUMMY_MUTEX) 38 #define _DBUS_DUMMY_CMUTEX ((DBusCMutex *) _DBUS_DUMMY_MUTEX) 39 40 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */ 41 #define _DBUS_DUMMY_CONDVAR ((DBusCondVar*)0xABCDEF2) 42 43 /** 44 * @defgroup DBusThreadsInternals Thread functions 45 * @ingroup DBusInternals 46 * @brief _dbus_rmutex_lock(), etc. 47 * 48 * Functions and macros related to threads and thread locks. 49 * 50 * @{ 51 */ 52 53 /** 54 * Creates a new mutex 55 * or creates a no-op mutex if threads are not initialized. 56 * May return #NULL even if threads are initialized, indicating 57 * out-of-memory. 58 * 59 * If possible, the mutex returned by this function is recursive, to 60 * avoid deadlocks. However, that cannot be relied on. 61 * 62 * The extra level of indirection given by allocating a pointer 63 * to point to the mutex location allows the threading 64 * module to swap out dummy mutexes for a real mutex so libraries 65 * can initialize threads even after the D-Bus API has been used. 66 * 67 * @param location_p the location of the new mutex, can return #NULL on OOM 68 */ 69 void 70 _dbus_rmutex_new_at_location (DBusRMutex **location_p) 71 { 72 _dbus_assert (location_p != NULL); 73 74 if (thread_init_generation == _dbus_current_generation) 75 { 76 *location_p = _dbus_platform_rmutex_new (); 77 } 78 else 79 { 80 *location_p = _DBUS_DUMMY_RMUTEX; 81 82 if (!_dbus_list_append (&uninitialized_rmutex_list, location_p)) 83 *location_p = NULL; 84 } 85 } 86 87 /** 88 * Creates a new mutex 89 * or creates a no-op mutex if threads are not initialized. 90 * May return #NULL even if threads are initialized, indicating 91 * out-of-memory. 92 * 93 * The returned mutex is suitable for use with condition variables. 94 * 95 * The extra level of indirection given by allocating a pointer 96 * to point to the mutex location allows the threading 97 * module to swap out dummy mutexes for a real mutex so libraries 98 * can initialize threads even after the D-Bus API has been used. 99 * 100 * @param location_p the location of the new mutex, can return #NULL on OOM 101 */ 102 void 103 _dbus_cmutex_new_at_location (DBusCMutex **location_p) 104 { 105 _dbus_assert (location_p != NULL); 106 107 if (thread_init_generation == _dbus_current_generation) 108 { 109 *location_p = _dbus_platform_cmutex_new (); 110 } 111 else 112 { 113 *location_p = _DBUS_DUMMY_CMUTEX; 114 115 if (!_dbus_list_append (&uninitialized_cmutex_list, location_p)) 116 *location_p = NULL; 117 } 118 } 119 120 /** 121 * Frees a DBusRMutex or removes it from the uninitialized mutex list; 122 * does nothing if passed a #NULL pointer. 123 */ 124 void 125 _dbus_rmutex_free_at_location (DBusRMutex **location_p) 126 { 127 if (location_p == NULL) 128 return; 129 130 if (thread_init_generation == _dbus_current_generation) 131 { 132 if (*location_p != NULL) 133 _dbus_platform_rmutex_free (*location_p); 134 } 135 else 136 { 137 _dbus_assert (*location_p == NULL || *location_p == _DBUS_DUMMY_RMUTEX); 138 139 _dbus_list_remove (&uninitialized_rmutex_list, location_p); 140 } 141 } 142 143 /** 144 * Frees a DBusCMutex and removes it from the 145 * uninitialized mutex list; 146 * does nothing if passed a #NULL pointer. 147 */ 148 void 149 _dbus_cmutex_free_at_location (DBusCMutex **location_p) 150 { 151 if (location_p == NULL) 152 return; 153 154 if (thread_init_generation == _dbus_current_generation) 155 { 156 if (*location_p != NULL) 157 _dbus_platform_cmutex_free (*location_p); 158 } 159 else 160 { 161 _dbus_assert (*location_p == NULL || *location_p == _DBUS_DUMMY_CMUTEX); 162 163 _dbus_list_remove (&uninitialized_cmutex_list, location_p); 164 } 165 } 166 167 /** 168 * Locks a mutex. Does nothing if passed a #NULL pointer. 169 * Locks may be recursive if threading implementation initialized 170 * recursive locks. 171 */ 172 void 173 _dbus_rmutex_lock (DBusRMutex *mutex) 174 { 175 if (mutex && thread_init_generation == _dbus_current_generation) 176 _dbus_platform_rmutex_lock (mutex); 177 } 178 179 /** 180 * Locks a mutex. Does nothing if passed a #NULL pointer. 181 * Locks may be recursive if threading implementation initialized 182 * recursive locks. 183 */ 184 void 185 _dbus_cmutex_lock (DBusCMutex *mutex) 186 { 187 if (mutex && thread_init_generation == _dbus_current_generation) 188 _dbus_platform_cmutex_lock (mutex); 189 } 190 191 /** 192 * Unlocks a mutex. Does nothing if passed a #NULL pointer. 193 * 194 * @returns #TRUE on success 195 */ 196 void 197 _dbus_rmutex_unlock (DBusRMutex *mutex) 198 { 199 if (mutex && thread_init_generation == _dbus_current_generation) 200 _dbus_platform_rmutex_unlock (mutex); 201 } 202 203 /** 204 * Unlocks a mutex. Does nothing if passed a #NULL pointer. 205 * 206 * @returns #TRUE on success 207 */ 208 void 209 _dbus_cmutex_unlock (DBusCMutex *mutex) 210 { 211 if (mutex && thread_init_generation == _dbus_current_generation) 212 _dbus_platform_cmutex_unlock (mutex); 213 } 214 215 /** 216 * Creates a new condition variable using the function supplied 217 * to dbus_threads_init(), or creates a no-op condition variable 218 * if threads are not initialized. May return #NULL even if 219 * threads are initialized, indicating out-of-memory. 220 * 221 * @returns new mutex or #NULL 222 */ 223 DBusCondVar * 224 _dbus_condvar_new (void) 225 { 226 if (thread_init_generation == _dbus_current_generation) 227 return _dbus_platform_condvar_new (); 228 else 229 return _DBUS_DUMMY_CONDVAR; 230 } 231 232 233 /** 234 * This does the same thing as _dbus_condvar_new. It however 235 * gives another level of indirection by allocating a pointer 236 * to point to the condvar location. This allows the threading 237 * module to swap out dummy condvars for a real condvar so libraries 238 * can initialize threads even after the D-Bus API has been used. 239 * 240 * @returns the location of a new condvar or #NULL on OOM 241 */ 242 243 void 244 _dbus_condvar_new_at_location (DBusCondVar **location_p) 245 { 246 _dbus_assert (location_p != NULL); 247 248 if (thread_init_generation == _dbus_current_generation) 249 { 250 *location_p = _dbus_condvar_new(); 251 } 252 else 253 { 254 *location_p = _DBUS_DUMMY_CONDVAR; 255 256 if (!_dbus_list_append (&uninitialized_condvar_list, location_p)) 257 *location_p = NULL; 258 } 259 } 260 261 262 /** 263 * Frees a conditional variable created with dbus_condvar_new(); does 264 * nothing if passed a #NULL pointer. 265 */ 266 void 267 _dbus_condvar_free (DBusCondVar *cond) 268 { 269 if (cond && thread_init_generation == _dbus_current_generation) 270 _dbus_platform_condvar_free (cond); 271 } 272 273 /** 274 * Frees a conditional variable and removes it from the 275 * uninitialized_condvar_list; 276 * does nothing if passed a #NULL pointer. 277 */ 278 void 279 _dbus_condvar_free_at_location (DBusCondVar **location_p) 280 { 281 if (location_p == NULL) 282 return; 283 284 if (thread_init_generation == _dbus_current_generation) 285 { 286 if (*location_p != NULL) 287 _dbus_platform_condvar_free (*location_p); 288 } 289 else 290 { 291 _dbus_assert (*location_p == NULL || *location_p == _DBUS_DUMMY_CONDVAR); 292 293 _dbus_list_remove (&uninitialized_condvar_list, location_p); 294 } 295 } 296 297 /** 298 * Atomically unlocks the mutex and waits for the conditions 299 * variable to be signalled. Locks the mutex again before 300 * returning. 301 * Does nothing if passed a #NULL pointer. 302 */ 303 void 304 _dbus_condvar_wait (DBusCondVar *cond, 305 DBusCMutex *mutex) 306 { 307 if (cond && mutex && thread_init_generation == _dbus_current_generation) 308 _dbus_platform_condvar_wait (cond, mutex); 309 } 310 311 /** 312 * Atomically unlocks the mutex and waits for the conditions variable 313 * to be signalled, or for a timeout. Locks the mutex again before 314 * returning. Does nothing if passed a #NULL pointer. Return value 315 * is #FALSE if we timed out, #TRUE otherwise. 316 * 317 * @param cond the condition variable 318 * @param mutex the mutex 319 * @param timeout_milliseconds the maximum time to wait 320 * @returns #FALSE if the timeout occurred, #TRUE if not 321 */ 322 dbus_bool_t 323 _dbus_condvar_wait_timeout (DBusCondVar *cond, 324 DBusCMutex *mutex, 325 int timeout_milliseconds) 326 { 327 if (cond && mutex && thread_init_generation == _dbus_current_generation) 328 return _dbus_platform_condvar_wait_timeout (cond, mutex, 329 timeout_milliseconds); 330 else 331 return TRUE; 332 } 333 334 /** 335 * If there are threads waiting on the condition variable, wake 336 * up exactly one. 337 * Does nothing if passed a #NULL pointer. 338 */ 339 void 340 _dbus_condvar_wake_one (DBusCondVar *cond) 341 { 342 if (cond && thread_init_generation == _dbus_current_generation) 343 _dbus_platform_condvar_wake_one (cond); 344 } 345 346 static void 347 shutdown_global_locks (void *data) 348 { 349 DBusRMutex ***locks = data; 350 int i; 351 352 i = 0; 353 while (i < _DBUS_N_GLOBAL_LOCKS) 354 { 355 if (*(locks[i]) != NULL) 356 _dbus_platform_rmutex_free (*(locks[i])); 357 358 *(locks[i]) = NULL; 359 ++i; 360 } 361 362 dbus_free (locks); 363 } 364 365 static void 366 shutdown_uninitialized_locks (void *data) 367 { 368 _dbus_list_clear (&uninitialized_rmutex_list); 369 _dbus_list_clear (&uninitialized_cmutex_list); 370 _dbus_list_clear (&uninitialized_condvar_list); 371 } 372 373 static dbus_bool_t 374 init_uninitialized_locks (void) 375 { 376 DBusList *link; 377 378 _dbus_assert (thread_init_generation != _dbus_current_generation); 379 380 link = uninitialized_rmutex_list; 381 while (link != NULL) 382 { 383 DBusRMutex **mp; 384 385 mp = link->data; 386 _dbus_assert (*mp == _DBUS_DUMMY_RMUTEX); 387 388 *mp = _dbus_platform_rmutex_new (); 389 if (*mp == NULL) 390 goto fail_mutex; 391 392 link = _dbus_list_get_next_link (&uninitialized_rmutex_list, link); 393 } 394 395 link = uninitialized_cmutex_list; 396 while (link != NULL) 397 { 398 DBusCMutex **mp; 399 400 mp = link->data; 401 _dbus_assert (*mp == _DBUS_DUMMY_CMUTEX); 402 403 *mp = _dbus_platform_cmutex_new (); 404 if (*mp == NULL) 405 goto fail_mutex; 406 407 link = _dbus_list_get_next_link (&uninitialized_cmutex_list, link); 408 } 409 410 link = uninitialized_condvar_list; 411 while (link != NULL) 412 { 413 DBusCondVar **cp; 414 415 cp = (DBusCondVar **)link->data; 416 _dbus_assert (*cp == _DBUS_DUMMY_CONDVAR); 417 418 *cp = _dbus_platform_condvar_new (); 419 if (*cp == NULL) 420 goto fail_condvar; 421 422 link = _dbus_list_get_next_link (&uninitialized_condvar_list, link); 423 } 424 425 _dbus_list_clear (&uninitialized_rmutex_list); 426 _dbus_list_clear (&uninitialized_cmutex_list); 427 _dbus_list_clear (&uninitialized_condvar_list); 428 429 if (!_dbus_register_shutdown_func (shutdown_uninitialized_locks, 430 NULL)) 431 goto fail_condvar; 432 433 return TRUE; 434 435 fail_condvar: 436 link = uninitialized_condvar_list; 437 while (link != NULL) 438 { 439 DBusCondVar **cp; 440 441 cp = link->data; 442 443 if (*cp != _DBUS_DUMMY_CONDVAR && *cp != NULL) 444 _dbus_platform_condvar_free (*cp); 445 446 *cp = _DBUS_DUMMY_CONDVAR; 447 448 link = _dbus_list_get_next_link (&uninitialized_condvar_list, link); 449 } 450 451 fail_mutex: 452 link = uninitialized_rmutex_list; 453 while (link != NULL) 454 { 455 DBusRMutex **mp; 456 457 mp = link->data; 458 459 if (*mp != _DBUS_DUMMY_RMUTEX && *mp != NULL) 460 _dbus_platform_rmutex_free (*mp); 461 462 *mp = _DBUS_DUMMY_RMUTEX; 463 464 link = _dbus_list_get_next_link (&uninitialized_rmutex_list, link); 465 } 466 467 link = uninitialized_cmutex_list; 468 while (link != NULL) 469 { 470 DBusCMutex **mp; 471 472 mp = link->data; 473 474 if (*mp != _DBUS_DUMMY_CMUTEX && *mp != NULL) 475 _dbus_platform_cmutex_free (*mp); 476 477 *mp = _DBUS_DUMMY_CMUTEX; 478 479 link = _dbus_list_get_next_link (&uninitialized_cmutex_list, link); 480 } 481 482 return FALSE; 483 } 484 485 static dbus_bool_t 486 init_locks (void) 487 { 488 int i; 489 DBusRMutex ***dynamic_global_locks; 490 DBusRMutex **global_locks[] = { 491 #define LOCK_ADDR(name) (& _dbus_lock_##name) 492 LOCK_ADDR (win_fds), 493 LOCK_ADDR (sid_atom_cache), 494 LOCK_ADDR (list), 495 LOCK_ADDR (connection_slots), 496 LOCK_ADDR (pending_call_slots), 497 LOCK_ADDR (server_slots), 498 LOCK_ADDR (message_slots), 499 #if !DBUS_USE_SYNC 500 LOCK_ADDR (atomic), 501 #endif 502 LOCK_ADDR (bus), 503 LOCK_ADDR (bus_datas), 504 LOCK_ADDR (shutdown_funcs), 505 LOCK_ADDR (system_users), 506 LOCK_ADDR (message_cache), 507 LOCK_ADDR (shared_connections), 508 LOCK_ADDR (machine_uuid) 509 #undef LOCK_ADDR 510 }; 511 512 _dbus_assert (_DBUS_N_ELEMENTS (global_locks) == 513 _DBUS_N_GLOBAL_LOCKS); 514 515 i = 0; 516 517 dynamic_global_locks = dbus_new (DBusRMutex**, _DBUS_N_GLOBAL_LOCKS); 518 if (dynamic_global_locks == NULL) 519 goto failed; 520 521 while (i < _DBUS_N_ELEMENTS (global_locks)) 522 { 523 *global_locks[i] = _dbus_platform_rmutex_new (); 524 525 if (*global_locks[i] == NULL) 526 goto failed; 527 528 dynamic_global_locks[i] = global_locks[i]; 529 530 ++i; 531 } 532 533 if (!_dbus_register_shutdown_func (shutdown_global_locks, 534 dynamic_global_locks)) 535 goto failed; 536 537 if (!init_uninitialized_locks ()) 538 goto failed; 539 540 return TRUE; 541 542 failed: 543 dbus_free (dynamic_global_locks); 544 545 for (i = i - 1; i >= 0; i--) 546 { 547 _dbus_platform_rmutex_free (*global_locks[i]); 548 *global_locks[i] = NULL; 549 } 550 return FALSE; 551 } 552 553 /** @} */ /* end of internals */ 554 555 /** 556 * @defgroup DBusThreads Thread functions 557 * @ingroup DBus 558 * @brief dbus_threads_init() and dbus_threads_init_default() 559 * 560 * Functions and macros related to threads and thread locks. 561 * 562 * If threads are initialized, the D-Bus library has locks on all 563 * global data structures. In addition, each #DBusConnection has a 564 * lock, so only one thread at a time can touch the connection. (See 565 * @ref DBusConnection for more on connection locking.) 566 * 567 * Most other objects, however, do not have locks - they can only be 568 * used from a single thread at a time, unless you lock them yourself. 569 * For example, a #DBusMessage can't be modified from two threads 570 * at once. 571 * 572 * @{ 573 */ 574 575 /** 576 * Initializes threads, like dbus_threads_init_default(). 577 * This version previously allowed user-specified threading 578 * primitives, but since D-Bus 1.6 it ignores them and behaves 579 * exactly like dbus_threads_init_default(). 580 * 581 * @param functions ignored, formerly functions for using threads 582 * @returns #TRUE on success, #FALSE if no memory 583 */ 584 dbus_bool_t 585 dbus_threads_init (const DBusThreadFunctions *functions) 586 { 587 if (thread_init_generation == _dbus_current_generation) 588 return TRUE; 589 590 if (!init_locks ()) 591 return FALSE; 592 593 thread_init_generation = _dbus_current_generation; 594 595 return TRUE; 596 } 597 598 599 600 /* Default thread implemenation */ 601 602 /** 603 * Initializes threads. If this function is not called, the D-Bus 604 * library will not lock any data structures. If it is called, D-Bus 605 * will do locking, at some cost in efficiency. Note that this 606 * function must be called BEFORE the second thread is started. 607 * 608 * It's safe to call dbus_threads_init_default() as many times as you 609 * want, but only the first time will have an effect. 610 * 611 * dbus_shutdown() reverses the effects of this function when it 612 * resets all global state in libdbus. 613 * 614 * @returns #TRUE on success, #FALSE if not enough memory 615 */ 616 dbus_bool_t 617 dbus_threads_init_default (void) 618 { 619 return _dbus_threads_init_platform_specific (); 620 } 621 622 623 /** @} */ 624 625 #ifdef DBUS_BUILD_TESTS 626 627 dbus_bool_t 628 _dbus_threads_init_debug (void) 629 { 630 return _dbus_threads_init_platform_specific(); 631 } 632 633 #endif /* DBUS_BUILD_TESTS */ 634