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>
33 static void c_marshaller(GClosure *closure, GValue *returnValue, uint paramValuesCount,
34 const GValue *paramValues,
void *hint,
void *data)
38 ClosureDataBase *cdata =
static_cast<ClosureDataBase*
>(closure->data);
43 for(uint i = cdata->passSender ? 0 : 1; i<paramValuesCount; ++i) {
44 params.append(Value(¶mValues[i]));
48 Value result(returnValue);
49 cdata->marshaller(result, params);
51 if (returnValue && G_IS_VALUE(returnValue)) {
52 g_value_copy(result, returnValue);
54 }
catch (
const std::exception & e) {
57 GSignalInvocationHint *ihint =
static_cast<GSignalInvocationHint*
>(hint);
60 g_signal_query(ihint->signal_id, &query);
61 signalName = QString::fromUtf8(query.signal_name);
63 if (ihint->detail != 0) {
64 Quark q(ihint->detail);
65 signalName.append(QLatin1String(
"::"));
66 signalName.append(q.toString());
70 QString instanceName = params.at(0).get<QString>();
76 dynamic_cast<const InvalidTypeException &
>(e);
78 msg = QLatin1String(
"One or more of the arguments of the signal are of different "
79 "type than the type that the closure expects");
82 dynamic_cast<const InvalidValueException &
>(e);
86 if (returnValue == NULL) {
87 msg = QLatin1String(
"The signal is defined to return void but the "
88 "closure returns something non-void");
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.");
94 msg = QString::fromAscii(e.what());
98 qCritical() <<
"Error during invocation of closure connected to signal"
99 << signalName <<
"from object" << instanceName <<
":" << msg;
103 static void closureDestroyNotify(
void *data, GClosure *closure)
106 delete static_cast<ClosureDataBase*
>(closure->data);
109 static inline GClosure *createCppClosure(ClosureDataBase *closureData)
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);
122 Q_GLOBAL_STATIC(QWeakPointer<DestroyNotifierIface>, s_qobjDestroyNotifier)
123 Q_GLOBAL_STATIC(QMutex, s_qobjDestroyNotifierMutex)
125 DestroyNotifierIfacePtr QObjectDestroyNotifier::instance()
127 QMutexLocker l(s_qobjDestroyNotifierMutex());
129 DestroyNotifierIfacePtr ptr = s_qobjDestroyNotifier()->toStrongRef();
131 ptr = DestroyNotifierIfacePtr(
new QObjectDestroyNotifier);
132 *s_qobjDestroyNotifier() = ptr;
137 bool QObjectDestroyNotifier::connect(
void *receiver, QObject *notificationReceiver,
const char *slot)
139 QObject *qreceiver =
reinterpret_cast<QObject*
>(receiver);
140 return QObject::connect(qreceiver, SIGNAL(destroyed(QObject*)),
141 notificationReceiver, slot, Qt::DirectConnection);
144 bool QObjectDestroyNotifier::disconnect(
void* receiver, QObject *notificationReceiver)
146 QObject *qreceiver =
reinterpret_cast<QObject*
>(receiver);
147 return QObject::disconnect(qreceiver, 0, notificationReceiver, 0);
153 class ConnectionsStore :
public QObject
157 inline ConnectionsStore() : QObject(), m_handlerIdInRemoval(0) {}
159 ulong
connect(
void *instance, uint signal, Quark detail,
160 void *receiver,
const DestroyNotifierIfacePtr & notifier,
161 uint slotHash, ClosureDataBase *closureData, ConnectFlags flags);
163 bool disconnect(
void *instance, uint signal, Quark detail,
164 void *receiver, uint slotHash, ulong handlerId);
169 inline Connection(uint signal, Quark detail,
void *receiver,
170 uint slotHash, ulong handlerId)
186 bool lookupAndExec(
void *instance, uint signal, Quark detail,
void *receiver, uint slotHash,
187 ulong handlerId,
void (ConnectionsStore::*func)(
void*,
const Connection &));
189 void disconnectHandler(
void *instance,
const Connection & c);
190 void disconnectAndDestroyRcvrWatch(
void *instance,
const Connection & c);
192 void setupClosureWatch(
void *instance, ulong handlerId, GClosure *closure);
193 void onClosureDestroyedAction(
void *instance, ulong handlerId);
194 static void onClosureDestroyed(
void *data, GClosure *closure);
196 void setupReceiverWatch(
void *instance,
void *receiver,
const DestroyNotifierIfacePtr & notifier);
197 void destroyReceiverWatch(
void *instance,
const Connection & c);
200 void onReceiverDestroyed(
void *receiver);
201 void onReceiverDestroyed(QObject *receiver);
205 struct sequential {};
206 struct by_handlerId {};
208 struct by_receiver {};
210 typedef boost::multi_index_container<
212 boost::multi_index::indexed_by<
213 boost::multi_index::sequenced<
214 boost::multi_index::tag<sequential>
216 boost::multi_index::ordered_non_unique<
217 boost::multi_index::tag<by_signal>,
218 boost::multi_index::member<Connection, uint, &Connection::signal>
220 boost::multi_index::ordered_non_unique<
221 boost::multi_index::tag<by_receiver>,
222 boost::multi_index::member<Connection, void*, &Connection::receiver>
224 boost::multi_index::ordered_unique<
225 boost::multi_index::tag<by_handlerId>,
226 boost::multi_index::member<Connection, ulong, &Connection::handlerId>
229 > ConnectionsContainer;
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;
240 DestroyNotifierIfacePtr notifier;
241 QHash<void*, int> senders;
245 QHash<void*, ConnectionsContainer> m_connections;
246 QHash<void*, ReceiverData> m_receivers;
248 QMutex m_handlerIdInRemovalMutex;
249 ulong m_handlerIdInRemoval;
252 Q_GLOBAL_STATIC(ConnectionsStore, s_connectionsStore)
254 ulong ConnectionsStore::
connect(
void *instance, uint signal, Quark detail,
255 void *receiver, const DestroyNotifierIfacePtr & notifier,
256 uint slotHash, ClosureDataBase *closureData, ConnectFlags flags)
258 QMutexLocker l(&m_mutex);
259 GClosure *closure = createCppClosure(closureData);
261 ulong handlerId = g_signal_connect_closure_by_id(instance, signal, detail, closure,
265 m_connections[instance].get<sequential>().push_back(
266 Connection(signal, detail, receiver, slotHash, handlerId)
269 setupClosureWatch(instance, handlerId, closure);
270 setupReceiverWatch(instance, receiver, notifier);
273 g_closure_unref(closure);
277 bool ConnectionsStore::disconnect(
void *instance, uint signal, Quark detail,
278 void *receiver, uint slotHash, ulong handlerId)
280 QMutexLocker l(&m_mutex);
281 return lookupAndExec(instance, signal, detail, receiver, slotHash, handlerId,
282 &ConnectionsStore::disconnectAndDestroyRcvrWatch);
285 bool ConnectionsStore::lookupAndExec(
void *instance, uint signal, Quark detail,
286 void *receiver, uint slotHash, ulong handlerId,
287 void (ConnectionsStore::*func)(
void*,
const Connection &))
289 bool executed =
false;
291 if (m_connections.contains(instance)) {
292 ConnectionsContainer & container = m_connections[instance];
295 ByHandlerIterator it = container.get<by_handlerId>().find(handlerId);
297 if (it != container.get<by_handlerId>().end()) {
298 (this->*func)(instance, *it);
301 container.get<by_handlerId>().erase(it);
304 BySignalIterators iterators = container.get<by_signal>().equal_range(signal);
306 while (iterators.first != iterators.second) {
308 (detail == iterators.first->detail &&
310 (receiver == iterators.first->receiver &&
311 (!slotHash || slotHash == iterators.first->slotHash)
317 (this->*func)(instance, *iterators.first);
320 iterators.first = container.get<by_signal>().erase(iterators.first);
325 }
else if (receiver) {
326 ByReceiverIterators iterators = container.get<by_receiver>().equal_range(receiver);
328 while (iterators.first != iterators.second) {
329 if (!slotHash || slotHash == iterators.first->slotHash) {
330 (this->*func)(instance, *iterators.first);
333 iterators.first = container.get<by_receiver>().erase(iterators.first);
339 for (SequentialIterator it = container.get<sequential>().begin();
340 it != container.get<sequential>().end(); ++it)
342 (this->*func)(instance, *it);
345 container.get<sequential>().clear();
348 if (container.get<sequential>().empty()) {
349 m_connections.remove(instance);
356 void ConnectionsStore::disconnectHandler(
void *instance,
const Connection & c)
358 m_handlerIdInRemovalMutex.lock();
359 m_handlerIdInRemoval = c.handlerId;
360 m_handlerIdInRemovalMutex.unlock();
363 g_signal_handler_disconnect(instance, c.handlerId);
365 m_handlerIdInRemovalMutex.lock();
366 m_handlerIdInRemoval = 0;
367 m_handlerIdInRemovalMutex.unlock();
370 void ConnectionsStore::disconnectAndDestroyRcvrWatch(
void *instance,
const Connection & c)
372 disconnectHandler(instance, c);
373 destroyReceiverWatch(instance, c);
376 void ConnectionsStore::setupClosureWatch(
void *instance, ulong handlerId, GClosure *closure)
378 void *data =
new QPair<void*, ulong>(instance, handlerId);
379 g_closure_add_finalize_notifier(closure, data, &ConnectionsStore::onClosureDestroyed);
383 void ConnectionsStore::onClosureDestroyed(
void *data, GClosure *closure)
386 QPair<void*, ulong> *pair =
static_cast< QPair<void*, ulong>*
>(data);
387 s_connectionsStore()->onClosureDestroyedAction(pair->first, pair->second);
391 void ConnectionsStore::onClosureDestroyedAction(
void *instance, ulong handlerId)
394 m_handlerIdInRemovalMutex.lock();
395 register bool ok = (m_handlerIdInRemoval != handlerId);
396 m_handlerIdInRemovalMutex.unlock();
399 QMutexLocker l(&m_mutex);
400 lookupAndExec(instance, 0, Quark(), 0, 0, handlerId, &ConnectionsStore::destroyReceiverWatch);
404 void ConnectionsStore::setupReceiverWatch(
void *instance,
void *receiver,
405 const DestroyNotifierIfacePtr & notifier)
407 if (!m_receivers.contains(receiver)) {
409 data.notifier = notifier;
410 if (!notifier->connect(receiver,
this, SLOT(onReceiverDestroyed(QObject*)))) {
411 notifier->connect(receiver,
this, SLOT(onReceiverDestroyed(
void*)));
413 m_receivers.insert(receiver, data);
416 m_receivers[receiver].senders[instance]++;
419 void ConnectionsStore::destroyReceiverWatch(
void *instance,
const Connection & c)
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);
430 void ConnectionsStore::onReceiverDestroyed(
void *receiver)
432 QMutexLocker l(&m_mutex);
433 QHashIterator<void*, int> it(m_receivers[receiver].senders);
434 while (it.hasNext()) {
436 lookupAndExec(it.key(), 0, Quark(), receiver, 0, 0, &ConnectionsStore::disconnectHandler);
438 m_receivers.remove(receiver);
443 void ConnectionsStore::onReceiverDestroyed(QObject *receiver)
445 onReceiverDestroyed(static_cast<void*>(receiver));
451 ulong
connect(
void *instance,
const char *signal, Quark detail,
452 void *receiver,
const DestroyNotifierIfacePtr & notifier,
453 uint slotHash, ClosureDataBase *closureData, ConnectFlags flags)
458 if (g_signal_parse_name(signal, Type::fromInstance(instance),
459 &signalId, &detailQuark, FALSE))
461 if (!detail && detailQuark) {
462 detail = detailQuark;
464 return s_connectionsStore()->connect(instance, signalId, detail, receiver,
465 notifier, slotHash, closureData, flags);
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";
479 bool disconnect(
void *instance,
const char *signal, Quark detail,
480 void *receiver, uint slotHash, ulong handlerId)
483 GQuark detailQuark = 0;
486 if (g_signal_parse_name(signal, Type::fromInstance(instance),
487 &signalId, &detailQuark, FALSE))
489 if (!detail && detailQuark) {
490 detail = detailQuark;
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";
500 return s_connectionsStore()->disconnect(instance, signalId, detail,
501 receiver, slotHash, handlerId);
509 #include "connect.moc"