2003-03-30 Havoc Pennington <hp@pobox.com>
[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  Red Hat Inc.
5  *
6  * Licensed under the Academic Free License version 1.2
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
26 static DBusThreadFunctions thread_functions =
27 {
28   0,
29   NULL, NULL, NULL, NULL,
30   NULL, NULL, NULL, NULL, NULL,
31
32   NULL, NULL, NULL, NULL,
33   NULL, NULL, NULL, NULL
34 };
35 static int thread_init_generation = 0;
36
37 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
38 #ifdef DBUS_BUILD_TESTS
39 #define _DBUS_DUMMY_MUTEX_NEW ((DBusMutex*)_dbus_strdup ("FakeMutex"))
40 #else
41 #define _DBUS_DUMMY_MUTEX_NEW ((DBusMutex*)0xABCDEF)
42 #endif
43
44 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
45 #ifdef DBUS_BUILD_TESTS
46 #define _DBUS_DUMMY_CONDVAR_NEW ((DBusCondVar*)_dbus_strdup ("FakeCondvar"))
47 #else
48 #define _DBUS_DUMMY_CONDVAR_NEW ((DBusCondVar*)0xABCDEF2)
49 #endif
50
51 /**
52  * @defgroup DBusThreads Thread functions
53  * @ingroup  DBus
54  * @brief dbus_threads_init(), dbus_mutex_lock(), etc.
55  *
56  * Functions and macros related to threads and thread locks.
57  *
58  * @{
59  */
60
61 /**
62  * Creates a new mutex using the function supplied to dbus_threads_init(),
63  * or creates a no-op mutex if threads are not initialized.
64  * May return #NULL even if threads are initialized, indicating
65  * out-of-memory.
66  *
67  * @returns new mutex or #NULL
68  */
69 DBusMutex*
70 dbus_mutex_new (void)
71 {
72   if (thread_functions.mutex_new)
73     return (* thread_functions.mutex_new) ();
74   else
75     return _DBUS_DUMMY_MUTEX_NEW;
76 }
77
78 /**
79  * Frees a mutex created with dbus_mutex_new(); does
80  * nothing if passed a #NULL pointer.
81  */
82 void
83 dbus_mutex_free (DBusMutex *mutex)
84 {
85   if (mutex && thread_functions.mutex_free)
86     (* thread_functions.mutex_free) (mutex);
87 #ifdef DBUS_BUILD_TESTS
88   /* Free the fake mutex */
89   else
90     dbus_free (mutex);
91 #endif
92 }
93
94 /**
95  * Locks a mutex. Does nothing if passed a #NULL pointer.
96  * Locks are not recursive.
97  *
98  * @returns #TRUE on success
99  */
100 dbus_bool_t
101 dbus_mutex_lock (DBusMutex *mutex)
102 {
103   if (mutex && thread_functions.mutex_lock)
104     return (* thread_functions.mutex_lock) (mutex);
105   else
106     return TRUE;
107 }
108
109 /**
110  * Unlocks a mutex. Does nothing if passed a #NULL pointer.
111  *
112  * @returns #TRUE on success
113  */
114 dbus_bool_t
115 dbus_mutex_unlock (DBusMutex *mutex)
116 {
117   if (mutex && thread_functions.mutex_unlock)
118     return (* thread_functions.mutex_unlock) (mutex);
119   else
120     return TRUE;
121 }
122
123 /**
124  * Creates a new condition variable using the function supplied
125  * to dbus_threads_init(), or creates a no-op condition variable
126  * if threads are not initialized. May return #NULL even if
127  * threads are initialized, indicating out-of-memory.
128  *
129  * @returns new mutex or #NULL
130  */
131 DBusCondVar *
132 dbus_condvar_new (void)
133 {
134   if (thread_functions.condvar_new)
135     return (* thread_functions.condvar_new) ();
136   else
137     return _DBUS_DUMMY_CONDVAR_NEW;
138 }
139
140 /**
141  * Frees a conditional variable created with dbus_condvar_new(); does
142  * nothing if passed a #NULL pointer.
143  */
144 void
145 dbus_condvar_free (DBusCondVar *cond)
146 {
147   if (cond && thread_functions.condvar_free)
148     (* thread_functions.condvar_free) (cond);
149 #ifdef DBUS_BUILD_TESTS
150   else
151     /* Free the fake condvar */
152     dbus_free (cond);
153 #endif
154 }
155
156 /**
157  * Atomically unlocks the mutex and waits for the conditions
158  * variable to be signalled. Locks the mutex again before
159  * returning.
160  * Does nothing if passed a #NULL pointer.
161  */
162 void
163 dbus_condvar_wait (DBusCondVar *cond,
164                    DBusMutex   *mutex)
165 {
166   if (cond && mutex && thread_functions.condvar_wait)
167     (* thread_functions.condvar_wait) (cond, mutex);
168 }
169
170 /**
171  * Atomically unlocks the mutex and waits for the conditions
172  * variable to be signalled, or for a timeout. Locks the
173  * mutex again before returning.
174  * Does nothing if passed a #NULL pointer.
175  *
176  * @param cond the condition variable
177  * @param mutex the mutex
178  * @param timeout_milliseconds the maximum time to wait
179  * @returns TRUE if the condition was reached, or FALSE if the
180  * timeout was reached.
181  */
182 dbus_bool_t
183 dbus_condvar_wait_timeout (DBusCondVar               *cond,
184                            DBusMutex                 *mutex,
185                            int                        timeout_milliseconds)
186 {
187   if (cond && mutex && thread_functions.condvar_wait)
188     return (* thread_functions.condvar_wait_timeout) (cond, mutex, timeout_milliseconds);
189   else
190     return TRUE;
191 }
192
193 /**
194  * If there are threads waiting on the condition variable, wake
195  * up exactly one. 
196  * Does nothing if passed a #NULL pointer.
197  */
198 void
199 dbus_condvar_wake_one (DBusCondVar *cond)
200 {
201   if (cond && thread_functions.condvar_wake_one)
202     (* thread_functions.condvar_wake_one) (cond);
203 }
204
205 /**
206  * If there are threads waiting on the condition variable, wake
207  * up all of them. 
208  * Does nothing if passed a #NULL pointer.
209  */
210 void
211 dbus_condvar_wake_all (DBusCondVar *cond)
212 {
213   if (cond && thread_functions.condvar_wake_all)
214     (* thread_functions.condvar_wake_all) (cond);
215 }
216
217 static void
218 shutdown_global_locks (void *data)
219 {
220   DBusMutex ***locks = data;
221   int i;
222
223   i = 0;
224   while (i < _DBUS_N_GLOBAL_LOCKS)
225     {
226       dbus_mutex_free (*(locks[i]));
227       *(locks[i]) = NULL;
228       ++i;
229     }
230   
231   dbus_free (locks);
232 }
233
234 static dbus_bool_t
235 init_global_locks (void)
236 {
237   int i;
238   DBusMutex ***dynamic_global_locks;
239   
240   DBusMutex **global_locks[] = {
241 #define LOCK_ADDR(name) (& _dbus_lock_##name)
242     LOCK_ADDR (list),
243     LOCK_ADDR (connection_slots),
244     LOCK_ADDR (server_slots),
245     LOCK_ADDR (atomic),
246     LOCK_ADDR (message_handler),
247     LOCK_ADDR (user_info),
248     LOCK_ADDR (bus),
249     LOCK_ADDR (shutdown_funcs)
250 #undef LOCK_ADDR
251   };
252
253   _dbus_assert (_DBUS_N_ELEMENTS (global_locks) ==
254                 _DBUS_N_GLOBAL_LOCKS);
255
256   i = 0;
257   
258   dynamic_global_locks = dbus_new (DBusMutex**, _DBUS_N_GLOBAL_LOCKS);
259   if (dynamic_global_locks == NULL)
260     goto failed;
261   
262   while (i < _DBUS_N_ELEMENTS (global_locks))
263     {
264       *global_locks[i] = dbus_mutex_new ();
265       
266       if (*global_locks[i] == NULL)
267         goto failed;
268
269       dynamic_global_locks[i] = global_locks[i];
270
271       ++i;
272     }
273   
274   if (!_dbus_register_shutdown_func (shutdown_global_locks,
275                                      dynamic_global_locks))
276     goto failed;
277   
278   return TRUE;
279
280  failed:
281   dbus_free (dynamic_global_locks);
282                                      
283   for (i = i - 1; i >= 0; i--)
284     {
285       dbus_mutex_free (*global_locks[i]);
286       *global_locks[i] = NULL;
287     }
288   return FALSE;
289 }
290
291
292 /**
293  * Initializes threads. If this function is not called,
294  * the D-BUS library will not lock any data structures.
295  * If it is called, D-BUS will do locking, at some cost
296  * in efficiency. Note that this function must be called
297  * BEFORE using any other D-BUS functions.
298  *
299  * @todo right now this function can only be called once,
300  * maybe we should instead silently ignore multiple calls.
301  *
302  * @param functions functions for using threads
303  * @returns #TRUE on success, #FALSE if no memory
304  */
305 dbus_bool_t
306 dbus_threads_init (const DBusThreadFunctions *functions)
307 {
308   _dbus_assert (functions != NULL);
309
310   /* these base functions are required. Future additions to
311    * DBusThreadFunctions may be optional.
312    */
313   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK);
314   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK);
315   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK);
316   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK);
317   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK);
318   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK);
319   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK);
320   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK);
321   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK);
322   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK);
323   _dbus_assert (functions->mutex_new != NULL);
324   _dbus_assert (functions->mutex_free != NULL);
325   _dbus_assert (functions->mutex_lock != NULL);
326   _dbus_assert (functions->mutex_unlock != NULL);
327   _dbus_assert (functions->condvar_new != NULL);
328   _dbus_assert (functions->condvar_free != NULL);
329   _dbus_assert (functions->condvar_wait != NULL);
330   _dbus_assert (functions->condvar_wait_timeout != NULL);
331   _dbus_assert (functions->condvar_wake_one != NULL);
332   _dbus_assert (functions->condvar_wake_all != NULL);
333
334   /* Check that all bits in the mask actually are valid mask bits.
335    * ensures people won't write code that breaks when we add
336    * new bits.
337    */
338   _dbus_assert ((functions->mask & ~DBUS_THREAD_FUNCTIONS_ALL_MASK) == 0);
339
340   if (thread_init_generation != _dbus_current_generation)
341     thread_functions.mask = 0; /* allow re-init in new generation */
342   
343   if (thread_functions.mask != 0)
344     {
345       _dbus_warn ("dbus_threads_init() may only be called one time\n");
346       return FALSE;
347     }
348   
349   thread_functions.mutex_new = functions->mutex_new;
350   thread_functions.mutex_free = functions->mutex_free;
351   thread_functions.mutex_lock = functions->mutex_lock;
352   thread_functions.mutex_unlock = functions->mutex_unlock;
353   
354   thread_functions.condvar_new = functions->condvar_new;
355   thread_functions.condvar_free = functions->condvar_free;
356   thread_functions.condvar_wait = functions->condvar_wait;
357   thread_functions.condvar_wait_timeout = functions->condvar_wait_timeout;
358   thread_functions.condvar_wake_one = functions->condvar_wake_one;
359   thread_functions.condvar_wake_all = functions->condvar_wake_all;
360   
361   thread_functions.mask = functions->mask;
362
363   if (!init_global_locks ())
364     return FALSE;
365
366   thread_init_generation = _dbus_current_generation;
367   
368   return TRUE;
369 }
370
371 /** @} */