Remove unused global mutexes for win_fds, sid_atom_cache
[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 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 (list),
493     LOCK_ADDR (connection_slots),
494     LOCK_ADDR (pending_call_slots),
495     LOCK_ADDR (server_slots),
496     LOCK_ADDR (message_slots),
497     LOCK_ADDR (bus),
498     LOCK_ADDR (bus_datas),
499     LOCK_ADDR (shutdown_funcs),
500     LOCK_ADDR (system_users),
501     LOCK_ADDR (message_cache),
502     LOCK_ADDR (shared_connections),
503     LOCK_ADDR (machine_uuid)
504 #undef LOCK_ADDR
505   };
506
507   _DBUS_STATIC_ASSERT (_DBUS_N_ELEMENTS (global_locks) == _DBUS_N_GLOBAL_LOCKS);
508
509   i = 0;
510   
511   dynamic_global_locks = dbus_new (DBusRMutex**, _DBUS_N_GLOBAL_LOCKS);
512   if (dynamic_global_locks == NULL)
513     goto failed;
514   
515   while (i < _DBUS_N_ELEMENTS (global_locks))
516     {
517       *global_locks[i] = _dbus_platform_rmutex_new ();
518
519       if (*global_locks[i] == NULL)
520         goto failed;
521
522       dynamic_global_locks[i] = global_locks[i];
523
524       ++i;
525     }
526   
527   if (!_dbus_register_shutdown_func (shutdown_global_locks,
528                                      dynamic_global_locks))
529     goto failed;
530
531   if (!init_uninitialized_locks ())
532     goto failed;
533   
534   return TRUE;
535
536  failed:
537   dbus_free (dynamic_global_locks);
538                                      
539   for (i = i - 1; i >= 0; i--)
540     {
541       _dbus_platform_rmutex_free (*global_locks[i]);
542       *global_locks[i] = NULL;
543     }
544   return FALSE;
545 }
546
547 /** @} */ /* end of internals */
548
549 /**
550  * @defgroup DBusThreads Thread functions
551  * @ingroup  DBus
552  * @brief dbus_threads_init() and dbus_threads_init_default()
553  *
554  * Functions and macros related to threads and thread locks.
555  *
556  * If threads are initialized, the D-Bus library has locks on all
557  * global data structures.  In addition, each #DBusConnection has a
558  * lock, so only one thread at a time can touch the connection.  (See
559  * @ref DBusConnection for more on connection locking.)
560  *
561  * Most other objects, however, do not have locks - they can only be
562  * used from a single thread at a time, unless you lock them yourself.
563  * For example, a #DBusMessage can't be modified from two threads
564  * at once.
565  * 
566  * @{
567  */
568
569 /**
570  * Initializes threads, like dbus_threads_init_default().
571  * This version previously allowed user-specified threading
572  * primitives, but since D-Bus 1.6 it ignores them and behaves
573  * exactly like dbus_threads_init_default().
574  *
575  * @param functions ignored, formerly functions for using threads
576  * @returns #TRUE on success, #FALSE if no memory
577  */
578 dbus_bool_t
579 dbus_threads_init (const DBusThreadFunctions *functions)
580 {
581   _dbus_threads_lock_platform_specific ();
582
583   if (thread_init_generation == _dbus_current_generation)
584     {
585       _dbus_threads_unlock_platform_specific ();
586       return TRUE;
587     }
588
589   if (!_dbus_threads_init_platform_specific() ||
590       !init_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 */