diff --git a/mythtv/libs/libmythbase/mythcorecontext.cpp b/mythtv/libs/libmythbase/mythcorecontext.cpp index 8894b90ae2c..c912fe696f4 100644 --- a/mythtv/libs/libmythbase/mythcorecontext.cpp +++ b/mythtv/libs/libmythbase/mythcorecontext.cpp @@ -90,7 +90,8 @@ class MythCoreContextPrivate : public QObject QMap m_playbackClients; QMutex m_playbackLock; - + bool m_inwanting; + MythPluginManager *m_pluginmanager; }; @@ -1397,6 +1398,9 @@ void MythCoreContext::WaitUntilSignals(const char *signal1, ...) */ void MythCoreContext::RegisterForPlayback(QObject *sender, const char *method) { + if (!sender || !method) + return; + QMutexLocker lock(&d->m_playbackLock); if (!d->m_playbackClients.contains(sender)) @@ -1438,20 +1442,78 @@ void MythCoreContext::WantingPlayback(QObject *sender) QMutexLocker lock(&d->m_playbackLock); QByteArray ba; const char *method = NULL; + d->m_inwanting = true; + + // If any registered client are in the same thread, they will deadlock, so rebuild + // connections for any clients in the same thread as non-blocking connection + QThread *currentThread = QThread::currentThread(); + + QMap::iterator it = d->m_playbackClients.begin(); + for (; it != d->m_playbackClients.end(); ++it) + { + if (it.key() == sender) + continue; // will be done separately, no need to do it again + + QThread *thread = it.key()->thread(); + if (thread != currentThread) + continue; + + disconnect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value()); + connect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value()); + } + + // disconnect sender so it won't receive the message if (d->m_playbackClients.contains(sender)) { ba = d->m_playbackClients.value(sender); method = ba.constData(); disconnect(this, SIGNAL(TVPlaybackAboutToStart()), sender, method); } + + // emit signal emit TVPlaybackAboutToStart(); + + // reconnect sender if (method) { connect(this, SIGNAL(TVPlaybackAboutToStart()), sender, method, Qt::BlockingQueuedConnection); } + // Restore blocking connections + for (; it != d->m_playbackClients.end(); ++it) + { + if (it.key() == sender) + continue; // already done above, no need to do it again + + QThread *thread = it.key()->thread(); + + if (thread != currentThread) + continue; + + disconnect(this, SIGNAL(TVPlaybackAboutToStart()), it.key(), it.value()); + connect(this, SIGNAL(TVPlaybackAboutToStart()), + it.key(), it.value(), Qt::BlockingQueuedConnection); + } + d->m_inwanting = false; +} + +/** + * Returns true if a client has requested playback. + * this can be used when one of the TVPlayback* is emitted to find out if you + * can assume playback has stopped + */ +bool MythCoreContext::InWantingPlayback(void) +{ + bool locked = d->m_playbackLock.tryLock(); + + if (!locked && d->m_inwanting) + return true; // we're in the middle of WantingPlayback + if (!locked) + return false; + d->m_playbackLock.unlock(); + return false; } bool MythCoreContext::TestPluginVersion(const QString &name, diff --git a/mythtv/libs/libmythbase/mythcorecontext.h b/mythtv/libs/libmythbase/mythcorecontext.h index c518189e765..70ab7fd64bb 100644 --- a/mythtv/libs/libmythbase/mythcorecontext.h +++ b/mythtv/libs/libmythbase/mythcorecontext.h @@ -176,6 +176,7 @@ class MBASE_PUBLIC MythCoreContext : public QObject, public MythObservable, publ void RegisterForPlayback(QObject *sender, const char *method); void UnregisterForPlayback(QObject *sender); void WantingPlayback(QObject *sender); + bool InWantingPlayback(void); // Plugin related methods bool TestPluginVersion(const QString &name, const QString &libversion,