2003-03-13 Havoc Pennington <hp@redhat.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
36 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
37 #define _DBUS_DUMMY_MUTEX ((void*)0xABCDEF)
38
39 /** This is used for the no-op default mutex pointer, just to be distinct from #NULL */
40 #define _DBUS_DUMMY_CONDVAR ((void*)0xABCDEF2)
41
42 /**
43  * @defgroup DBusThreads Thread functions
44  * @ingroup  DBus
45  * @brief dbus_threads_init(), dbus_mutex_lock(), etc.
46  *
47  * Functions and macros related to threads and thread locks.
48  *
49  * @{
50  */
51
52 /**
53  * Creates a new mutex using the function supplied to dbus_threads_init(),
54  * or creates a no-op mutex if threads are not initialized.
55  * May return #NULL even if threads are initialized, indicating
56  * out-of-memory.
57  *
58  * @returns new mutex or #NULL
59  */
60 DBusMutex*
61 dbus_mutex_new (void)
62 {
63   if (thread_functions.mutex_new)
64     return (* thread_functions.mutex_new) ();
65   else
66     return _DBUS_DUMMY_MUTEX;
67 }
68
69 /**
70  * Frees a mutex created with dbus_mutex_new(); does
71  * nothing if passed a #NULL pointer.
72  */
73 void
74 dbus_mutex_free (DBusMutex *mutex)
75 {
76   if (mutex && thread_functions.mutex_free)
77     (* thread_functions.mutex_free) (mutex);
78 }
79
80 /**
81  * Locks a mutex. Does nothing if passed a #NULL pointer.
82  * Locks are not recursive.
83  *
84  * @returns #TRUE on success
85  */
86 dbus_bool_t
87 dbus_mutex_lock (DBusMutex *mutex)
88 {
89   if (mutex && thread_functions.mutex_lock)
90     return (* thread_functions.mutex_lock) (mutex);
91   else
92     return TRUE;
93 }
94
95 /**
96  * Unlocks a mutex. Does nothing if passed a #NULL pointer.
97  *
98  * @returns #TRUE on success
99  */
100 dbus_bool_t
101 dbus_mutex_unlock (DBusMutex *mutex)
102 {
103   if (mutex && thread_functions.mutex_unlock)
104     return (* thread_functions.mutex_unlock) (mutex);
105   else
106     return TRUE;
107 }
108
109 /**
110  * Creates a new condition variable using the function supplied
111  * to dbus_threads_init(), or creates a no-op condition variable
112  * if threads are not initialized. May return #NULL even if
113  * threads are initialized, indicating out-of-memory.
114  *
115  * @returns new mutex or #NULL
116  */
117 DBusCondVar *
118 dbus_condvar_new (void)
119 {
120   if (thread_functions.condvar_new)
121     return (* thread_functions.condvar_new) ();
122   else
123     return _DBUS_DUMMY_MUTEX;
124 }
125
126 /**
127  * Frees a conditional variable created with dbus_condvar_new(); does
128  * nothing if passed a #NULL pointer.
129  */
130 void
131 dbus_condvar_free (DBusCondVar *cond)
132 {
133   if (cond && thread_functions.condvar_free)
134     (* thread_functions.condvar_free) (cond);
135 }
136
137 /**
138  * Atomically unlocks the mutex and waits for the conditions
139  * variable to be signalled. Locks the mutex again before
140  * returning.
141  * Does nothing if passed a #NULL pointer.
142  */
143 void
144 dbus_condvar_wait (DBusCondVar *cond,
145                    DBusMutex   *mutex)
146 {
147   if (cond && mutex && thread_functions.condvar_wait)
148     (* thread_functions.condvar_wait) (cond, mutex);
149 }
150
151 /**
152  * Atomically unlocks the mutex and waits for the conditions
153  * variable to be signalled, or for a timeout. Locks the
154  * mutex again before returning.
155  * Does nothing if passed a #NULL pointer.
156  *
157  * @param cond the condition variable
158  * @param mutex the mutex
159  * @param timeout_milliseconds the maximum time to wait
160  * @returns TRUE if the condition was reached, or FALSE if the
161  * timeout was reached.
162  */
163 dbus_bool_t
164 dbus_condvar_wait_timeout (DBusCondVar               *cond,
165                            DBusMutex                 *mutex,
166                            int                        timeout_milliseconds)
167 {
168   if (cond && mutex && thread_functions.condvar_wait)
169     return (* thread_functions.condvar_wait_timeout) (cond, mutex, timeout_milliseconds);
170   else
171     return TRUE;
172 }
173
174 /**
175  * If there are threads waiting on the condition variable, wake
176  * up exactly one. 
177  * Does nothing if passed a #NULL pointer.
178  */
179 void
180 dbus_condvar_wake_one (DBusCondVar *cond)
181 {
182   if (cond && thread_functions.condvar_wake_one)
183     (* thread_functions.condvar_wake_one) (cond);
184 }
185
186 /**
187  * If there are threads waiting on the condition variable, wake
188  * up all of them. 
189  * Does nothing if passed a #NULL pointer.
190  */
191 void
192 dbus_condvar_wake_all (DBusCondVar *cond)
193 {
194   if (cond && thread_functions.condvar_wake_all)
195     (* thread_functions.condvar_wake_all) (cond);
196 }
197
198 static dbus_bool_t
199 init_static_locks(void)
200 {
201   int i;
202   
203   struct {
204     DBusMutex *(*init_func)(void);
205     DBusMutex *mutex;
206   } static_locks[] = {
207     {&_dbus_list_init_lock},
208     {&_dbus_server_slots_init_lock},
209     {&_dbus_connection_slots_init_lock},
210     {&_dbus_atomic_init_lock},
211     {&_dbus_message_handler_init_lock},
212     {&_dbus_user_info_init_lock}
213   };
214   
215   for (i = 0; i < _DBUS_N_ELEMENTS (static_locks); i++)
216     {
217       static_locks[i].mutex = (*static_locks[i].init_func)();
218       
219       if (static_locks[i].mutex == NULL)
220         {
221           for (i = i - 1; i >= 0; i--)
222             dbus_mutex_free (static_locks[i].mutex);
223           return FALSE;
224         }
225       
226     }
227   return TRUE;
228 }
229
230
231 /**
232  * Initializes threads. If this function is not called,
233  * the D-BUS library will not lock any data structures.
234  * If it is called, D-BUS will do locking, at some cost
235  * in efficiency. Note that this function must be called
236  * BEFORE using any other D-BUS functions.
237  *
238  * @todo right now this function can only be called once,
239  * maybe we should instead silently ignore multiple calls.
240  *
241  * @param functions functions for using threads
242  * @returns #TRUE on success, #FALSE if no memory
243  */
244 dbus_bool_t
245 dbus_threads_init (const DBusThreadFunctions *functions)
246 {
247   _dbus_assert (functions != NULL);
248
249   /* these base functions are required. Future additions to
250    * DBusThreadFunctions may be optional.
251    */
252   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK);
253   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK);
254   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK);
255   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK);
256   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK);
257   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK);
258   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK);
259   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK);
260   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK);
261   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK);
262   _dbus_assert (functions->mutex_new != NULL);
263   _dbus_assert (functions->mutex_free != NULL);
264   _dbus_assert (functions->mutex_lock != NULL);
265   _dbus_assert (functions->mutex_unlock != NULL);
266   _dbus_assert (functions->condvar_new != NULL);
267   _dbus_assert (functions->condvar_free != NULL);
268   _dbus_assert (functions->condvar_wait != NULL);
269   _dbus_assert (functions->condvar_wait_timeout != NULL);
270   _dbus_assert (functions->condvar_wake_one != NULL);
271   _dbus_assert (functions->condvar_wake_all != NULL);
272
273   /* Check that all bits in the mask actually are valid mask bits.
274    * ensures people won't write code that breaks when we add
275    * new bits.
276    */
277   _dbus_assert ((functions->mask & ~DBUS_THREAD_FUNCTIONS_ALL_MASK) == 0);
278   
279   if (thread_functions.mask != 0)
280     {
281       _dbus_warn ("dbus_threads_init() may only be called one time\n");
282       return FALSE;
283     }
284   
285   thread_functions.mutex_new = functions->mutex_new;
286   thread_functions.mutex_free = functions->mutex_free;
287   thread_functions.mutex_lock = functions->mutex_lock;
288   thread_functions.mutex_unlock = functions->mutex_unlock;
289   
290   thread_functions.condvar_new = functions->condvar_new;
291   thread_functions.condvar_free = functions->condvar_free;
292   thread_functions.condvar_wait = functions->condvar_wait;
293   thread_functions.condvar_wait_timeout = functions->condvar_wait_timeout;
294   thread_functions.condvar_wake_one = functions->condvar_wake_one;
295   thread_functions.condvar_wake_all = functions->condvar_wake_all;
296   
297   thread_functions.mask = functions->mask;
298
299   if (!init_static_locks ())
300     return FALSE;
301   
302   return TRUE;
303 }
304
305 /** @} */