2c2a81669b4123ea5ba31ad9e99a24088418eb5b
[platform/upstream/dbus.git] / dbus / dbus-threads.c
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 DBusRMutex *global_locks[_DBUS_N_GLOBAL_LOCKS] = { NULL };
347
348 static void
349 shutdown_global_locks (void *nil)
350 {
351   int i;
352
353   for (i = 0; i < _DBUS_N_GLOBAL_LOCKS; i++)
354     {
355       _dbus_assert (global_locks[i] != NULL);
356       _dbus_platform_rmutex_free (global_locks[i]);
357       global_locks[i] = NULL;
358     }
359 }
360
361 static void
362 shutdown_uninitialized_locks (void *data)
363 {
364   _dbus_list_clear (&uninitialized_rmutex_list);
365   _dbus_list_clear (&uninitialized_cmutex_list);
366   _dbus_list_clear (&uninitialized_condvar_list);
367 }
368
369 /* init_global_locks() must be called first. */
370 static dbus_bool_t
371 init_uninitialized_locks (void)
372 {
373   DBusList *link;
374   dbus_bool_t ok;
375
376   _dbus_assert (thread_init_generation != _dbus_current_generation);
377
378   link = uninitialized_rmutex_list;
379   while (link != NULL)
380     {
381       DBusRMutex **mp;
382
383       mp = link->data;
384       _dbus_assert (*mp == _DBUS_DUMMY_RMUTEX);
385
386       *mp = _dbus_platform_rmutex_new ();
387       if (*mp == NULL)
388         goto fail_mutex;
389
390       link = _dbus_list_get_next_link (&uninitialized_rmutex_list, link);
391     }
392
393   link = uninitialized_cmutex_list;
394   while (link != NULL)
395     {
396       DBusCMutex **mp;
397
398       mp = link->data;
399       _dbus_assert (*mp == _DBUS_DUMMY_CMUTEX);
400
401       *mp = _dbus_platform_cmutex_new ();
402       if (*mp == NULL)
403         goto fail_mutex;
404
405       link = _dbus_list_get_next_link (&uninitialized_cmutex_list, link);
406     }
407
408   link = uninitialized_condvar_list;
409   while (link != NULL)
410     {
411       DBusCondVar **cp;
412
413       cp = (DBusCondVar **)link->data;
414       _dbus_assert (*cp == _DBUS_DUMMY_CONDVAR);
415
416       *cp = _dbus_platform_condvar_new ();
417       if (*cp == NULL)
418         goto fail_condvar;
419
420       link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
421     }
422
423   _dbus_list_clear (&uninitialized_rmutex_list);
424   _dbus_list_clear (&uninitialized_cmutex_list);
425   _dbus_list_clear (&uninitialized_condvar_list);
426
427   /* This assumes that init_global_locks() has already been called. */
428   _dbus_platform_rmutex_lock (global_locks[_DBUS_LOCK_shutdown_funcs]);
429   ok = _dbus_register_shutdown_func_unlocked (shutdown_uninitialized_locks, NULL);
430   _dbus_platform_rmutex_unlock (global_locks[_DBUS_LOCK_shutdown_funcs]);
431
432   if (!ok)
433     goto fail_condvar;
434
435   return TRUE;
436
437  fail_condvar:
438   link = uninitialized_condvar_list;
439   while (link != NULL)
440     {
441       DBusCondVar **cp;
442
443       cp = link->data;
444
445       if (*cp != _DBUS_DUMMY_CONDVAR && *cp != NULL)
446         _dbus_platform_condvar_free (*cp);
447
448       *cp = _DBUS_DUMMY_CONDVAR;
449
450       link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
451     }
452
453  fail_mutex:
454   link = uninitialized_rmutex_list;
455   while (link != NULL)
456     {
457       DBusRMutex **mp;
458
459       mp = link->data;
460
461       if (*mp != _DBUS_DUMMY_RMUTEX && *mp != NULL)
462         _dbus_platform_rmutex_free (*mp);
463
464       *mp = _DBUS_DUMMY_RMUTEX;
465
466       link = _dbus_list_get_next_link (&uninitialized_rmutex_list, link);
467     }
468
469   link = uninitialized_cmutex_list;
470   while (link != NULL)
471     {
472       DBusCMutex **mp;
473
474       mp = link->data;
475
476       if (*mp != _DBUS_DUMMY_CMUTEX && *mp != NULL)
477         _dbus_platform_cmutex_free (*mp);
478
479       *mp = _DBUS_DUMMY_CMUTEX;
480
481       link = _dbus_list_get_next_link (&uninitialized_cmutex_list, link);
482     }
483
484   return FALSE;
485 }
486
487 static dbus_bool_t
488 init_global_locks (void)
489 {
490   int i;
491   dbus_bool_t ok;
492
493   for (i = 0; i < _DBUS_N_GLOBAL_LOCKS; i++)
494     {
495       _dbus_assert (global_locks[i] == NULL);
496
497       global_locks[i] = _dbus_platform_rmutex_new ();
498
499       if (global_locks[i] == NULL)
500         goto failed;
501     }
502
503   _dbus_platform_rmutex_lock (global_locks[_DBUS_LOCK_shutdown_funcs]);
504   ok = _dbus_register_shutdown_func_unlocked (shutdown_global_locks, NULL);
505   _dbus_platform_rmutex_unlock (global_locks[_DBUS_LOCK_shutdown_funcs]);
506
507   if (!ok)
508     goto failed;
509
510   return TRUE;
511
512  failed:
513   for (i = i - 1; i >= 0; i--)
514     {
515       _dbus_platform_rmutex_free (global_locks[i]);
516       global_locks[i] = NULL;
517     }
518
519   return FALSE;
520 }
521
522 dbus_bool_t
523 _dbus_lock (DBusGlobalLock lock)
524 {
525   _dbus_assert (lock >= 0);
526   _dbus_assert (lock < _DBUS_N_GLOBAL_LOCKS);
527
528   if (thread_init_generation != _dbus_current_generation &&
529       !dbus_threads_init_default ())
530     return FALSE;
531
532   _dbus_platform_rmutex_lock (global_locks[lock]);
533   return TRUE;
534 }
535
536 void
537 _dbus_unlock (DBusGlobalLock lock)
538 {
539   _dbus_assert (lock >= 0);
540   _dbus_assert (lock < _DBUS_N_GLOBAL_LOCKS);
541
542   _dbus_platform_rmutex_unlock (global_locks[lock]);
543 }
544
545 /** @} */ /* end of internals */
546
547 /**
548  * @defgroup DBusThreads Thread functions
549  * @ingroup  DBus
550  * @brief dbus_threads_init() and dbus_threads_init_default()
551  *
552  * Functions and macros related to threads and thread locks.
553  *
554  * If threads are initialized, the D-Bus library has locks on all
555  * global data structures.  In addition, each #DBusConnection has a
556  * lock, so only one thread at a time can touch the connection.  (See
557  * @ref DBusConnection for more on connection locking.)
558  *
559  * Most other objects, however, do not have locks - they can only be
560  * used from a single thread at a time, unless you lock them yourself.
561  * For example, a #DBusMessage can't be modified from two threads
562  * at once.
563  * 
564  * @{
565  */
566
567 /**
568  * Initializes threads, like dbus_threads_init_default().
569  * This version previously allowed user-specified threading
570  * primitives, but since D-Bus 1.6 it ignores them and behaves
571  * exactly like dbus_threads_init_default().
572  *
573  * @param functions ignored, formerly functions for using threads
574  * @returns #TRUE on success, #FALSE if no memory
575  */
576 dbus_bool_t
577 dbus_threads_init (const DBusThreadFunctions *functions)
578 {
579   _dbus_threads_lock_platform_specific ();
580
581   if (thread_init_generation == _dbus_current_generation)
582     {
583       _dbus_threads_unlock_platform_specific ();
584       return TRUE;
585     }
586
587   if (!_dbus_threads_init_platform_specific() ||
588       /* init_global_locks() must be called before init_uninitialized_locks. */
589       !init_global_locks () ||
590       !init_uninitialized_locks ())
591     {
592       _dbus_threads_unlock_platform_specific ();
593       return FALSE;
594     }
595
596   thread_init_generation = _dbus_current_generation;
597
598   _dbus_threads_unlock_platform_specific ();
599   return TRUE;
600 }
601
602
603
604 /* Default thread implemenation */
605
606 /**
607  * Initializes threads. If this function is not called, the D-Bus
608  * library will not lock any data structures.  If it is called, D-Bus
609  * will do locking, at some cost in efficiency.
610  *
611  * Since D-Bus 1.7 it is safe to call this function from any thread,
612  * any number of times (but it must be called before any other
613  * libdbus API is used).
614  *
615  * In D-Bus 1.6 or older, this function must be called in the main thread
616  * before any other thread starts. As a result, it is not sufficient to
617  * call this function in a library or plugin, unless the library or plugin
618  * imposes a similar requirement on its callers.
619  *
620  * dbus_shutdown() reverses the effects of this function when it
621  * resets all global state in libdbus.
622  * 
623  * @returns #TRUE on success, #FALSE if not enough memory
624  */
625 dbus_bool_t
626 dbus_threads_init_default (void)
627 {
628   return dbus_threads_init (NULL);
629 }
630
631
632 /** @} */
633
634 #ifdef DBUS_BUILD_TESTS
635
636 dbus_bool_t
637 _dbus_threads_init_debug (void)
638 {
639   return dbus_threads_init (NULL);
640 }
641
642 #endif /* DBUS_BUILD_TESTS */