1 /*
2  *
3  *  D-Bus++ - C++ bindings for D-Bus
4  *
5  *  Copyright (C) 2005-2007  Paolo Durante <shackan@gmail.com>
6  *
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library 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 GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <dbus-c++/dispatcher.h>
29 
30 #include <dbus/dbus.h>
31 
32 #include "dispatcher_p.h"
33 #include "server_p.h"
34 #include "connection_p.h"
35 
36 DBus::Dispatcher *DBus::default_dispatcher = NULL;
37 
38 using namespace DBus;
39 
Timeout(Timeout::Internal * i)40 Timeout::Timeout(Timeout::Internal *i)
41   : _int(i)
42 {
43   dbus_timeout_set_data((DBusTimeout *)i, this, NULL);
44 }
45 
interval() const46 int Timeout::interval() const
47 {
48   return dbus_timeout_get_interval((DBusTimeout *)_int);
49 }
50 
enabled() const51 bool Timeout::enabled() const
52 {
53   return dbus_timeout_get_enabled((DBusTimeout *)_int);
54 }
55 
handle()56 bool Timeout::handle()
57 {
58   return dbus_timeout_handle((DBusTimeout *)_int);
59 }
60 
61 /*
62 */
63 
Watch(Watch::Internal * i)64 Watch::Watch(Watch::Internal *i)
65   : _int(i)
66 {
67   dbus_watch_set_data((DBusWatch *)i, this, NULL);
68 }
69 
descriptor() const70 int Watch::descriptor() const
71 {
72 #if HAVE_WIN32
73   return dbus_watch_get_socket((DBusWatch *)_int);
74 #else
75   // check dbus version and use dbus_watch_get_unix_fd() only in dbus >= 1.1.1
76 #if (DBUS_VERSION_MAJOR == 1 && DBUS_VERSION_MINOR == 1 && DBUS_VERSION_MICRO >= 1) || \
77 		(DBUS_VERSION_MAJOR == 1 && DBUS_VERSION_MAJOR > 1) || \
78 		(DBUS_VERSION_MAJOR > 1)
79   return dbus_watch_get_unix_fd((DBusWatch *)_int);
80 #else
81   return dbus_watch_get_fd((DBusWatch *)_int);
82 #endif
83 #endif
84 }
85 
flags() const86 int Watch::flags() const
87 {
88   return dbus_watch_get_flags((DBusWatch *)_int);
89 }
90 
enabled() const91 bool Watch::enabled() const
92 {
93   return dbus_watch_get_enabled((DBusWatch *)_int);
94 }
95 
handle(int flags)96 bool Watch::handle(int flags)
97 {
98   return dbus_watch_handle((DBusWatch *)_int, flags);
99 }
100 
101 /*
102 */
103 
on_add_watch(DBusWatch * watch,void * data)104 dbus_bool_t Dispatcher::Private::on_add_watch(DBusWatch *watch, void *data)
105 {
106   Dispatcher *d = static_cast<Dispatcher *>(data);
107 
108   Watch::Internal *w = reinterpret_cast<Watch::Internal *>(watch);
109 
110   d->add_watch(w);
111 
112   return true;
113 }
114 
on_rem_watch(DBusWatch * watch,void * data)115 void Dispatcher::Private::on_rem_watch(DBusWatch *watch, void *data)
116 {
117   Dispatcher *d = static_cast<Dispatcher *>(data);
118 
119   Watch *w = static_cast<Watch *>(dbus_watch_get_data(watch));
120 
121   d->rem_watch(w);
122 }
123 
on_toggle_watch(DBusWatch * watch,void * data)124 void Dispatcher::Private::on_toggle_watch(DBusWatch *watch, void *data)
125 {
126   Watch *w = static_cast<Watch *>(dbus_watch_get_data(watch));
127 
128   w->toggle();
129 }
130 
on_add_timeout(DBusTimeout * timeout,void * data)131 dbus_bool_t Dispatcher::Private::on_add_timeout(DBusTimeout *timeout, void *data)
132 {
133   Dispatcher *d = static_cast<Dispatcher *>(data);
134 
135   Timeout::Internal *t = reinterpret_cast<Timeout::Internal *>(timeout);
136 
137   d->add_timeout(t);
138 
139   return true;
140 }
141 
on_rem_timeout(DBusTimeout * timeout,void * data)142 void Dispatcher::Private::on_rem_timeout(DBusTimeout *timeout, void *data)
143 {
144   Dispatcher *d = static_cast<Dispatcher *>(data);
145 
146   Timeout *t = static_cast<Timeout *>(dbus_timeout_get_data(timeout));
147 
148   d->rem_timeout(t);
149 }
150 
on_toggle_timeout(DBusTimeout * timeout,void * data)151 void Dispatcher::Private::on_toggle_timeout(DBusTimeout *timeout, void *data)
152 {
153   Timeout *t = static_cast<Timeout *>(dbus_timeout_get_data(timeout));
154 
155   t->toggle();
156 }
157 
queue_connection(Connection::Private * cp)158 void Dispatcher::queue_connection(Connection::Private *cp)
159 {
160   _mutex_p.lock();
161   _pending_queue.push_back(cp);
162   _mutex_p.unlock();
163 }
164 
165 
has_something_to_dispatch()166 bool Dispatcher::has_something_to_dispatch()
167 {
168   _mutex_p.lock();
169   bool has_something = false;
170   for (Connection::PrivatePList::iterator it = _pending_queue.begin();
171        it != _pending_queue.end() && !has_something;
172        ++it)
173   {
174     has_something = (*it)->has_something_to_dispatch();
175   }
176 
177   _mutex_p.unlock();
178   return has_something;
179 }
180 
181 
dispatch_pending()182 void Dispatcher::dispatch_pending()
183 {
184   while (1)
185   {
186     _mutex_p.lock();
187     if (_pending_queue.empty())
188     {
189       _mutex_p.unlock();
190       break;
191     }
192 
193     Connection::PrivatePList pending_queue_copy(_pending_queue);
194     _mutex_p.unlock();
195 
196     size_t copy_elem_num(pending_queue_copy.size());
197 
198     dispatch_pending(pending_queue_copy);
199 
200     //only push_back on list is mandatory!
201     _mutex_p.lock();
202 
203     Connection::PrivatePList::iterator i, j;
204     i = _pending_queue.begin();
205     size_t counter = 0;
206     while (counter < copy_elem_num && i != _pending_queue.end())
207     {
208       j = i;
209       ++j;
210       _pending_queue.erase(i);
211       i = j;
212       ++counter;
213     }
214 
215     _mutex_p.unlock();
216   }
217 }
218 
dispatch_pending(Connection::PrivatePList & pending_queue)219 void Dispatcher::dispatch_pending(Connection::PrivatePList &pending_queue)
220 {
221   // SEEME: dbus-glib is dispatching only one message at a time to not starve the loop/other things...
222 
223   _mutex_p_copy.lock();
224   while (pending_queue.size() > 0)
225   {
226     Connection::PrivatePList::iterator i, j;
227 
228     i = pending_queue.begin();
229 
230     while (i != pending_queue.end())
231     {
232       j = i;
233 
234       ++j;
235 
236       if ((*i)->do_dispatch())
237         pending_queue.erase(i);
238       else
239         debug_log("dispatch_pending_private: do_dispatch error");
240 
241       i = j;
242     }
243   }
244   _mutex_p_copy.unlock();
245 }
246 
_init_threading()247 void DBus::_init_threading()
248 {
249 #ifdef DBUS_HAS_THREADS_INIT_DEFAULT
250   dbus_threads_init_default();
251 #else
252   debug_log("Thread support is not enabled! Your D-Bus version is too old!");
253 #endif//DBUS_HAS_THREADS_INIT_DEFAULT
254 }
255 
_init_threading(MutexNewFn m1,MutexFreeFn m2,MutexLockFn m3,MutexUnlockFn m4,CondVarNewFn c1,CondVarFreeFn c2,CondVarWaitFn c3,CondVarWaitTimeoutFn c4,CondVarWakeOneFn c5,CondVarWakeAllFn c6)256 void DBus::_init_threading(
257   MutexNewFn m1,
258   MutexFreeFn m2,
259   MutexLockFn m3,
260   MutexUnlockFn m4,
261   CondVarNewFn c1,
262   CondVarFreeFn c2,
263   CondVarWaitFn c3,
264   CondVarWaitTimeoutFn c4,
265   CondVarWakeOneFn c5,
266   CondVarWakeAllFn c6
267 )
268 {
269 #ifndef DBUS_HAS_RECURSIVE_MUTEX
270   DBusThreadFunctions functions =
271   {
272     DBUS_THREAD_FUNCTIONS_MUTEX_NEW_MASK |
273     DBUS_THREAD_FUNCTIONS_MUTEX_FREE_MASK |
274     DBUS_THREAD_FUNCTIONS_MUTEX_LOCK_MASK |
275     DBUS_THREAD_FUNCTIONS_MUTEX_UNLOCK_MASK |
276     DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK |
277     DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK |
278     DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK |
279     DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK |
280     DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK |
281     DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK,
282     (DBusMutexNewFunction) m1,
283     (DBusMutexFreeFunction) m2,
284     (DBusMutexLockFunction) m3,
285     (DBusMutexUnlockFunction) m4,
286     (DBusCondVarNewFunction) c1,
287     (DBusCondVarFreeFunction) c2,
288     (DBusCondVarWaitFunction) c3,
289     (DBusCondVarWaitTimeoutFunction) c4,
290     (DBusCondVarWakeOneFunction) c5,
291     (DBusCondVarWakeAllFunction) c6
292   };
293 #else
294   DBusThreadFunctions functions =
295   {
296     DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_NEW_MASK |
297     DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_FREE_MASK |
298     DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_LOCK_MASK |
299     DBUS_THREAD_FUNCTIONS_RECURSIVE_MUTEX_UNLOCK_MASK |
300     DBUS_THREAD_FUNCTIONS_CONDVAR_NEW_MASK |
301     DBUS_THREAD_FUNCTIONS_CONDVAR_FREE_MASK |
302     DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_MASK |
303     DBUS_THREAD_FUNCTIONS_CONDVAR_WAIT_TIMEOUT_MASK |
304     DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ONE_MASK |
305     DBUS_THREAD_FUNCTIONS_CONDVAR_WAKE_ALL_MASK,
306     0, 0, 0, 0,
307     (DBusCondVarNewFunction) c1,
308     (DBusCondVarFreeFunction) c2,
309     (DBusCondVarWaitFunction) c3,
310     (DBusCondVarWaitTimeoutFunction) c4,
311     (DBusCondVarWakeOneFunction) c5,
312     (DBusCondVarWakeAllFunction) c6,
313     (DBusRecursiveMutexNewFunction) m1,
314     (DBusRecursiveMutexFreeFunction) m2,
315     (DBusRecursiveMutexLockFunction) m3,
316     (DBusRecursiveMutexUnlockFunction) m4
317   };
318 #endif//DBUS_HAS_RECURSIVE_MUTEX
319   dbus_threads_init(&functions);
320 }
321