From cba520a9d9c98b6715e57cc558ecc9e3a4749ecd Mon Sep 17 00:00:00 2001
From: Richard <peper03@yahoo.com>
Date: Fri, 16 Nov 2012 00:03:39 +0100
Subject: [PATCH] Added 'SafeConnectToMasterServer' to CoreContext to provide
 a defined, thread-safe way to connect to the master server.

---
 mythtv/libs/libmythbase/mythcorecontext.cpp        |   81 ++++++++++++++++++++
 mythtv/libs/libmythbase/mythcorecontext.h          |    6 ++
 .../mythfrontend/backendconnectionmanager.cpp      |    2 +-
 3 files changed, 88 insertions(+), 1 deletion(-)

diff --git a/mythtv/libs/libmythbase/mythcorecontext.cpp b/mythtv/libs/libmythbase/mythcorecontext.cpp
index 1e15dfb..873db89 100644
--- a/mythtv/libs/libmythbase/mythcorecontext.cpp
+++ b/mythtv/libs/libmythbase/mythcorecontext.cpp
@@ -87,6 +87,9 @@ class MythCoreContextPrivate : public QObject
     MythScheduler *m_scheduler;
 
     bool m_blockingClient;
+
+    QMap<QObject *, QByteArray> m_playbackClients;
+    QMutex m_playbackLock;
 };
 
 MythCoreContextPrivate::MythCoreContextPrivate(MythCoreContext *lparent,
@@ -283,6 +286,17 @@ bool MythCoreContext::SetupCommandSocket(MythSocket *serverSock,
     return true;
 }
 
+// Connects to master server safely (i.e. by taking m_sockLock)
+bool MythCoreContext::SafeConnectToMasterServer(bool openEventSocket)
+{
+    QMutexLocker locker(&d->m_sockLock);
+    if (!d->m_serverSock)
+    {
+        bool blockingClient = GetNumSetting("idleTimeoutSecs",0) > 0;
+        ConnectToMasterServer(blockingClient, openEventSocket);
+    }
+}
+
 // Assumes that either m_sockLock is held, or the app is still single
 // threaded (i.e. during startup).
 bool MythCoreContext::ConnectToMasterServer(bool blockingClient,
@@ -1364,4 +1378,71 @@ void MythCoreContext::WaitUntilSignals(const char *signal1, ...)
     eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
 }
 
+/**
+ * \fn void MythCoreContext::RegisterForPlayback(QObject *sender, const char *method)
+ * Register sender for TVPlaybackAboutToStart signal. Method will be called upon
+ * the signal being emitted.
+ * sender must call MythCoreContext::UnregisterForPlayback upon deletion
+ */
+void MythCoreContext::RegisterForPlayback(QObject *sender, const char *method)
+{
+    QMutexLocker lock(&d->m_playbackLock);
+
+    if (!d->m_playbackClients.contains(sender))
+    {
+        d->m_playbackClients.insert(sender, QByteArray(method));
+        connect(this, SIGNAL(TVPlaybackAboutToStart()),
+                sender, method,
+                Qt::BlockingQueuedConnection);
+    }
+}
+
+/**
+ * \fn void MythCoreContext::UnregisterForPlayback(QObject *sender)
+ * Unregister sender from being called when TVPlaybackAboutToStart signal
+ * is emitted
+ */
+void MythCoreContext::UnregisterForPlayback(QObject *sender)
+{
+    QMutexLocker lock(&d->m_playbackLock);
+
+    if (d->m_playbackClients.contains(sender))
+    {
+        QByteArray ba = d->m_playbackClients.value(sender);
+        const char *method = ba.constData();
+        disconnect(this, SIGNAL(TVPlaybackAboutToStart()),
+                   sender, method);
+        d->m_playbackClients.remove(sender);
+    }
+}
+
+/**
+ * \fn void MythCoreContext::WantingPlayback(QObject *sender)
+ * All the objects that have registered using MythCoreContext::RegisterForPlayback
+ * but sender will be called. The objet's registered method will be called
+ * in a blocking fashion, each of them being called one after the other
+ */
+void MythCoreContext::WantingPlayback(QObject *sender)
+{
+    QMutexLocker lock(&d->m_playbackLock);
+    bool found = false;
+    QByteArray ba;
+    const char *method;
+
+    if (d->m_playbackClients.contains(sender))
+    {
+        found = true;
+        ba = d->m_playbackClients.value(sender);
+        method = ba.constData();
+        disconnect(this, SIGNAL(TVPlaybackAboutToStart()), sender, method);
+    }
+    emit TVPlaybackAboutToStart();
+    if (found)
+    {
+        connect(this, SIGNAL(TVPlaybackAboutToStart()),
+                sender, method,
+                Qt::BlockingQueuedConnection);
+    }
+}
+
 /* vim: set expandtab tabstop=4 shiftwidth=4: */
diff --git a/mythtv/libs/libmythbase/mythcorecontext.h b/mythtv/libs/libmythbase/mythcorecontext.h
index 5cfbc01..0bd98ce 100644
--- a/mythtv/libs/libmythbase/mythcorecontext.h
+++ b/mythtv/libs/libmythbase/mythcorecontext.h
@@ -59,6 +59,7 @@ class MBASE_PUBLIC MythCoreContext : public QObject, public MythObservable, publ
     void SetEventSocket(MythSocket *eventSock);
     void SetScheduler(MythScheduler *sched);
 
+    bool SafeConnectToMasterServer(bool openEventSocket = true);
     bool ConnectToMasterServer(bool blockingClient = true,
                                bool openEventSocket = true);
 
@@ -169,6 +170,10 @@ class MBASE_PUBLIC MythCoreContext : public QObject, public MythObservable, publ
     QString GetLanguageAndVariant(void);
     void ResetLanguage(void);
 
+    void RegisterForPlayback(QObject *sender, const char *method);
+    void UnregisterForPlayback(QObject *sender);
+    void WantingPlayback(QObject *sender);
+
     // signal related methods
     void WaitUntilSignals(const char *signal1, ...);
     void emitTVPlaybackStarted(void)            { emit TVPlaybackStarted(); }
@@ -185,6 +190,7 @@ class MBASE_PUBLIC MythCoreContext : public QObject, public MythObservable, publ
     void TVPlaybackPaused(void);
     void TVPlaybackUnpaused(void);
     void TVPlaybackAborted(void);
+    void TVPlaybackAboutToStart(void);
 
   private:
     MythCoreContextPrivate *d;
diff --git a/mythtv/programs/mythfrontend/backendconnectionmanager.cpp b/mythtv/programs/mythfrontend/backendconnectionmanager.cpp
index 5843e48..8545912 100644
--- a/mythtv/programs/mythfrontend/backendconnectionmanager.cpp
+++ b/mythtv/programs/mythfrontend/backendconnectionmanager.cpp
@@ -25,7 +25,7 @@ class Reconnect : public QRunnable
 
     virtual void run(void)
     {
-        if (gCoreContext->GetMasterHostPrefix().isEmpty())
+        if (gCoreContext->SafeConnectToMasterServer())
             gCoreContext->dispatch(MythEvent(QString("RECONNECT_FAILURE")));
         else
             gCoreContext->dispatch(MythEvent(QString("RECONNECT_SUCCESS")));
-- 
1.7.9.5

