QtGStreamer  0.10.2
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator
connect.cpp
00001 /*
00002     Copyright (C) 2010 George Kiagiadakis <kiagiadakis.george@gmail.com>
00003     Copyright (C) 2010 Collabora Ltd.
00004       @author George Kiagiadakis <george.kiagiadakis@collabora.co.uk>
00005 
00006     This library is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU Lesser General Public License as published
00008     by the Free Software Foundation; either version 2.1 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014     GNU Lesser General Public License for more details.
00015 
00016     You should have received a copy of the GNU Lesser General Public License
00017     along with this program.  If not, see <http://www.gnu.org/licenses/>.
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 //BEGIN ******** Closure internals ********
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     //the signal sender is always the first argument. if we are instructed not to pass it
00042     //as an argument to the slot, begin converting from paramValues[1]
00043     for(uint i = cdata->passSender ? 0 : 1; i<paramValuesCount; ++i) {
00044         params.append(Value(&paramValues[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         //attempt to determine the cause of the failure
00073         QString msg;
00074         try {
00075             //dynamic_cast will throw an std::bad_cast if it fails
00076             dynamic_cast<const InvalidTypeException &>(e);
00077             //cast succeded, e is indeed an InvalidTypeException
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                 //cast succeded, e is indeed an InvalidValueException
00084                 //this is most likely to happen because the signal returns void
00085                 //but the closure returns something non-void. check this first.
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 //END ******** Closure internals ********
00120 //BEGIN ******** QObjectDestroyNotifier ********
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 //END ******** QObjectDestroyNotifier ********
00151 //BEGIN ******** ConnectionsStore ********
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     //tags
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; //<sender, refcount>
00242     };
00243 
00244     QMutex m_mutex;
00245     QHash<void*, ConnectionsContainer> m_connections; // <sender, connections>
00246     QHash<void*, ReceiverData> m_receivers; // <receiver, data>
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     /* This will unref the closure and cause onClosureDestroyed to be invoked. */
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 //static
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     /* Do not do any action if we are being invoked from disconnectHandler() */
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 //optimization hack, to avoid making QObjectDestroyNotifier inherit
00442 //QObject and re-emit QObject::destroyed() with void* argument
00443 void ConnectionsStore::onReceiverDestroyed(QObject *receiver)
00444 {
00445     onReceiverDestroyed(static_cast<void*>(receiver));
00446 }
00447 
00448 //END ******** ConnectionsStore ********
00449 //BEGIN ******** connect ********
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 //END ******** connect ********
00477 //BEGIN ******** disconnect ********
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 //END ******** disconnect ********
00505 
00506 } //namespace Private
00507 } //namespace QGlib
00508 
00509 #include "connect.moc"