Permalink
Browse files

Make WantingPlayback work when more than one registered client exists…

… in the same thread.

Connection to registered playback clients are created using Qt::BlockingQueuedConnection, which will deadlock if running within the same thread.
Add InWantingPlayback methods, to determine if interrupted signal was received while in the process of starting new playback

Part of fix for #11671
  • Loading branch information...
jyavenard committed Jul 14, 2013
1 parent 1bc8169 commit 22e5fb0e68a783e455fd4a9fbbdbd5be1820238c
Showing with 64 additions and 1 deletion.
  1. +63 −1 mythtv/libs/libmythbase/mythcorecontext.cpp
  2. +1 −0 mythtv/libs/libmythbase/mythcorecontext.h
@@ -90,7 +90,8 @@ class MythCoreContextPrivate : public QObject
QMap<QObject *, QByteArray> 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<QObject *, QByteArray>::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,
@@ -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,

0 comments on commit 22e5fb0

Please sign in to comment.