00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "connect.h"
00020 #include <glib-object.h>
00021 #include <QtCore/QHash>
00022 #include <QtCore/QMutex>
00023 #include <boost/multi_index_container.hpp>
00024 #include <boost/multi_index/sequenced_index.hpp>
00025 #include <boost/multi_index/ordered_index.hpp>
00026 #include <boost/multi_index/member.hpp>
00027
00028 namespace QGlib {
00029 namespace Private {
00030
00031
00032
00033 static void c_marshaller(GClosure *closure, GValue *returnValue, uint paramValuesCount,
00034 const GValue *paramValues, void *hint, void *data)
00035 {
00036 Q_UNUSED(data);
00037
00038 ClosureDataBase *cdata = static_cast<ClosureDataBase*>(closure->data);
00039
00040 QList<Value> params;
00041
00042
00043 for(uint i = cdata->passSender ? 0 : 1; i<paramValuesCount; ++i) {
00044 params.append(Value(¶mValues[i]));
00045 }
00046
00047 try {
00048 Value result(returnValue);
00049 cdata->marshaller(result, params);
00050
00051 if (returnValue && G_IS_VALUE(returnValue)) {
00052 g_value_copy(result, returnValue);
00053 }
00054 } catch (const std::exception & e) {
00055 QString signalName;
00056 if (hint != NULL) {
00057 GSignalInvocationHint *ihint = static_cast<GSignalInvocationHint*>(hint);
00058
00059 GSignalQuery query;
00060 g_signal_query(ihint->signal_id, &query);
00061 signalName = QString::fromUtf8(query.signal_name);
00062
00063 if (ihint->detail != 0) {
00064 Quark q(ihint->detail);
00065 signalName.append(QLatin1String("::"));
00066 signalName.append(q.toString());
00067 }
00068 }
00069
00070 QString instanceName = params.at(0).get<QString>();
00071
00072
00073 QString msg;
00074 try {
00075
00076 dynamic_cast<const InvalidTypeException &>(e);
00077
00078 msg = QLatin1String("One or more of the arguments of the signal are of different "
00079 "type than the type that the closure expects");
00080 } catch (...) {
00081 try {
00082 dynamic_cast<const InvalidValueException &>(e);
00083
00084
00085
00086 if (returnValue == NULL) {
00087 msg = QLatin1String("The signal is defined to return void but the "
00088 "closure returns something non-void");
00089 } else {
00090 msg = QLatin1String("One of the arguments of the signal was not a valid GValue. "
00091 "This is most likely a bug in the code that invoked the signal.");
00092 }
00093 } catch (...) {
00094 msg = QString::fromAscii(e.what());
00095 }
00096 }
00097
00098 qCritical() << "Error during invocation of closure connected to signal"
00099 << signalName << "from object" << instanceName << ":" << msg;
00100 }
00101 }
00102
00103 static void closureDestroyNotify(void *data, GClosure *closure)
00104 {
00105 Q_UNUSED(data);
00106 delete static_cast<ClosureDataBase*>(closure->data);
00107 }
00108
00109 static inline GClosure *createCppClosure(ClosureDataBase *closureData)
00110 {
00111 GClosure *closure = g_closure_new_simple(sizeof(GClosure), closureData);
00112 g_closure_set_marshal(closure, &c_marshaller);
00113 g_closure_add_finalize_notifier(closure, NULL, &closureDestroyNotify);
00114 g_closure_ref(closure);
00115 g_closure_sink(closure);
00116 return closure;
00117 }
00118
00119
00120
00121
00122 Q_GLOBAL_STATIC(QWeakPointer<DestroyNotifierIface>, s_qobjDestroyNotifier)
00123 Q_GLOBAL_STATIC(QMutex, s_qobjDestroyNotifierMutex)
00124
00125 DestroyNotifierIfacePtr QObjectDestroyNotifier::instance()
00126 {
00127 QMutexLocker l(s_qobjDestroyNotifierMutex());
00128
00129 DestroyNotifierIfacePtr ptr = s_qobjDestroyNotifier()->toStrongRef();
00130 if (!ptr) {
00131 ptr = DestroyNotifierIfacePtr(new QObjectDestroyNotifier);
00132 *s_qobjDestroyNotifier() = ptr;
00133 }
00134 return ptr;
00135 }
00136
00137 bool QObjectDestroyNotifier::connect(void *receiver, QObject *notificationReceiver, const char *slot)
00138 {
00139 QObject *qreceiver = reinterpret_cast<QObject*>(receiver);
00140 return QObject::connect(qreceiver, SIGNAL(destroyed(QObject*)),
00141 notificationReceiver, slot, Qt::DirectConnection);
00142 }
00143
00144 bool QObjectDestroyNotifier::disconnect(void* receiver, QObject *notificationReceiver)
00145 {
00146 QObject *qreceiver = reinterpret_cast<QObject*>(receiver);
00147 return QObject::disconnect(qreceiver, 0, notificationReceiver, 0);
00148 }
00149
00150
00151
00152
00153 class ConnectionsStore : public QObject
00154 {
00155 Q_OBJECT
00156 public:
00157 inline ConnectionsStore() : QObject(), m_handlerIdInRemoval(0) {}
00158
00159 ulong connect(void *instance, uint signal, Quark detail,
00160 void *receiver, const DestroyNotifierIfacePtr & notifier,
00161 uint slotHash, ClosureDataBase *closureData, ConnectFlags flags);
00162
00163 bool disconnect(void *instance, uint signal, Quark detail,
00164 void *receiver, uint slotHash, ulong handlerId);
00165
00166 private:
00167 struct Connection
00168 {
00169 inline Connection(uint signal, Quark detail, void *receiver,
00170 uint slotHash, ulong handlerId)
00171 : signal(signal),
00172 detail(detail),
00173 receiver(receiver),
00174 slotHash(slotHash),
00175 handlerId(handlerId)
00176 {
00177 }
00178
00179 uint signal;
00180 Quark detail;
00181 void *receiver;
00182 uint slotHash;
00183 ulong handlerId;
00184 };
00185
00186 bool lookupAndExec(void *instance, uint signal, Quark detail, void *receiver, uint slotHash,
00187 ulong handlerId, void (ConnectionsStore::*func)(void*, const Connection &));
00188
00189 void disconnectHandler(void *instance, const Connection & c);
00190 void disconnectAndDestroyRcvrWatch(void *instance, const Connection & c);
00191
00192 void setupClosureWatch(void *instance, ulong handlerId, GClosure *closure);
00193 void onClosureDestroyedAction(void *instance, ulong handlerId);
00194 static void onClosureDestroyed(void *data, GClosure *closure);
00195
00196 void setupReceiverWatch(void *instance, void *receiver, const DestroyNotifierIfacePtr & notifier);
00197 void destroyReceiverWatch(void *instance, const Connection & c);
00198
00199 private Q_SLOTS:
00200 void onReceiverDestroyed(void *receiver);
00201 void onReceiverDestroyed(QObject *receiver);
00202
00203 private:
00204
00205 struct sequential {};
00206 struct by_handlerId {};
00207 struct by_signal {};
00208 struct by_receiver {};
00209
00210 typedef boost::multi_index_container<
00211 Connection,
00212 boost::multi_index::indexed_by<
00213 boost::multi_index::sequenced<
00214 boost::multi_index::tag<sequential>
00215 >,
00216 boost::multi_index::ordered_non_unique<
00217 boost::multi_index::tag<by_signal>,
00218 boost::multi_index::member<Connection, uint, &Connection::signal>
00219 >,
00220 boost::multi_index::ordered_non_unique<
00221 boost::multi_index::tag<by_receiver>,
00222 boost::multi_index::member<Connection, void*, &Connection::receiver>
00223 >,
00224 boost::multi_index::ordered_unique<
00225 boost::multi_index::tag<by_handlerId>,
00226 boost::multi_index::member<Connection, ulong, &Connection::handlerId>
00227 >
00228 >
00229 > ConnectionsContainer;
00230
00231 typedef ConnectionsContainer::index<sequential>::type::iterator SequentialIterator;
00232 typedef ConnectionsContainer::index<by_signal>::type::iterator BySignalIterator;
00233 typedef ConnectionsContainer::index<by_receiver>::type::iterator ByReceiverIterator;
00234 typedef ConnectionsContainer::index<by_handlerId>::type::iterator ByHandlerIterator;
00235 typedef std::pair<BySignalIterator, BySignalIterator> BySignalIterators;
00236 typedef std::pair<ByReceiverIterator, ByReceiverIterator> ByReceiverIterators;
00237
00238 struct ReceiverData
00239 {
00240 DestroyNotifierIfacePtr notifier;
00241 QHash<void*, int> senders;
00242 };
00243
00244 QMutex m_mutex;
00245 QHash<void*, ConnectionsContainer> m_connections;
00246 QHash<void*, ReceiverData> m_receivers;
00247
00248 QMutex m_handlerIdInRemovalMutex;
00249 ulong m_handlerIdInRemoval;
00250 };
00251
00252 Q_GLOBAL_STATIC(ConnectionsStore, s_connectionsStore)
00253
00254 ulong ConnectionsStore::connect(void *instance, uint signal, Quark detail,
00255 void *receiver, const DestroyNotifierIfacePtr & notifier,
00256 uint slotHash, ClosureDataBase *closureData, ConnectFlags flags)
00257 {
00258 QMutexLocker l(&m_mutex);
00259 GClosure *closure = createCppClosure(closureData);
00260
00261 ulong handlerId = g_signal_connect_closure_by_id(instance, signal, detail, closure,
00262 (flags & ConnectAfter) ? TRUE : FALSE);
00263
00264 if (handlerId) {
00265 m_connections[instance].get<sequential>().push_back(
00266 Connection(signal, detail, receiver, slotHash, handlerId)
00267 );
00268
00269 setupClosureWatch(instance, handlerId, closure);
00270 setupReceiverWatch(instance, receiver, notifier);
00271 }
00272
00273 g_closure_unref(closure);
00274 return handlerId;
00275 }
00276
00277 bool ConnectionsStore::disconnect(void *instance, uint signal, Quark detail,
00278 void *receiver, uint slotHash, ulong handlerId)
00279 {
00280 QMutexLocker l(&m_mutex);
00281 return lookupAndExec(instance, signal, detail, receiver, slotHash, handlerId,
00282 &ConnectionsStore::disconnectAndDestroyRcvrWatch);
00283 }
00284
00285 bool ConnectionsStore::lookupAndExec(void *instance, uint signal, Quark detail,
00286 void *receiver, uint slotHash, ulong handlerId,
00287 void (ConnectionsStore::*func)(void*, const Connection &))
00288 {
00289 bool executed = false;
00290
00291 if (m_connections.contains(instance)) {
00292 ConnectionsContainer & container = m_connections[instance];
00293
00294 if (handlerId) {
00295 ByHandlerIterator it = container.get<by_handlerId>().find(handlerId);
00296
00297 if (it != container.get<by_handlerId>().end()) {
00298 (this->*func)(instance, *it);
00299 executed = true;
00300
00301 container.get<by_handlerId>().erase(it);
00302 }
00303 } else if (signal) {
00304 BySignalIterators iterators = container.get<by_signal>().equal_range(signal);
00305
00306 while (iterators.first != iterators.second) {
00307 if (!detail ||
00308 (detail == iterators.first->detail &&
00309 (!receiver ||
00310 (receiver == iterators.first->receiver &&
00311 (!slotHash || slotHash == iterators.first->slotHash)
00312 )
00313 )
00314 )
00315 )
00316 {
00317 (this->*func)(instance, *iterators.first);
00318 executed = true;
00319
00320 iterators.first = container.get<by_signal>().erase(iterators.first);
00321 } else {
00322 ++iterators.first;
00323 }
00324 }
00325 } else if (receiver) {
00326 ByReceiverIterators iterators = container.get<by_receiver>().equal_range(receiver);
00327
00328 while (iterators.first != iterators.second) {
00329 if (!slotHash || slotHash == iterators.first->slotHash) {
00330 (this->*func)(instance, *iterators.first);
00331 executed = true;
00332
00333 iterators.first = container.get<by_receiver>().erase(iterators.first);
00334 } else {
00335 ++iterators.first;
00336 }
00337 }
00338 } else {
00339 for (SequentialIterator it = container.get<sequential>().begin();
00340 it != container.get<sequential>().end(); ++it)
00341 {
00342 (this->*func)(instance, *it);
00343 executed = true;
00344 }
00345 container.get<sequential>().clear();
00346 }
00347
00348 if (container.get<sequential>().empty()) {
00349 m_connections.remove(instance);
00350 }
00351 }
00352
00353 return executed;
00354 }
00355
00356 void ConnectionsStore::disconnectHandler(void *instance, const Connection & c)
00357 {
00358 m_handlerIdInRemovalMutex.lock();
00359 m_handlerIdInRemoval = c.handlerId;
00360 m_handlerIdInRemovalMutex.unlock();
00361
00362
00363 g_signal_handler_disconnect(instance, c.handlerId);
00364
00365 m_handlerIdInRemovalMutex.lock();
00366 m_handlerIdInRemoval = 0;
00367 m_handlerIdInRemovalMutex.unlock();
00368 }
00369
00370 void ConnectionsStore::disconnectAndDestroyRcvrWatch(void *instance, const Connection & c)
00371 {
00372 disconnectHandler(instance, c);
00373 destroyReceiverWatch(instance, c);
00374 }
00375
00376 void ConnectionsStore::setupClosureWatch(void *instance, ulong handlerId, GClosure *closure)
00377 {
00378 void *data = new QPair<void*, ulong>(instance, handlerId);
00379 g_closure_add_finalize_notifier(closure, data, &ConnectionsStore::onClosureDestroyed);
00380 }
00381
00382
00383 void ConnectionsStore::onClosureDestroyed(void *data, GClosure *closure)
00384 {
00385 Q_UNUSED(closure);
00386 QPair<void*, ulong> *pair = static_cast< QPair<void*, ulong>* >(data);
00387 s_connectionsStore()->onClosureDestroyedAction(pair->first, pair->second);
00388 delete pair;
00389 }
00390
00391 void ConnectionsStore::onClosureDestroyedAction(void *instance, ulong handlerId)
00392 {
00393
00394 m_handlerIdInRemovalMutex.lock();
00395 register bool ok = (m_handlerIdInRemoval != handlerId);
00396 m_handlerIdInRemovalMutex.unlock();
00397
00398 if (ok) {
00399 QMutexLocker l(&m_mutex);
00400 lookupAndExec(instance, 0, Quark(), 0, 0, handlerId, &ConnectionsStore::destroyReceiverWatch);
00401 }
00402 }
00403
00404 void ConnectionsStore::setupReceiverWatch(void *instance, void *receiver,
00405 const DestroyNotifierIfacePtr & notifier)
00406 {
00407 if (!m_receivers.contains(receiver)) {
00408 ReceiverData data;
00409 data.notifier = notifier;
00410 if (!notifier->connect(receiver, this, SLOT(onReceiverDestroyed(QObject*)))) {
00411 notifier->connect(receiver, this, SLOT(onReceiverDestroyed(void*)));
00412 }
00413 m_receivers.insert(receiver, data);
00414 }
00415
00416 m_receivers[receiver].senders[instance]++;
00417 }
00418
00419 void ConnectionsStore::destroyReceiverWatch(void *instance, const Connection & c)
00420 {
00421 if (--m_receivers[c.receiver].senders[instance] == 0) {
00422 m_receivers[c.receiver].senders.remove(instance);
00423 if (m_receivers[c.receiver].senders.isEmpty()) {
00424 m_receivers[c.receiver].notifier->disconnect(c.receiver, this);
00425 m_receivers.remove(c.receiver);
00426 }
00427 }
00428 }
00429
00430 void ConnectionsStore::onReceiverDestroyed(void *receiver)
00431 {
00432 QMutexLocker l(&m_mutex);
00433 QHashIterator<void*, int> it(m_receivers[receiver].senders);
00434 while (it.hasNext()) {
00435 it.next();
00436 lookupAndExec(it.key(), 0, Quark(), receiver, 0, 0, &ConnectionsStore::disconnectHandler);
00437 }
00438 m_receivers.remove(receiver);
00439 }
00440
00441
00442
00443 void ConnectionsStore::onReceiverDestroyed(QObject *receiver)
00444 {
00445 onReceiverDestroyed(static_cast<void*>(receiver));
00446 }
00447
00448
00449
00450
00451 ulong connect(void *instance, const char *signal, Quark detail,
00452 void *receiver, const DestroyNotifierIfacePtr & notifier,
00453 uint slotHash, ClosureDataBase *closureData, ConnectFlags flags)
00454 {
00455 guint signalId;
00456 GQuark detailQuark;
00457
00458 if (g_signal_parse_name(signal, Type::fromInstance(instance),
00459 &signalId, &detailQuark, FALSE))
00460 {
00461 if (!detail && detailQuark) {
00462 detail = detailQuark;
00463 }
00464 return s_connectionsStore()->connect(instance, signalId, detail, receiver,
00465 notifier, slotHash, closureData, flags);
00466 } else {
00467 qWarning() << "QGlib::connect: Could not parse signal:" << signal
00468 << "- Either it does not exist on this instance, or a detail "
00469 "was specified but the signal is not detailed";
00470 delete closureData;
00471 }
00472
00473 return 0;
00474 }
00475
00476
00477
00478
00479 bool disconnect(void *instance, const char *signal, Quark detail,
00480 void *receiver, uint slotHash, ulong handlerId)
00481 {
00482 guint signalId = 0;
00483 GQuark detailQuark = 0;
00484
00485 if (signal) {
00486 if (g_signal_parse_name(signal, Type::fromInstance(instance),
00487 &signalId, &detailQuark, FALSE))
00488 {
00489 if (!detail && detailQuark) {
00490 detail = detailQuark;
00491 }
00492 } else {
00493 qWarning() << "QGlib::disconnect: Could not parse signal:" << signal
00494 << "- Either it does not exist on this instance, or a detail "
00495 "was specified but the signal is not detailed";
00496 return false;
00497 }
00498 }
00499
00500 return s_connectionsStore()->disconnect(instance, signalId, detail,
00501 receiver, slotHash, handlerId);
00502 }
00503
00504
00505
00506 }
00507 }
00508
00509 #include "connect.moc"