Merge branch 'master' of drop.maemo.org:/git/monky
[monky] / src / dbus / dbus-sysdeps-pthread.c
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /* dbus-sysdeps-pthread.c Implements threads using pthreads (internal to libdbus)
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23
24 #include "dbus-internals.h"
25 #include "dbus-sysdeps.h"
26 #include "dbus-threads.h"
27
28 #include <sys/time.h>
29 #include <pthread.h>
30 #include <string.h>
31
32 #ifdef HAVE_ERRNO_H
33 #include <errno.h>
34 #endif
35
36 typedef struct {
37   pthread_mutex_t lock; /**< lock protecting count field */
38   volatile int count;   /**< count of how many times lock holder has recursively locked */
39   volatile pthread_t holder; /**< holder of the lock if count >0,
40                                 valid but arbitrary thread if count
41                                 has ever been >0, uninitialized memory
42                                 if count has never been >0 */
43 } DBusMutexPThread;
44
45 typedef struct {
46   pthread_cond_t cond; /**< the condition */
47 } DBusCondVarPThread;
48
49 #define DBUS_MUTEX(m)         ((DBusMutex*) m)
50 #define DBUS_MUTEX_PTHREAD(m) ((DBusMutexPThread*) m)
51
52 #define DBUS_COND_VAR(c)         ((DBusCondVar*) c)
53 #define DBUS_COND_VAR_PTHREAD(c) ((DBusCondVarPThread*) c)
54
55
56 #ifdef DBUS_DISABLE_ASSERT
57 /* (tmp != 0) is a no-op usage to silence compiler */
58 #define PTHREAD_CHECK(func_name, result_or_call)    \
59     do { int tmp = (result_or_call); if (tmp != 0) {;} } while (0)
60 #else
61 #define PTHREAD_CHECK(func_name, result_or_call) do {                                  \
62     int tmp = (result_or_call);                                                        \
63     if (tmp != 0) {                                                                    \
64       _dbus_warn_check_failed ("pthread function %s failed with %d %s in %s\n",        \
65                                func_name, tmp, strerror(tmp), _DBUS_FUNCTION_NAME);    \
66     }                                                                                  \
67 } while (0)
68 #endif /* !DBUS_DISABLE_ASSERT */
69
70 static DBusMutex*
71 _dbus_pthread_mutex_new (void)
72 {
73   DBusMutexPThread *pmutex;
74   int result;
75   
76   pmutex = dbus_new (DBusMutexPThread, 1);
77   if (pmutex == NULL)
78     return NULL;
79
80   result = pthread_mutex_init (&pmutex->lock, NULL);
81
82   if (result == ENOMEM || result == EAGAIN)
83     {
84       dbus_free (pmutex);
85       return NULL;
86     }
87   else
88     {
89       PTHREAD_CHECK ("pthread_mutex_init", result);
90     }
91
92   /* Only written */
93   pmutex->count = 0;
94
95   /* There's no portable way to have a "null" pthread afaik so we
96    * can't set pmutex->holder to anything sensible.  We only access it
97    * once the lock is held (which means we've set it).
98    */
99   
100   return DBUS_MUTEX (pmutex);
101 }
102
103 static void
104 _dbus_pthread_mutex_free (DBusMutex *mutex)
105 {
106   DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex);
107
108   _dbus_assert (pmutex->count == 0);
109   
110   PTHREAD_CHECK ("pthread_mutex_destroy", pthread_mutex_destroy (&pmutex->lock));
111
112   dbus_free (pmutex);
113 }
114
115 static void
116 _dbus_pthread_mutex_lock (DBusMutex *mutex)
117 {
118   DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex);
119   pthread_t self = pthread_self ();
120
121   /* If the count is > 0 then someone had the lock, maybe us. If it is
122    * 0, then it might immediately change right after we read it,
123    * but it will be changed by another thread; i.e. if we read 0,
124    * we assume that this thread doesn't have the lock.
125    *
126    * Not 100% sure this is safe, but ... seems like it should be.
127    */
128   if (pmutex->count == 0)
129     {
130       /* We know we don't have the lock; someone may have the lock. */
131       
132       PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock));
133
134       /* We now have the lock. Count must be 0 since it must be 0 when
135        * the lock is released by another thread, and we just now got
136        * the lock.
137        */
138       _dbus_assert (pmutex->count == 0);
139       
140       pmutex->holder = self;
141       pmutex->count = 1;
142     }
143   else
144     {
145       /* We know someone had the lock, possibly us. Thus
146        * pmutex->holder is not pointing to junk, though it may not be
147        * the lock holder anymore if the lock holder is not us.  If the
148        * lock holder is us, then we definitely have the lock.
149        */
150
151       if (pthread_equal (pmutex->holder, self))
152         {
153           /* We already have the lock. */
154           _dbus_assert (pmutex->count > 0);
155         }
156       else
157         {
158           /* Wait for the lock */
159           PTHREAD_CHECK ("pthread_mutex_lock", pthread_mutex_lock (&pmutex->lock));
160           pmutex->holder = self;
161           _dbus_assert (pmutex->count == 0);
162         }
163
164       pmutex->count += 1;
165     }
166 }
167
168 static void
169 _dbus_pthread_mutex_unlock (DBusMutex *mutex)
170 {
171   DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex);
172
173   _dbus_assert (pmutex->count > 0);
174   
175   pmutex->count -= 1;
176
177   if (pmutex->count == 0)
178     PTHREAD_CHECK ("pthread_mutex_unlock", pthread_mutex_unlock (&pmutex->lock));
179   
180   /* We leave pmutex->holder set to ourselves, its content is undefined if count is 0 */
181 }
182
183 static DBusCondVar *
184 _dbus_pthread_condvar_new (void)
185 {
186   DBusCondVarPThread *pcond;
187   int result;
188   
189   pcond = dbus_new (DBusCondVarPThread, 1);
190   if (pcond == NULL)
191     return NULL;
192
193   result = pthread_cond_init (&pcond->cond, NULL);
194
195   if (result == EAGAIN || result == ENOMEM)
196     {
197       dbus_free (pcond);
198       return NULL;
199     }
200   else
201     {
202       PTHREAD_CHECK ("pthread_cond_init", result);
203     }
204   
205   return DBUS_COND_VAR (pcond);
206 }
207
208 static void
209 _dbus_pthread_condvar_free (DBusCondVar *cond)
210 {  
211   DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond);
212   
213   PTHREAD_CHECK ("pthread_cond_destroy", pthread_cond_destroy (&pcond->cond));
214
215   dbus_free (pcond);
216 }
217
218 static void
219 _dbus_pthread_condvar_wait (DBusCondVar *cond,
220                             DBusMutex   *mutex)
221 {
222   DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex);
223   DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond);
224   int old_count;
225   
226   _dbus_assert (pmutex->count > 0);
227   _dbus_assert (pthread_equal (pmutex->holder, pthread_self ()));
228
229   old_count = pmutex->count;
230   pmutex->count = 0;            /* allow other threads to lock */
231   PTHREAD_CHECK ("pthread_cond_wait", pthread_cond_wait (&pcond->cond, &pmutex->lock));
232   _dbus_assert (pmutex->count == 0);
233   pmutex->count = old_count;
234   pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */
235 }
236
237 static dbus_bool_t
238 _dbus_pthread_condvar_wait_timeout (DBusCondVar               *cond,
239                                     DBusMutex                 *mutex,
240                                     int                        timeout_milliseconds)
241 {
242   DBusMutexPThread *pmutex = DBUS_MUTEX_PTHREAD (mutex);
243   DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond);
244   struct timeval time_now;
245   struct timespec end_time;
246   int result;
247   int old_count;
248   
249   _dbus_assert (pmutex->count > 0);
250   _dbus_assert (pthread_equal (pmutex->holder, pthread_self ()));  
251   
252   gettimeofday (&time_now, NULL);
253   
254   end_time.tv_sec = time_now.tv_sec + timeout_milliseconds / 1000;
255   end_time.tv_nsec = (time_now.tv_usec + (timeout_milliseconds % 1000) * 1000) * 1000;
256   if (end_time.tv_nsec > 1000*1000*1000)
257     {
258       end_time.tv_sec += 1;
259       end_time.tv_nsec -= 1000*1000*1000;
260     }
261
262   old_count = pmutex->count;
263   pmutex->count = 0;
264   result = pthread_cond_timedwait (&pcond->cond, &pmutex->lock, &end_time);
265   
266   if (result != ETIMEDOUT)
267     {
268       PTHREAD_CHECK ("pthread_cond_timedwait", result);
269     }
270
271   _dbus_assert (pmutex->count == 0);
272   pmutex->count = old_count;
273   pmutex->holder = pthread_self(); /* other threads may have locked the mutex in the meantime */
274   
275   /* return true if we did not time out */
276   return result != ETIMEDOUT;
277 }
278
279 static void
280 _dbus_pthread_condvar_wake_one (DBusCondVar *cond)
281 {
282   DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond);
283
284   PTHREAD_CHECK ("pthread_cond_signal", pthread_cond_signal (&pcond->cond));
285 }
286
287 static void
288 _dbus_pthread_condvar_wake_all (DBusCondVar *cond)
289 {
290   DBusCondVarPThread *pcond = DBUS_COND_VAR_PTHREAD (cond);
291   
292   PTHREAD_CHECK ("pthread_cond_broadcast", pthread_cond_broadcast (&pcond->cond));
293 }
294
295 static const DBusThreadFunctions pthread_functions =
296 {
297   DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK |
298   DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK |
299   DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK |
300   DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK |
301   DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK |
302   DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK |
303   DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK |
304   DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK |
305   DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK|
306   DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK,
307   NULL, NULL, NULL, NULL,
308   _dbus_pthread_condvar_new,
309   _dbus_pthread_condvar_free,
310   _dbus_pthread_condvar_wait,
311   _dbus_pthread_condvar_wait_timeout,
312   _dbus_pthread_condvar_wake_one,
313   _dbus_pthread_condvar_wake_all,
314   _dbus_pthread_mutex_new,
315   _dbus_pthread_mutex_free,
316   _dbus_pthread_mutex_lock,
317   _dbus_pthread_mutex_unlock
318 };
319
320 dbus_bool_t
321 _dbus_threads_init_platform_specific (void)
322 {
323   return dbus_threads_init (&pthread_functions);
324 }