QtGStreamer 0.10.1
|
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 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(¶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 //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"