QtGStreamer 1.2.0
Loading...
Searching...
No Matches
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
30namespace QGlib {
31namespace Private {
32
33//BEGIN ******** Closure internals ********
34
35static 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
105static void closureDestroyNotify(void *data, GClosure *closure)
106{
107 Q_UNUSED(data);
108 delete static_cast<ClosureDataBase*>(closure->data);
109}
110
111static 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
124Q_GLOBAL_STATIC(QWeakPointer<DestroyNotifierIface>, s_qobjDestroyNotifier)
125Q_GLOBAL_STATIC(QMutex, s_qobjDestroyNotifierMutex)
126
127DestroyNotifierIfacePtr 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
139bool 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
146bool 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
155class ConnectionsStore : public QObject
156{
157 Q_OBJECT
158public:
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
168private:
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
201private Q_SLOTS:
202 void onReceiverDestroyed(void *receiver);
203 void onReceiverDestroyed(QObject *receiver);
204
205private:
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
254Q_GLOBAL_STATIC(ConnectionsStore, s_connectionsStore)
255
256ulong 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
279bool 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
287bool 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
358void 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
372void ConnectionsStore::disconnectAndDestroyRcvrWatch(void *instance, const Connection & c)
373{
374 disconnectHandler(instance, c);
375 destroyReceiverWatch(instance, c);
376}
377
378void 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
385void 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
393void 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
406void 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
421void 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
432void 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
445void ConnectionsStore::onReceiverDestroyed(QObject *receiver)
446{
447 onReceiverDestroyed(static_cast<void*>(receiver));
448}
449
450//END ******** ConnectionsStore ********
451//BEGIN ******** connect ********
452
453ulong 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
481bool 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.
@ ConnectAfter
Definition connect.h:44