Patch provided by Ralf Habacker (ralf dot habacker at freenet dot de)
[platform/upstream/dbus.git] / dbus / dbus-threads.c
1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-threads.h  D-Bus threads handling
3  *
4  * Copyright (C) 2002, 2003 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 #include "dbus-threads.h"
24 #include "dbus-internals.h"
25 #include "dbus-threads-internal.h"
26 #include "dbus-list.h"
27
28 static DBusThreadFunctions thread_functions =
29 {
30   0,
31   NULL, NULL, NULL, NULL,
32   NULL, NULL, NULL, NULL, NULL,
33
34   NULL, NULL, NULL, NULL,
35   NULL, NULL, NULL, NULL
36 };
37 static int thread_init_generation = 0;
38
39 static DBusList *uninitialized_mutex_list = NULL;
40 static DBusList *uninitialized_condvar_list = NULL;
41
42 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
43 #define _DBUS_DUMMY_MUTEX ((DBusMutex*)0xABCDEF)
44
45 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
46 #define _DBUS_DUMMY_CONDVAR ((DBusCondVar*)0xABCDEF2)
47
48 /**
49  * @defgroup DBusThreadsInternals Thread functions
50  * @ingroup  DBusInternals
51  * @brief _dbus_mutex_lock(), etc.
52  *
53  * Functions and macros related to threads and thread locks.
54  *
55  * @{
56  */
57
58 /**
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
62  * out-of-memory.
63  *
64  * @returns new mutex or #NULL
65  */
66 DBusMutex*
67 _dbus_mutex_new (void)
68 {
69   if (thread_functions.mutex_new)
70     return (* thread_functions.mutex_new) ();
71   else
72     return _DBUS_DUMMY_MUTEX;
73 }
74
75 /**
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.
81  *
82  * @param location_p the location of the new mutex, can return #NULL on OOM
83  */
84 void
85 _dbus_mutex_new_at_location (DBusMutex **location_p)
86 {
87   _dbus_assert (location_p != NULL);
88
89   *location_p = _dbus_mutex_new();
90
91   if (thread_init_generation != _dbus_current_generation && *location_p)
92     {
93       if (!_dbus_list_append (&uninitialized_mutex_list, location_p))
94         {
95           _dbus_mutex_free (*location_p);
96           *location_p = NULL;
97         }
98     }
99 }
100
101 /**
102  * Frees a mutex created with dbus_mutex_new(); does
103  * nothing if passed a #NULL pointer.
104  */
105 void
106 _dbus_mutex_free (DBusMutex *mutex)
107 {
108   if (mutex && thread_functions.mutex_free)
109     (* thread_functions.mutex_free) (mutex);
110 }
111
112 /**
113  * Frees a mutex and removes it from the 
114  * uninitialized_mutex_list;
115  * does nothing if passed a #NULL pointer.
116  */
117 void
118 _dbus_mutex_free_at_location (DBusMutex **location_p)
119 {
120   if (location_p)
121     {
122       if (thread_init_generation != _dbus_current_generation)
123         _dbus_list_remove (&uninitialized_mutex_list, location_p);
124
125       _dbus_mutex_free (*location_p);
126     }
127 }
128
129 /**
130  * Locks a mutex. Does nothing if passed a #NULL pointer.
131  * Locks are not recursive.
132  *
133  * @returns #TRUE on success
134  */
135 dbus_bool_t
136 _dbus_mutex_lock (DBusMutex *mutex)
137 {
138   if (mutex && thread_functions.mutex_lock)
139     return (* thread_functions.mutex_lock) (mutex);
140   else
141     return TRUE;
142 }
143
144 /**
145  * Unlocks a mutex. Does nothing if passed a #NULL pointer.
146  *
147  * @returns #TRUE on success
148  */
149 dbus_bool_t
150 _dbus_mutex_unlock (DBusMutex *mutex)
151 {
152   if (mutex && thread_functions.mutex_unlock)
153     return (* thread_functions.mutex_unlock) (mutex);
154   else
155     return TRUE;
156 }
157
158 /**
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.
163  *
164  * @returns new mutex or #NULL
165  */
166 DBusCondVar *
167 _dbus_condvar_new (void)
168 {
169   if (thread_functions.condvar_new)
170     return (* thread_functions.condvar_new) ();
171   else
172     return _DBUS_DUMMY_CONDVAR;
173 }
174
175
176 /**
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.
182  *
183  * @returns the location of a new condvar or #NULL on OOM
184  */
185
186 void 
187 _dbus_condvar_new_at_location (DBusCondVar **location_p)
188 {
189   *location_p = _dbus_condvar_new();
190
191   if (thread_init_generation != _dbus_current_generation && *location_p)
192     {
193       if (!_dbus_list_append (&uninitialized_condvar_list, location_p))
194         {
195           _dbus_condvar_free (*location_p);
196           *location_p = NULL;
197         }
198     }
199 }
200
201
202 /**
203  * Frees a conditional variable created with dbus_condvar_new(); does
204  * nothing if passed a #NULL pointer.
205  */
206 void
207 _dbus_condvar_free (DBusCondVar *cond)
208 {
209   if (cond && thread_functions.condvar_free)
210     (* thread_functions.condvar_free) (cond);
211 }
212
213 /**
214  * Frees a conditional variable and removes it from the 
215  * uninitialized_condvar_list; 
216  * does nothing if passed a #NULL pointer.
217  */
218 void
219 _dbus_condvar_free_at_location (DBusCondVar **location_p)
220 {
221   if (location_p)
222     {
223       if (thread_init_generation != _dbus_current_generation)
224         _dbus_list_remove (&uninitialized_condvar_list, location_p);
225
226       _dbus_condvar_free (*location_p);
227     }
228 }
229
230 /**
231  * Atomically unlocks the mutex and waits for the conditions
232  * variable to be signalled. Locks the mutex again before
233  * returning.
234  * Does nothing if passed a #NULL pointer.
235  */
236 void
237 _dbus_condvar_wait (DBusCondVar *cond,
238                     DBusMutex   *mutex)
239 {
240   if (cond && mutex && thread_functions.condvar_wait)
241     (* thread_functions.condvar_wait) (cond, mutex);
242 }
243
244 /**
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.
249  *
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.
255  */
256 dbus_bool_t
257 _dbus_condvar_wait_timeout (DBusCondVar               *cond,
258                             DBusMutex                 *mutex,
259                             int                        timeout_milliseconds)
260 {
261   if (cond && mutex && thread_functions.condvar_wait)
262     return (* thread_functions.condvar_wait_timeout) (cond, mutex, timeout_milliseconds);
263   else
264     return TRUE;
265 }
266
267 /**
268  * If there are threads waiting on the condition variable, wake
269  * up exactly one. 
270  * Does nothing if passed a #NULL pointer.
271  */
272 void
273 _dbus_condvar_wake_one (DBusCondVar *cond)
274 {
275   if (cond && thread_functions.condvar_wake_one)
276     (* thread_functions.condvar_wake_one) (cond);
277 }
278
279 /**
280  * If there are threads waiting on the condition variable, wake
281  * up all of them. 
282  * Does nothing if passed a #NULL pointer.
283  */
284 void
285 _dbus_condvar_wake_all (DBusCondVar *cond)
286 {
287   if (cond && thread_functions.condvar_wake_all)
288     (* thread_functions.condvar_wake_all) (cond);
289 }
290
291 static void
292 shutdown_global_locks (void *data)
293 {
294   DBusMutex ***locks = data;
295   int i;
296
297   i = 0;
298   while (i < _DBUS_N_GLOBAL_LOCKS)
299     {
300       _dbus_mutex_free (*(locks[i]));
301       *(locks[i]) = NULL;
302       ++i;
303     }
304   
305   dbus_free (locks);
306 }
307
308 static void
309 shutdown_uninitialized_locks (void *data)
310 {
311   _dbus_list_clear (&uninitialized_mutex_list);
312   _dbus_list_clear (&uninitialized_condvar_list);
313 }
314
315 static dbus_bool_t
316 init_uninitialized_locks (void)
317 {
318   DBusList *link;
319
320   _dbus_assert (thread_init_generation == 0);
321
322   link = uninitialized_mutex_list;
323   while (link != NULL)
324     {
325       DBusMutex **mp;
326
327       mp = (DBusMutex **)link->data;
328       _dbus_assert (*mp == _DBUS_DUMMY_MUTEX);
329
330       *mp = _dbus_mutex_new ();
331       if (*mp == NULL)
332         goto fail_mutex;
333
334       link = _dbus_list_get_next_link (&uninitialized_mutex_list, link);
335     }
336
337   link = uninitialized_condvar_list;
338   while (link != NULL)
339     {
340       DBusCondVar **cp;
341
342       cp = (DBusCondVar **)link->data;
343       _dbus_assert (*cp == _DBUS_DUMMY_CONDVAR);
344
345       *cp = _dbus_condvar_new ();
346       if (*cp == NULL)
347         goto fail_condvar;
348
349       link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
350     }
351
352   _dbus_list_clear (&uninitialized_mutex_list);
353   _dbus_list_clear (&uninitialized_condvar_list);
354
355   if (!_dbus_register_shutdown_func (shutdown_uninitialized_locks,
356                                      NULL))
357     goto fail_condvar;
358
359   return TRUE;
360
361  fail_condvar:
362   link = uninitialized_condvar_list;
363   while (link != NULL)
364     {
365       DBusCondVar **cp;
366
367       cp = (DBusCondVar **)link->data;
368
369       if (*cp != _DBUS_DUMMY_CONDVAR)
370         _dbus_condvar_free (*cp);
371       else
372         break;
373
374       *cp = _DBUS_DUMMY_CONDVAR;
375
376       link = _dbus_list_get_next_link (&uninitialized_condvar_list, link);
377     }
378
379  fail_mutex:
380   link = uninitialized_mutex_list;
381   while (link != NULL)
382     {
383       DBusMutex **mp;
384
385       mp = (DBusMutex **)link->data;
386
387       if (*mp != _DBUS_DUMMY_MUTEX)
388         _dbus_mutex_free (*mp);
389       else
390         break;
391
392       *mp = _DBUS_DUMMY_MUTEX;
393
394       link = _dbus_list_get_next_link (&uninitialized_mutex_list, link);
395     }
396
397   return FALSE;
398 }
399
400 static dbus_bool_t
401 init_locks (void)
402 {
403   int i;
404   DBusMutex ***dynamic_global_locks;
405   
406   DBusMutex **global_locks[] = {
407 #define LOCK_ADDR(name) (& _dbus_lock_##name)
408     LOCK_ADDR (win_fds),
409     LOCK_ADDR (sid_atom_cache),
410     LOCK_ADDR (list),
411     LOCK_ADDR (connection_slots),
412     LOCK_ADDR (pending_call_slots),
413     LOCK_ADDR (server_slots),
414     LOCK_ADDR (message_slots),
415     LOCK_ADDR (atomic),
416     LOCK_ADDR (bus),
417     LOCK_ADDR (shutdown_funcs),
418     LOCK_ADDR (system_users),
419     LOCK_ADDR (message_cache),
420     LOCK_ADDR (shared_connections)
421 #undef LOCK_ADDR
422   };
423
424   _dbus_assert (_DBUS_N_ELEMENTS (global_locks) ==
425                 _DBUS_N_GLOBAL_LOCKS);
426
427   i = 0;
428   
429   dynamic_global_locks = dbus_new (DBusMutex**, _DBUS_N_GLOBAL_LOCKS);
430   if (dynamic_global_locks == NULL)
431     goto failed;
432   
433   while (i < _DBUS_N_ELEMENTS (global_locks))
434     {
435       *global_locks[i] = _dbus_mutex_new ();
436       
437       if (*global_locks[i] == NULL)
438         goto failed;
439
440       dynamic_global_locks[i] = global_locks[i];
441
442       ++i;
443     }
444   
445   if (!_dbus_register_shutdown_func (shutdown_global_locks,
446                                      dynamic_global_locks))
447     goto failed;
448
449   if (!init_uninitialized_locks ())
450     goto failed;
451   
452   return TRUE;
453
454  failed:
455   dbus_free (dynamic_global_locks);
456                                      
457   for (i = i - 1; i >= 0; i--)
458     {
459       _dbus_mutex_free (*global_locks[i]);
460       *global_locks[i] = NULL;
461     }
462   return FALSE;
463 }
464
465 /** @} */ /* end of internals */
466
467 /**
468  * @defgroup DBusThreads Thread functions
469  * @ingroup  DBus
470  * @brief dbus_threads_init()
471  *
472  * Functions and macros related to threads and thread locks.
473  *
474  * @{
475  */
476
477 /**
478  * 
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.
484  *
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.
489  *
490  * @param functions functions for using threads
491  * @returns #TRUE on success, #FALSE if no memory
492  */
493 dbus_bool_t
494 dbus_threads_init (const DBusThreadFunctions *functions)
495 {
496   _dbus_assert (functions != NULL);
497
498   /* these base functions are required. Future additions to
499    * DBusThreadFunctions may be optional.
500    */
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);
521
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
524    * new bits.
525    */
526   _dbus_assert ((functions->mask & ~DBUS_THREAD_FUNCTIONS_ALL_MASK) == 0);
527
528   if (thread_init_generation != _dbus_current_generation)
529     thread_functions.mask = 0; /* allow re-init in new generation */
530  
531   /* Silently allow multiple init
532    * First init wins and D-Bus will always use its threading system 
533    */ 
534   if (thread_functions.mask != 0)
535     return TRUE;
536   
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;
541   
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;
548   
549   thread_functions.mask = functions->mask;
550
551   if (!init_locks ())
552     return FALSE;
553
554   thread_init_generation = _dbus_current_generation;
555   
556   return TRUE;
557 }
558
559 /** @} */
560
561 #ifdef DBUS_BUILD_TESTS
562 /** Fake mutex used for debugging */
563 typedef struct DBusFakeMutex DBusFakeMutex;
564 /** Fake mutex used for debugging */
565 struct DBusFakeMutex
566 {
567   dbus_bool_t locked; /**< Mutex is "locked" */
568 };      
569
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,
577                                                     DBusMutex   *mutex);
578 static dbus_bool_t  dbus_fake_condvar_wait_timeout (DBusCondVar *cond,
579                                                     DBusMutex   *mutex,
580                                                     int          timeout_msec);
581 static void         dbus_fake_condvar_wake_one     (DBusCondVar *cond);
582 static void         dbus_fake_condvar_wake_all     (DBusCondVar *cond);
583
584
585 static const DBusThreadFunctions fake_functions =
586 {
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,
597   dbus_fake_mutex_new,
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
607 };
608
609 static DBusMutex *
610 dbus_fake_mutex_new (void)
611 {
612   DBusFakeMutex *mutex;
613
614   mutex = dbus_new0 (DBusFakeMutex, 1);
615
616   return (DBusMutex *)mutex;
617 }
618
619 static void
620 dbus_fake_mutex_free (DBusMutex *mutex)
621 {
622   DBusFakeMutex *fake = (DBusFakeMutex*) mutex;
623
624   _dbus_assert (!fake->locked);
625   
626   dbus_free (fake);
627 }
628
629 static dbus_bool_t
630 dbus_fake_mutex_lock (DBusMutex *mutex)
631 {
632   DBusFakeMutex *fake = (DBusFakeMutex*) mutex;
633
634   _dbus_assert (!fake->locked);
635
636   fake->locked = TRUE;
637   
638   return TRUE;
639 }
640
641 static dbus_bool_t
642 dbus_fake_mutex_unlock (DBusMutex *mutex)
643 {
644   DBusFakeMutex *fake = (DBusFakeMutex*) mutex;
645
646   _dbus_assert (fake->locked);
647
648   fake->locked = FALSE;
649   
650   return TRUE;
651 }
652
653 static DBusCondVar*
654 dbus_fake_condvar_new (void)
655 {
656   return (DBusCondVar*) _dbus_strdup ("FakeCondvar");
657 }
658
659 static void
660 dbus_fake_condvar_free (DBusCondVar *cond)
661 {
662   dbus_free (cond);
663 }
664
665 static void
666 dbus_fake_condvar_wait (DBusCondVar *cond,
667                         DBusMutex   *mutex)
668 {
669   
670 }
671
672 static dbus_bool_t
673 dbus_fake_condvar_wait_timeout (DBusCondVar *cond,
674                                 DBusMutex   *mutex,
675                                 int         timeout_msec)
676 {
677   return TRUE;
678 }
679
680 static void
681 dbus_fake_condvar_wake_one (DBusCondVar *cond)
682 {
683
684 }
685
686 static void
687 dbus_fake_condvar_wake_all (DBusCondVar *cond)
688 {
689
690 }
691
692 dbus_bool_t
693 _dbus_threads_init_debug (void)
694 {
695   return dbus_threads_init (&fake_functions);
696 }
697
698 #endif /* DBUS_BUILD_TESTS */