1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-threads.h D-Bus threads handling
4 * Copyright (C) 2002, 2003 Red Hat Inc.
6 * Licensed under the Academic Free License version 2.1
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #include "dbus-threads.h"
24 #include "dbus-internals.h"
25 #include "dbus-threads-internal.h"
26 #include "dbus-list.h"
28 static DBusThreadFunctions thread_functions =
31 NULL, NULL, NULL, NULL,
32 NULL, NULL, NULL, NULL, NULL,
34 NULL, NULL, NULL, NULL,
35 NULL, NULL, NULL, NULL
37 static int thread_init_generation = 0;
39 static DBusList *uninitialized_mutex_list = NULL;
40 static DBusList *uninitialized_condvar_list = NULL;
42 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
43 #define _DBUS_DUMMY_MUTEX ((DBusMutex*)0xABCDEF)
45 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
46 #define _DBUS_DUMMY_CONDVAR ((DBusCondVar*)0xABCDEF2)
49 * @defgroup DBusThreadsInternals Thread functions
50 * @ingroup DBusInternals
51 * @brief _dbus_mutex_lock(), etc.
53 * Functions and macros related to threads and thread locks.
59 * Creates a new mutex using the function supplied to dbus_threads_init(),
60 * or creates a no-op mutex if threads are not initialized.
61 * May return #NULL even if threads are initialized, indicating
64 * @returns new mutex or #NULL
67 _dbus_mutex_new (void)
69 if (thread_functions.mutex_new)
70 return (* thread_functions.mutex_new) ();
72 return _DBUS_DUMMY_MUTEX;
76 * This does the same thing as _dbus_mutex_new. It however
77 * gives another level of indirection by allocating a pointer
78 * to point to the mutex location. This allows the threading
79 * module to swap out dummy mutexes for real a real mutex so libraries
80 * can initialize threads even after the D-Bus API has been used.
82 * @param location_p the location of the new mutex, can return #NULL on OOM
85 _dbus_mutex_new_at_location (DBusMutex **location_p)
87 _dbus_assert (location_p != NULL);
89 *location_p = _dbus_mutex_new();
91 if (thread_init_generation != _dbus_current_generation && *location_p)
93 if (!_dbus_list_append (&uninitialized_mutex_list, location_p))
95 _dbus_mutex_free (*location_p);
102 * Frees a mutex created with dbus_mutex_new(); does
103 * nothing if passed a #NULL pointer.
106 _dbus_mutex_free (DBusMutex *mutex)
108 if (mutex && thread_functions.mutex_free)
109 (* thread_functions.mutex_free) (mutex);
113 * Frees a mutex and removes it from the
114 * uninitialized_mutex_list;
115 * does nothing if passed a #NULL pointer.
118 _dbus_mutex_free_at_location (DBusMutex **location_p)
122 if (thread_init_generation != _dbus_current_generation)
123 _dbus_list_remove (&uninitialized_mutex_list, location_p);
125 _dbus_mutex_free (*location_p);
130 * Locks a mutex. Does nothing if passed a #NULL pointer.
131 * Locks are not recursive.
133 * @returns #TRUE on success
136 _dbus_mutex_lock (DBusMutex *mutex)
138 if (mutex && thread_functions.mutex_lock)
139 return (* thread_functions.mutex_lock) (mutex);
145 * Unlocks a mutex. Does nothing if passed a #NULL pointer.
147 * @returns #TRUE on success
150 _dbus_mutex_unlock (DBusMutex *mutex)
152 if (mutex && thread_functions.mutex_unlock)
153 return (* thread_functions.mutex_unlock) (mutex);
159 * Creates a new condition variable using the function supplied
160 * to dbus_threads_init(), or creates a no-op condition variable
161 * if threads are not initialized. May return #NULL even if
162 * threads are initialized, indicating out-of-memory.
164 * @returns new mutex or #NULL
167 _dbus_condvar_new (void)
169 if (thread_functions.condvar_new)
170 return (* thread_functions.condvar_new) ();
172 return _DBUS_DUMMY_CONDVAR;
177 * This does the same thing as _dbus_condvar_new. It however
178 * gives another level of indirection by allocating a pointer
179 * to point to the condvar location. This allows the threading
180 * module to swap out dummy condvars for real a real condvar so libraries
181 * can initialize threads even after the D-Bus API has been used.
183 * @returns the location of a new condvar or #NULL on OOM
187 _dbus_condvar_new_at_location (DBusCondVar **location_p)
189 *location_p = _dbus_condvar_new();
191 if (thread_init_generation != _dbus_current_generation && *location_p)
193 if (!_dbus_list_append (&uninitialized_condvar_list, location_p))
195 _dbus_condvar_free (*location_p);
203 * Frees a conditional variable created with dbus_condvar_new(); does
204 * nothing if passed a #NULL pointer.
207 _dbus_condvar_free (DBusCondVar *cond)
209 if (cond && thread_functions.condvar_free)
210 (* thread_functions.condvar_free) (cond);
214 * Frees a conditional variable and removes it from the
215 * uninitialized_condvar_list;
216 * does nothing if passed a #NULL pointer.
219 _dbus_condvar_free_at_location (DBusCondVar **location_p)
223 if (thread_init_generation != _dbus_current_generation)
224 _dbus_list_remove (&uninitialized_condvar_list, location_p);
226 _dbus_condvar_free (*location_p);
231 * Atomically unlocks the mutex and waits for the conditions
232 * variable to be signalled. Locks the mutex again before
234 * Does nothing if passed a #NULL pointer.
237 _dbus_condvar_wait (DBusCondVar *cond,
240 if (cond && mutex && thread_functions.condvar_wait)
241 (* thread_functions.condvar_wait) (cond, mutex);
245 * Atomically unlocks the mutex and waits for the conditions
246 * variable to be signalled, or for a timeout. Locks the
247 * mutex again before returning.
248 * Does nothing if passed a #NULL pointer.
250 * @param cond the condition variable
251 * @param mutex the mutex
252 * @param timeout_milliseconds the maximum time to wait
253 * @returns TRUE if the condition was reached, or FALSE if the
254 * timeout was reached.
257 _dbus_condvar_wait_timeout (DBusCondVar *cond,
259 int timeout_milliseconds)
261 if (cond && mutex && thread_functions.condvar_wait)
262 return (* thread_functions.condvar_wait_timeout) (cond, mutex, timeout_milliseconds);
268 * If there are threads waiting on the condition variable, wake
270 * Does nothing if passed a #NULL pointer.
273 _dbus_condvar_wake_one (DBusCondVar *cond)
275 if (cond && thread_functions.condvar_wake_one)
276 (* thread_functions.condvar_wake_one) (cond);
280 * If there are threads waiting on the condition variable, wake
282 * Does nothing if passed a #NULL pointer.
285 _dbus_condvar_wake_all (DBusCondVar *cond)
287 if (cond && thread_functions.condvar_wake_all)
288 (* thread_functions.condvar_wake_all) (cond);
292 shutdown_global_locks (void *data)
294 DBusMutex ***locks = data;
298 while (i < _DBUS_N_GLOBAL_LOCKS)
300 _dbus_mutex_free (*(locks[i]));
309 shutdown_uninitialized_locks (void *data)
311 _dbus_list_clear (&uninitialized_mutex_list);
312 _dbus_list_clear (&uninitialized_condvar_list);
316 init_uninitialized_locks (void)
320 _dbus_assert (thread_init_generation == 0);
322 link = uninitialized_mutex_list;
327 mp = (DBusMutex **)link->data;
328 _dbus_assert (*mp == _DBUS_DUMMY_MUTEX);
330 *mp = _dbus_mutex_new ();
334 link = _dbus_list_get_next_link (&uninitialized_mutex_list, link);
337 link = uninitialized_condvar_list;
342 cp = (DBusCondVar **)link->data;
343 _dbus_assert (*cp == _DBUS_DUMMY_CONDVAR);
345 *cp = _dbus_condvar_new ();
349 link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
352 _dbus_list_clear (&uninitialized_mutex_list);
353 _dbus_list_clear (&uninitialized_condvar_list);
355 if (!_dbus_register_shutdown_func (shutdown_uninitialized_locks,
362 link = uninitialized_condvar_list;
367 cp = (DBusCondVar **)link->data;
369 if (*cp != _DBUS_DUMMY_CONDVAR)
370 _dbus_condvar_free (*cp);
374 *cp = _DBUS_DUMMY_CONDVAR;
376 link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
380 link = uninitialized_mutex_list;
385 mp = (DBusMutex **)link->data;
387 if (*mp != _DBUS_DUMMY_MUTEX)
388 _dbus_mutex_free (*mp);
392 *mp = _DBUS_DUMMY_MUTEX;
394 link = _dbus_list_get_next_link (&uninitialized_mutex_list, link);
404 DBusMutex ***dynamic_global_locks;
406 DBusMutex **global_locks[] = {
407 #define LOCK_ADDR(name) (& _dbus_lock_##name)
409 LOCK_ADDR (sid_atom_cache),
411 LOCK_ADDR (connection_slots),
412 LOCK_ADDR (pending_call_slots),
413 LOCK_ADDR (server_slots),
414 LOCK_ADDR (message_slots),
417 LOCK_ADDR (shutdown_funcs),
418 LOCK_ADDR (system_users),
419 LOCK_ADDR (message_cache),
420 LOCK_ADDR (shared_connections)
424 _dbus_assert (_DBUS_N_ELEMENTS (global_locks) ==
425 _DBUS_N_GLOBAL_LOCKS);
429 dynamic_global_locks = dbus_new (DBusMutex**, _DBUS_N_GLOBAL_LOCKS);
430 if (dynamic_global_locks == NULL)
433 while (i < _DBUS_N_ELEMENTS (global_locks))
435 *global_locks[i] = _dbus_mutex_new ();
437 if (*global_locks[i] == NULL)
440 dynamic_global_locks[i] = global_locks[i];
445 if (!_dbus_register_shutdown_func (shutdown_global_locks,
446 dynamic_global_locks))
449 if (!init_uninitialized_locks ())
455 dbus_free (dynamic_global_locks);
457 for (i = i - 1; i >= 0; i--)
459 _dbus_mutex_free (*global_locks[i]);
460 *global_locks[i] = NULL;
465 /** @} */ /* end of internals */
468 * @defgroup DBusThreads Thread functions
470 * @brief dbus_threads_init()
472 * Functions and macros related to threads and thread locks.
479 * Initializes threads. If this function is not called,
480 * the D-Bus library will not lock any data structures.
481 * If it is called, D-Bus will do locking, at some cost
482 * in efficiency. Note that this function must be called
483 * BEFORE using any other D-Bus functions.
485 * This function may be called more than once, as long
486 * as you pass in the same functions each time. If it's
487 * called multiple times with different functions, then
488 * a warning is printed, because someone is confused.
490 * @param functions functions for using threads
491 * @returns #TRUE on success, #FALSE if no memory
494 dbus_threads_init (const DBusThreadFunctions *functions)
496 _dbus_assert (functions != NULL);
498 /* these base functions are required. Future additions to
499 * DBusThreadFunctions may be optional.
501 _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK);
502 _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK);
503 _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK);
504 _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK);
505 _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK);
506 _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK);
507 _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK);
508 _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK);
509 _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK);
510 _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK);
511 _dbus_assert (functions->mutex_new != NULL);
512 _dbus_assert (functions->mutex_free != NULL);
513 _dbus_assert (functions->mutex_lock != NULL);
514 _dbus_assert (functions->mutex_unlock != NULL);
515 _dbus_assert (functions->condvar_new != NULL);
516 _dbus_assert (functions->condvar_free != NULL);
517 _dbus_assert (functions->condvar_wait != NULL);
518 _dbus_assert (functions->condvar_wait_timeout != NULL);
519 _dbus_assert (functions->condvar_wake_one != NULL);
520 _dbus_assert (functions->condvar_wake_all != NULL);
522 /* Check that all bits in the mask actually are valid mask bits.
523 * ensures people won't write code that breaks when we add
526 _dbus_assert ((functions->mask & ~DBUS_THREAD_FUNCTIONS_ALL_MASK) == 0);
528 if (thread_init_generation != _dbus_current_generation)
529 thread_functions.mask = 0; /* allow re-init in new generation */
531 /* Silently allow multiple init
532 * First init wins and D-Bus will always use its threading system
534 if (thread_functions.mask != 0)
537 thread_functions.mutex_new = functions->mutex_new;
538 thread_functions.mutex_free = functions->mutex_free;
539 thread_functions.mutex_lock = functions->mutex_lock;
540 thread_functions.mutex_unlock = functions->mutex_unlock;
542 thread_functions.condvar_new = functions->condvar_new;
543 thread_functions.condvar_free = functions->condvar_free;
544 thread_functions.condvar_wait = functions->condvar_wait;
545 thread_functions.condvar_wait_timeout = functions->condvar_wait_timeout;
546 thread_functions.condvar_wake_one = functions->condvar_wake_one;
547 thread_functions.condvar_wake_all = functions->condvar_wake_all;
549 thread_functions.mask = functions->mask;
554 thread_init_generation = _dbus_current_generation;
561 #ifdef DBUS_BUILD_TESTS
562 /** Fake mutex used for debugging */
563 typedef struct DBusFakeMutex DBusFakeMutex;
564 /** Fake mutex used for debugging */
567 dbus_bool_t locked; /**< Mutex is "locked" */
570 static DBusMutex * dbus_fake_mutex_new (void);
571 static void dbus_fake_mutex_free (DBusMutex *mutex);
572 static dbus_bool_t dbus_fake_mutex_lock (DBusMutex *mutex);
573 static dbus_bool_t dbus_fake_mutex_unlock (DBusMutex *mutex);
574 static DBusCondVar* dbus_fake_condvar_new (void);
575 static void dbus_fake_condvar_free (DBusCondVar *cond);
576 static void dbus_fake_condvar_wait (DBusCondVar *cond,
578 static dbus_bool_t dbus_fake_condvar_wait_timeout (DBusCondVar *cond,
581 static void dbus_fake_condvar_wake_one (DBusCondVar *cond);
582 static void dbus_fake_condvar_wake_all (DBusCondVar *cond);
585 static const DBusThreadFunctions fake_functions =
587 DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK |
588 DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK |
589 DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK |
590 DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK |
591 DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK |
592 DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK |
593 DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK |
594 DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK |
595 DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK|
596 DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK,
598 dbus_fake_mutex_free,
599 dbus_fake_mutex_lock,
600 dbus_fake_mutex_unlock,
601 dbus_fake_condvar_new,
602 dbus_fake_condvar_free,
603 dbus_fake_condvar_wait,
604 dbus_fake_condvar_wait_timeout,
605 dbus_fake_condvar_wake_one,
606 dbus_fake_condvar_wake_all
610 dbus_fake_mutex_new (void)
612 DBusFakeMutex *mutex;
614 mutex = dbus_new0 (DBusFakeMutex, 1);
616 return (DBusMutex *)mutex;
620 dbus_fake_mutex_free (DBusMutex *mutex)
622 DBusFakeMutex *fake = (DBusFakeMutex*) mutex;
624 _dbus_assert (!fake->locked);
630 dbus_fake_mutex_lock (DBusMutex *mutex)
632 DBusFakeMutex *fake = (DBusFakeMutex*) mutex;
634 _dbus_assert (!fake->locked);
642 dbus_fake_mutex_unlock (DBusMutex *mutex)
644 DBusFakeMutex *fake = (DBusFakeMutex*) mutex;
646 _dbus_assert (fake->locked);
648 fake->locked = FALSE;
654 dbus_fake_condvar_new (void)
656 return (DBusCondVar*) _dbus_strdup ("FakeCondvar");
660 dbus_fake_condvar_free (DBusCondVar *cond)
666 dbus_fake_condvar_wait (DBusCondVar *cond,
673 dbus_fake_condvar_wait_timeout (DBusCondVar *cond,
681 dbus_fake_condvar_wake_one (DBusCondVar *cond)
687 dbus_fake_condvar_wake_all (DBusCondVar *cond)
693 _dbus_threads_init_debug (void)
695 return dbus_threads_init (&fake_functions);
698 #endif /* DBUS_BUILD_TESTS */