QtGStreamer  1.2.0
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Pages
connect.cpp
1 /*
2  Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
3  Copyright (C) 2010 Collabora Ltd.
4  @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
5 
6  This library is free software; you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation; either version 2.1 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU Lesser General Public License for more details.
15 
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #include "connect.h"
20 #include <glib-object.h>
21 #include <QtCore/QHash>
22 #include <QtCore/QMutex>
23 #include <boost/multi_index_container.hpp>
24 #include <boost/multi_index/sequenced_index.hpp>
25 #include <boost/multi_index/ordered_index.hpp>
26 #include <boost/multi_index/member.hpp>
27 
28 namespace QGlib {
29 namespace Private {
30 
31 //BEGIN ******** Closure internals ********
32 
33 static void c_marshaller(GClosure *closure, GValue *returnValue, uint paramValuesCount,
34  const GValue *paramValues, void *hint, void *data)
35 {
36  Q_UNUSED(data);
37 
38  ClosureDataBase *cdata = static_cast<ClosureDataBase*>(closure->data);
39 
40  QList<Value> params;
41  //the signal sender is always the first argument. if we are instructed not to pass it
42  //as an argument to the slot, begin converting from paramValues[1]
43  for(uint i = cdata->passSender ? 0 : 1; i<paramValuesCount; ++i) {
44  params.append(Value(&paramValues[i]));
45  }
46 
47  try {
48  Value result(returnValue);
49  cdata->marshaller(result, params);
50 
51  if (returnValue && G_IS_VALUE(returnValue)) {
52  g_value_copy(result, returnValue);
53  }
54  } catch (const std::exception & e) {
55  QString signalName;
56  if (hint != NULL) {
57  GSignalInvocationHint *ihint = static_cast<GSignalInvocationHint*>(hint);
58 
59  GSignalQuery query;
60  g_signal_query(ihint->signal_id, &query);
61  signalName = QString::fromUtf8(query.signal_name);
62 
63  if (ihint->detail != 0) {
64  Quark q(ihint->detail);
65  signalName.append(QLatin1String("::"));
66  signalName.append(q.toString());
67  }
68  }
69 
70  QString instanceName = params.at(0).get<QString>();
71 
72  //attempt to determine the cause of the failure
73  QString msg;
74  try {
75  //dynamic_cast will throw an std::bad_cast if it fails
76  dynamic_cast<const InvalidTypeException &>(e);
77  //cast succeded, e is indeed an InvalidTypeException
78  msg = QLatin1String("One or more of the arguments of the signal are of different "
79  "type than the type that the closure expects");
80  } catch (...) {
81  try {
82  dynamic_cast<const InvalidValueException &>(e);
83  //cast succeded, e is indeed an InvalidValueException
84  //this is most likely to happen because the signal returns void
85  //but the closure returns something non-void. check this first.
86  if (returnValue == NULL) {
87  msg = QLatin1String("The signal is defined to return void but the "
88  "closure returns something non-void");
89  } else {
90  msg = QLatin1String("One of the arguments of the signal was not a valid GValue. "
91  "This is most likely a bug in the code that invoked the signal.");
92  }
93  } catch (...) {
94  msg = QString::fromLatin1(e.what());
95  }
96  }
97 
98  qCritical() << "Error during invocation of closure connected to signal"
99  << signalName << "from object" << instanceName << ":" << msg;
100  }
101 }
102 
103 static void closureDestroyNotify(void *data, GClosure *closure)
104 {
105  Q_UNUSED(data);
106  delete static_cast<ClosureDataBase*>(closure->data);
107 }
108 
109 static inline GClosure *createCppClosure(ClosureDataBase *closureData)
110 {
111  GClosure *closure = g_closure_new_simple(sizeof(GClosure), closureData);
112  g_closure_set_marshal(closure, &c_marshaller);
113  g_closure_add_finalize_notifier(closure, NULL, &closureDestroyNotify);
114  g_closure_ref(closure);
115  g_closure_sink(closure);
116  return closure;
117 }
118 
119 //END ******** Closure internals ********
120 //BEGIN ******** QObjectDestroyNotifier ********
121 
122 Q_GLOBAL_STATIC(QWeakPointer<DestroyNotifierIface>, s_qobjDestroyNotifier)
123 Q_GLOBAL_STATIC(QMutex, s_qobjDestroyNotifierMutex)
124 
125 DestroyNotifierIfacePtr QObjectDestroyNotifier::instance()
126 {
127  QMutexLocker l(s_qobjDestroyNotifierMutex());
128 
129  DestroyNotifierIfacePtr ptr = s_qobjDestroyNotifier()->toStrongRef();
130  if (!ptr) {
131  ptr = DestroyNotifierIfacePtr(new QObjectDestroyNotifier);
132  *s_qobjDestroyNotifier() = ptr;
133  }
134  return ptr;
135 }
136 
137 bool QObjectDestroyNotifier::connect(void *receiver, QObject *notificationReceiver, const char *slot)
138 {
139  QObject *qreceiver = reinterpret_cast<QObject*>(receiver);
140  return QObject::connect(qreceiver, SIGNAL(destroyed(QObject*)),
141  notificationReceiver, slot, Qt::DirectConnection);
142 }
143 
144 bool QObjectDestroyNotifier::disconnect(void* receiver, QObject *notificationReceiver)
145 {
146  QObject *qreceiver = reinterpret_cast<QObject*>(receiver);
147  return QObject::disconnect(qreceiver, 0, notificationReceiver, 0);
148 }
149 
150 //END ******** QObjectDestroyNotifier ********
151 //BEGIN ******** ConnectionsStore ********
152 
153 class ConnectionsStore : public QObject
154 {
155  Q_OBJECT
156 public:
157  inline ConnectionsStore() : QObject(), m_handlerIdInRemoval(0) {}
158 
159  ulong connect(void *instance, uint signal, Quark detail,
160  void *receiver, const DestroyNotifierIfacePtr & notifier,
161  uint slotHash, ClosureDataBase *closureData, ConnectFlags flags);
162 
163  bool disconnect(void *instance, uint signal, Quark detail,
164  void *receiver, uint slotHash, ulong handlerId);
165 
166 private:
167  struct Connection
168  {
169  inline Connection(uint signal, Quark detail, void *receiver,
170  uint slotHash, ulong handlerId)
171  : signal(signal),
172  detail(detail),
173  receiver(receiver),
174  slotHash(slotHash),
175  handlerId(handlerId)
176  {
177  }
178 
179  uint signal;
180  Quark detail;
181  void *receiver;
182  uint slotHash;
183  ulong handlerId;
184  };
185 
186  bool lookupAndExec(void *instance, uint signal, Quark detail, void *receiver, uint slotHash,
187  ulong handlerId, void (ConnectionsStore::*func)(void*, const Connection &));
188 
189  void disconnectHandler(void *instance, const Connection & c);
190  void disconnectAndDestroyRcvrWatch(void *instance, const Connection & c);
191 
192  void setupClosureWatch(void *instance, ulong handlerId, GClosure *closure);
193  void onClosureDestroyedAction(void *instance, ulong handlerId);
194  static void onClosureDestroyed(void *data, GClosure *closure);
195 
196  void setupReceiverWatch(void *instance, void *receiver, const DestroyNotifierIfacePtr & notifier);
197  void destroyReceiverWatch(void *instance, const Connection & c);
198 
199 private Q_SLOTS:
200  void onReceiverDestroyed(void *receiver);
201  void onReceiverDestroyed(QObject *receiver);
202 
203 private:
204  //tags
205  struct sequential {};
206  struct by_handlerId {};
207  struct by_signal {};
208  struct by_receiver {};
209 
210  typedef boost::multi_index_container<
211  Connection,
212  boost::multi_index::indexed_by<
213  boost::multi_index::sequenced<
214  boost::multi_index::tag<sequential>
215  >,
216  boost::multi_index::ordered_non_unique<
217  boost::multi_index::tag<by_signal>,
218  boost::multi_index::member<Connection, uint, &Connection::signal>
219  >,
220  boost::multi_index::ordered_non_unique<
221  boost::multi_index::tag<by_receiver>,
222  boost::multi_index::member<Connection, void*, &Connection::receiver>
223  >,
224  boost::multi_index::ordered_unique<
225  boost::multi_index::tag<by_handlerId>,
226  boost::multi_index::member<Connection, ulong, &Connection::handlerId>
227  >
228  >
229  > ConnectionsContainer;
230 
231  typedef ConnectionsContainer::index<sequential>::type::iterator SequentialIterator;
232  typedef ConnectionsContainer::index<by_signal>::type::iterator BySignalIterator;
233  typedef ConnectionsContainer::index<by_receiver>::type::iterator ByReceiverIterator;
234  typedef ConnectionsContainer::index<by_handlerId>::type::iterator ByHandlerIterator;
235  typedef std::pair<BySignalIterator, BySignalIterator> BySignalIterators;
236  typedef std::pair<ByReceiverIterator, ByReceiverIterator> ByReceiverIterators;
237 
238  struct ReceiverData
239  {
240  DestroyNotifierIfacePtr notifier;
241  QHash<void*, int> senders; //<sender, refcount>
242  };
243 
244  QMutex m_mutex;
245  QHash<void*, ConnectionsContainer> m_connections; // <sender, connections>
246  QHash<void*, ReceiverData> m_receivers; // <receiver, data>
247 
248  QMutex m_handlerIdInRemovalMutex;
249  ulong m_handlerIdInRemoval;
250 };
251 
252 Q_GLOBAL_STATIC(ConnectionsStore, s_connectionsStore)
253 
254 ulong ConnectionsStore::connect(void *instance, uint signal, Quark detail,
255  void *receiver, const DestroyNotifierIfacePtr & notifier,
256  uint slotHash, ClosureDataBase *closureData, ConnectFlags flags)
257 {
258  QMutexLocker l(&m_mutex);
259  GClosure *closure = createCppClosure(closureData);
260 
261  ulong handlerId = g_signal_connect_closure_by_id(instance, signal, detail, closure,
262  (flags & ConnectAfter) ? TRUE : FALSE);
263 
264  if (handlerId) {
265  m_connections[instance].get<sequential>().push_back(
266  Connection(signal, detail, receiver, slotHash, handlerId)
267  );
268 
269  setupClosureWatch(instance, handlerId, closure);
270  setupReceiverWatch(instance, receiver, notifier);
271  }
272 
273  g_closure_unref(closure);
274  return handlerId;
275 }
276 
277 bool ConnectionsStore::disconnect(void *instance, uint signal, Quark detail,
278  void *receiver, uint slotHash, ulong handlerId)
279 {
280  QMutexLocker l(&m_mutex);
281  return lookupAndExec(instance, signal, detail, receiver, slotHash, handlerId,
282  &ConnectionsStore::disconnectAndDestroyRcvrWatch);
283 }
284 
285 bool ConnectionsStore::lookupAndExec(void *instance, uint signal, Quark detail,
286  void *receiver, uint slotHash, ulong handlerId,
287  void (ConnectionsStore::*func)(void*, const Connection &))
288 {
289  bool executed = false;
290 
291  if (m_connections.contains(instance)) {
292  ConnectionsContainer & container = m_connections[instance];
293 
294  if (handlerId) {
295  ByHandlerIterator it = container.get<by_handlerId>().find(handlerId);
296 
297  if (it != container.get<by_handlerId>().end()) {
298  (this->*func)(instance, *it);
299  executed = true;
300 
301  container.get<by_handlerId>().erase(it);
302  }
303  } else if (signal) {
304  BySignalIterators iterators = container.get<by_signal>().equal_range(signal);
305 
306  while (iterators.first != iterators.second) {
307  if (!detail ||
308  (detail == iterators.first->detail &&
309  (!receiver ||
310  (receiver == iterators.first->receiver &&
311  (!slotHash || slotHash == iterators.first->slotHash)
312  )
313  )
314  )
315  )
316  {
317  (this->*func)(instance, *iterators.first);
318  executed = true;
319 
320  iterators.first = container.get<by_signal>().erase(iterators.first);
321  } else {
322  ++iterators.first;
323  }
324  }
325  } else if (receiver) {
326  ByReceiverIterators iterators = container.get<by_receiver>().equal_range(receiver);
327 
328  while (iterators.first != iterators.second) {
329  if (!slotHash || slotHash == iterators.first->slotHash) {
330  (this->*func)(instance, *iterators.first);
331  executed = true;
332 
333  iterators.first = container.get<by_receiver>().erase(iterators.first);
334  } else {
335  ++iterators.first;
336  }
337  }
338  } else {
339  for (SequentialIterator it = container.get<sequential>().begin();
340  it != container.get<sequential>().end(); ++it)
341  {
342  (this->*func)(instance, *it);
343  executed = true;
344  }
345  container.get<sequential>().clear();
346  }
347 
348  if (container.get<sequential>().empty()) {
349  m_connections.remove(instance);
350  }
351  }
352 
353  return executed;
354 }
355 
356 void ConnectionsStore::disconnectHandler(void *instance, const Connection & c)
357 {
358  m_handlerIdInRemovalMutex.lock();
359  m_handlerIdInRemoval = c.handlerId;
360  m_handlerIdInRemovalMutex.unlock();
361 
362  /* This will unref the closure and cause onClosureDestroyed to be invoked. */
363  g_signal_handler_disconnect(instance, c.handlerId);
364 
365  m_handlerIdInRemovalMutex.lock();
366  m_handlerIdInRemoval = 0;
367  m_handlerIdInRemovalMutex.unlock();
368 }
369 
370 void ConnectionsStore::disconnectAndDestroyRcvrWatch(void *instance, const Connection & c)
371 {
372  disconnectHandler(instance, c);
373  destroyReceiverWatch(instance, c);
374 }
375 
376 void ConnectionsStore::setupClosureWatch(void *instance, ulong handlerId, GClosure *closure)
377 {
378  void *data = new QPair<void*, ulong>(instance, handlerId);
379  g_closure_add_finalize_notifier(closure, data, &ConnectionsStore::onClosureDestroyed);
380 }
381 
382 //static
383 void ConnectionsStore::onClosureDestroyed(void *data, GClosure *closure)
384 {
385  Q_UNUSED(closure);
386  QPair<void*, ulong> *pair = static_cast< QPair<void*, ulong>* >(data);
387  s_connectionsStore()->onClosureDestroyedAction(pair->first, pair->second);
388  delete pair;
389 }
390 
391 void ConnectionsStore::onClosureDestroyedAction(void *instance, ulong handlerId)
392 {
393  /* Do not do any action if we are being invoked from disconnectHandler() */
394  m_handlerIdInRemovalMutex.lock();
395  register bool ok = (m_handlerIdInRemoval != handlerId);
396  m_handlerIdInRemovalMutex.unlock();
397 
398  if (ok) {
399  QMutexLocker l(&m_mutex);
400  lookupAndExec(instance, 0, Quark(), 0, 0, handlerId, &ConnectionsStore::destroyReceiverWatch);
401  }
402 }
403 
404 void ConnectionsStore::setupReceiverWatch(void *instance, void *receiver,
405  const DestroyNotifierIfacePtr & notifier)
406 {
407  if (!m_receivers.contains(receiver)) {
408  ReceiverData data;
409  data.notifier = notifier;
410  if (!notifier->connect(receiver, this, SLOT(onReceiverDestroyed(QObject*)))) {
411  notifier->connect(receiver, this, SLOT(onReceiverDestroyed(void*)));
412  }
413  m_receivers.insert(receiver, data);
414  }
415 
416  m_receivers[receiver].senders[instance]++;
417 }
418 
419 void ConnectionsStore::destroyReceiverWatch(void *instance, const Connection & c)
420 {
421  if (--m_receivers[c.receiver].senders[instance] == 0) {
422  m_receivers[c.receiver].senders.remove(instance);
423  if (m_receivers[c.receiver].senders.isEmpty()) {
424  m_receivers[c.receiver].notifier->disconnect(c.receiver, this);
425  m_receivers.remove(c.receiver);
426  }
427  }
428 }
429 
430 void ConnectionsStore::onReceiverDestroyed(void *receiver)
431 {
432  QMutexLocker l(&m_mutex);
433  QHashIterator<void*, int> it(m_receivers[receiver].senders);
434  while (it.hasNext()) {
435  it.next();
436  lookupAndExec(it.key(), 0, Quark(), receiver, 0, 0, &ConnectionsStore::disconnectHandler);
437  }
438  m_receivers.remove(receiver);
439 }
440 
441 //optimization hack, to avoid making QObjectDestroyNotifier inherit
442 //QObject and re-emit QObject::destroyed() with void* argument
443 void ConnectionsStore::onReceiverDestroyed(QObject *receiver)
444 {
445  onReceiverDestroyed(static_cast<void*>(receiver));
446 }
447 
448 //END ******** ConnectionsStore ********
449 //BEGIN ******** connect ********
450 
451 ulong connect(void *instance, const char *signal, Quark detail,
452  void *receiver, const DestroyNotifierIfacePtr & notifier,
453  uint slotHash, ClosureDataBase *closureData, ConnectFlags flags)
454 {
455  guint signalId;
456  GQuark detailQuark;
457 
458  if (g_signal_parse_name(signal, Type::fromInstance(instance),
459  &signalId, &detailQuark, FALSE))
460  {
461  if (!detail && detailQuark) {
462  detail = detailQuark;
463  }
464  return s_connectionsStore()->connect(instance, signalId, detail, receiver,
465  notifier, slotHash, closureData, flags);
466  } else {
467  qWarning() << "QGlib::connect: Could not parse signal:" << signal
468  << "- Either it does not exist on this instance, or a detail "
469  "was specified but the signal is not detailed";
470  delete closureData;
471  }
472 
473  return 0;
474 }
475 
476 //END ******** connect ********
477 //BEGIN ******** disconnect ********
478 
479 bool disconnect(void *instance, const char *signal, Quark detail,
480  void *receiver, uint slotHash, ulong handlerId)
481 {
482  guint signalId = 0;
483  GQuark detailQuark = 0;
484 
485  if (signal) {
486  if (g_signal_parse_name(signal, Type::fromInstance(instance),
487  &signalId, &detailQuark, FALSE))
488  {
489  if (!detail && detailQuark) {
490  detail = detailQuark;
491  }
492  } else {
493  qWarning() << "QGlib::disconnect: Could not parse signal:" << signal
494  << "- Either it does not exist on this instance, or a detail "
495  "was specified but the signal is not detailed";
496  return false;
497  }
498  }
499 
500  return s_connectionsStore()->disconnect(instance, signalId, detail,
501  receiver, slotHash, handlerId);
502 }
503 
504 //END ******** disconnect ********
505 
506 } //namespace Private
507 } //namespace QGlib
508 
509 #include "connect.moc"
bool disconnect(void *instance, const char *detailedSignal=0, T *receiver=0, R(T::*slot)(Args...)=0)
bool connect(void *instance, const char *detailedSignal, T *receiver, R(T::*slot)(Args...), ConnectFlags flags=0)
Definition: connectimpl.h:186