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