2003-02-16 Anders Carlsson <andersca@codefactory.se>
[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 timeout_milliseconds the maximum time to wait
158  * @returns TRUE if the condition was reached, or FALSE if the
159  * timeout was reached.
160  */
161 dbus_bool_t
162 dbus_condvar_wait_timeout (DBusCondVar               *cond,
163                            DBusMutex                 *mutex,
164                            int                        timeout_milliseconds)
165 {
166   if (cond && mutex && thread_functions.condvar_wait)
167     return (* thread_functions.condvar_wait_timeout) (cond, mutex, timeout_milliseconds);
168   else
169     return TRUE;
170 }
171
172 /**
173  * If there are threads waiting on the condition variable, wake
174  * up exactly one. 
175  * Does nothing if passed a #NULL pointer.
176  */
177 void
178 dbus_condvar_wake_one (DBusCondVar *cond)
179 {
180   if (cond && thread_functions.condvar_wake_one)
181     (* thread_functions.condvar_wake_one) (cond);
182 }
183
184 /**
185  * If there are threads waiting on the condition variable, wake
186  * up all of them. 
187  * Does nothing if passed a #NULL pointer.
188  */
189 void
190 dbus_condvar_wake_all (DBusCondVar *cond)
191 {
192   if (cond && thread_functions.condvar_wake_all)
193     (* thread_functions.condvar_wake_all) (cond);
194 }
195
196
197 DBusMutex * _dbus_list_init_lock (void);
198 DBusMutex * _dbus_allocated_slots_init_lock (void);
199 DBusMutex *_dbus_atomic_init_lock (void);
200 DBusMutex *_dbus_message_handler_init_lock (void);
201
202 static dbus_bool_t
203 init_static_locks(void)
204 {
205   int i;
206   
207   struct {
208     DBusMutex *(*init_func)(void);
209     DBusMutex *mutex;
210   } static_locks[] = {
211     {&_dbus_list_init_lock},
212     {&_dbus_allocated_slots_init_lock},
213     {&_dbus_atomic_init_lock},
214     {&_dbus_message_handler_init_lock},
215   };
216   
217   for (i = 0; i < _DBUS_N_ELEMENTS (static_locks); i++)
218     {
219       static_locks[i].mutex = (*static_locks[i].init_func)();
220       
221       if (static_locks[i].mutex == NULL)
222         {
223           for (i = i - 1; i >= 0; i--)
224             dbus_mutex_free (static_locks[i].mutex);
225           return FALSE;
226         }
227       
228     }
229   return TRUE;
230 }
231
232
233 /**
234  * Initializes threads. If this function is not called,
235  * the D-BUS library will not lock any data structures.
236  * If it is called, D-BUS will do locking, at some cost
237  * in efficiency. Note that this function must be called
238  * BEFORE using any other D-BUS functions.
239  *
240  * @todo right now this function can only be called once,
241  * maybe we should instead silently ignore multiple calls.
242  *
243  * @param functions functions for using threads
244  * @returns #TRUE on success, #FALSE if no memory
245  */
246 dbus_bool_t
247 dbus_threads_init (const DBusThreadFunctions *functions)
248 {
249   _dbus_assert (functions != NULL);
250
251   /* these base functions are required. Future additions to
252    * DBusThreadFunctions may be optional.
253    */
254   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK);
255   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK);
256   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK);
257   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK);
258   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK);
259   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK);
260   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK);
261   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK);
262   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK);
263   _dbus_assert (functions->mask & DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK);
264   _dbus_assert (functions->mutex_new != NULL);
265   _dbus_assert (functions->mutex_free != NULL);
266   _dbus_assert (functions->mutex_lock != NULL);
267   _dbus_assert (functions->mutex_unlock != NULL);
268   _dbus_assert (functions->condvar_new != NULL);
269   _dbus_assert (functions->condvar_free != NULL);
270   _dbus_assert (functions->condvar_wait != NULL);
271   _dbus_assert (functions->condvar_wait_timeout != NULL);
272   _dbus_assert (functions->condvar_wake_one != NULL);
273   _dbus_assert (functions->condvar_wake_all != NULL);
274
275   /* Check that all bits in the mask actually are valid mask bits.
276    * ensures people won't write code that breaks when we add
277    * new bits.
278    */
279   _dbus_assert ((functions->mask & ~DBUS_THREAD_FUNCTIONS_ALL_MASK) == 0);
280   
281   if (thread_functions.mask != 0)
282     {
283       _dbus_warn ("dbus_threads_init() may only be called one time\n");
284       return FALSE;
285     }
286   
287   thread_functions.mutex_new = functions->mutex_new;
288   thread_functions.mutex_free = functions->mutex_free;
289   thread_functions.mutex_lock = functions->mutex_lock;
290   thread_functions.mutex_unlock = functions->mutex_unlock;
291   
292   thread_functions.condvar_new = functions->condvar_new;
293   thread_functions.condvar_free = functions->condvar_free;
294   thread_functions.condvar_wait = functions->condvar_wait;
295   thread_functions.condvar_wait_timeout = functions->condvar_wait_timeout;
296   thread_functions.condvar_wake_one = functions->condvar_wake_one;
297   thread_functions.condvar_wake_all = functions->condvar_wake_all;
298   
299   thread_functions.mask = functions->mask;
300
301   if (!init_static_locks ())
302     return FALSE;
303   
304   return TRUE;
305 }
306
307 /** @} */