From 56a3996fe65f30cbd8c9712a858b27577a3d3000 Mon Sep 17 00:00:00 2001 From: Kai Sommerfeld Date: Fri, 28 Aug 2020 23:26:21 +0200 Subject: [PATCH 1/4] 7.1.4: Fix deadlock while reconnecting to backend after a backend response timeout (Fixes #465) --- pvr.hts/addon.xml.in | 2 +- pvr.hts/changelog.txt | 3 ++ src/Tvheadend.cpp | 76 +++++++++++++++++----------- src/Tvheadend.h | 1 + src/tvheadend/AutoRecordings.cpp | 2 +- src/tvheadend/AutoRecordings.h | 2 +- src/tvheadend/HTSPConnection.cpp | 10 ++-- src/tvheadend/HTSPDemuxer.cpp | 4 +- src/tvheadend/HTSPDemuxer.h | 2 +- src/tvheadend/HTSPVFS.cpp | 2 +- src/tvheadend/HTSPVFS.h | 2 +- src/tvheadend/TimeRecordings.cpp | 2 +- src/tvheadend/TimeRecordings.h | 2 +- src/tvheadend/utilities/AsyncState.h | 9 ++-- 14 files changed, 72 insertions(+), 47 deletions(-) diff --git a/pvr.hts/addon.xml.in b/pvr.hts/addon.xml.in index 471c7894..1f4dcda5 100644 --- a/pvr.hts/addon.xml.in +++ b/pvr.hts/addon.xml.in @@ -1,7 +1,7 @@ @ADDON_DEPENDS@ diff --git a/pvr.hts/changelog.txt b/pvr.hts/changelog.txt index ada85c3f..1996334b 100644 --- a/pvr.hts/changelog.txt +++ b/pvr.hts/changelog.txt @@ -1,3 +1,6 @@ +7.1.4 +- Fix deadlock while reconnecting to backend after a backend response timeout (Fixes #465) + 7.1.3 - Withdraw v7.1.2 due to new deadlocks it introduces, for example during initial connect to backend. diff --git a/src/Tvheadend.cpp b/src/Tvheadend.cpp index 0228cfff..127d4f23 100644 --- a/src/Tvheadend.cpp +++ b/src/Tvheadend.cpp @@ -1493,31 +1493,8 @@ void CTvheadend::Disconnected() bool CTvheadend::Connected() { - /* Rebuild state */ - for (auto* dmx : m_dmx) - dmx->Connected(); - - m_vfs->Connected(); - m_timeRecordings.Connected(); - m_autoRecordings.Connected(); - - /* Flag all async fields in case they've been deleted */ - for (auto& entry : m_channels) - entry.second.SetDirty(true); - for (auto& entry : m_tags) - entry.second.SetDirty(true); - for (auto& entry : m_schedules) - entry.second.SetDirty(true); - - { - CLockObject lock(m_mutex); - - for (auto& entry : m_recordings) - entry.second.SetDirty(true); - } - - /* Request Async data, first is channels */ - m_asyncState.SetState(ASYNC_CHN); + /* Request Async data, first is init (which rebuilds state) */ + m_asyncState.SetState(ASYNC_INIT); htsmsg_t* msg = htsmsg_create_map(); if (Settings::GetInstance().GetAsyncEpg()) @@ -1902,6 +1879,7 @@ void CTvheadend::SyncCompleted() Logger::Log(LogLevel::LEVEL_INFO, "async updates initialised"); /* The complete calls are probably redundant, but its a safety feature */ + SyncInitCompleted(); SyncChannelsCompleted(); SyncDvrCompleted(); SyncEpgCompleted(); @@ -1926,6 +1904,34 @@ void CTvheadend::SyncCompleted() } } +void CTvheadend::SyncInitCompleted() +{ + /* check state engine */ + if (m_asyncState.GetState() != ASYNC_INIT) + return; + + /* Rebuild state */ + for (auto* dmx : m_dmx) + dmx->RebuildState(); + + m_vfs->RebuildState(); + m_timeRecordings.RebuildState(); + m_autoRecordings.RebuildState(); + + /* Flag all async fields in case they've been deleted */ + for (auto& entry : m_channels) + entry.second.SetDirty(true); + for (auto& entry : m_tags) + entry.second.SetDirty(true); + for (auto& entry : m_schedules) + entry.second.SetDirty(true); + for (auto& entry : m_recordings) + entry.second.SetDirty(true); + + /* Next */ + m_asyncState.SetState(ASYNC_CHN); +} + void CTvheadend::SyncChannelsCompleted() { /* check state engine */ @@ -1987,16 +1993,15 @@ void CTvheadend::SyncDvrCompleted() void CTvheadend::SyncEpgCompleted() { /* check state engine */ + if (m_asyncState.GetState() != ASYNC_EPG) + return; + if (!Settings::GetInstance().GetAsyncEpg()) { m_asyncState.SetState(ASYNC_DONE); return; } - /* check state engine */ - if (m_asyncState.GetState() != ASYNC_EPG) - return; - /* Schedules */ std::vector> deletedEvents; utilities::erase_if(m_schedules, [&](const ScheduleMapEntry& entry) { @@ -2040,6 +2045,9 @@ void CTvheadend::SyncEpgCompleted() void CTvheadend::ParseTagAddOrUpdate(htsmsg_t* msg, bool bAdd) { + /* Rebuild state upon arrival of first async data */ + SyncInitCompleted(); + /* Validate */ uint32_t u32 = 0; if (htsmsg_get_u32(msg, "tagId", &u32)) @@ -2120,6 +2128,8 @@ void CTvheadend::ParseTagDelete(htsmsg_t* msg) void CTvheadend::ParseChannelAddOrUpdate(htsmsg_t* msg, bool bAdd) { + /* Rebuild state upon arrival of first async data */ + SyncInitCompleted(); /* Validate */ uint32_t u32 = 0; @@ -2243,7 +2253,10 @@ void CTvheadend::ParseChannelDelete(htsmsg_t* msg) void CTvheadend::ParseRecordingAddOrUpdate(htsmsg_t* msg, bool bAdd) { - /* Channels must be complete */ + /* Rebuild state upon arrival of first async data */ + SyncInitCompleted(); + + /* Channels complete */ SyncChannelsCompleted(); /* Validate */ @@ -2637,6 +2650,9 @@ void CTvheadend::ParseRecordingDelete(htsmsg_t* msg) bool CTvheadend::ParseEvent(htsmsg_t* msg, bool bAdd, Event& evt) { + /* Rebuild state upon arrival of first async data */ + SyncInitCompleted(); + /* Recordings complete */ SyncDvrCompleted(); diff --git a/src/Tvheadend.h b/src/Tvheadend.h index 14ece52d..eeff856c 100644 --- a/src/Tvheadend.h +++ b/src/Tvheadend.h @@ -174,6 +174,7 @@ class ATTRIBUTE_HIDDEN CTvheadend : public kodi::addon::CInstancePVRClient, /* * Channel/Tags/Recordings/Events */ + void SyncInitCompleted(); void SyncChannelsCompleted(); void SyncDvrCompleted(); void SyncEpgCompleted(); diff --git a/src/tvheadend/AutoRecordings.cpp b/src/tvheadend/AutoRecordings.cpp index 3e8f641b..0f5e2b5c 100644 --- a/src/tvheadend/AutoRecordings.cpp +++ b/src/tvheadend/AutoRecordings.cpp @@ -31,7 +31,7 @@ AutoRecordings::~AutoRecordings() { } -void AutoRecordings::Connected() +void AutoRecordings::RebuildState() { /* Flag all async fields in case they've been deleted */ for (auto& rec : m_autoRecordings) diff --git a/src/tvheadend/AutoRecordings.h b/src/tvheadend/AutoRecordings.h index 9c0fb559..77e9b1ea 100644 --- a/src/tvheadend/AutoRecordings.h +++ b/src/tvheadend/AutoRecordings.h @@ -33,7 +33,7 @@ class AutoRecordings ~AutoRecordings(); /* state updates */ - void Connected(); + void RebuildState(); void SyncDvrCompleted(); /* data access */ diff --git a/src/tvheadend/HTSPConnection.cpp b/src/tvheadend/HTSPConnection.cpp index ce55afb2..15a4e6ba 100644 --- a/src/tvheadend/HTSPConnection.cpp +++ b/src/tvheadend/HTSPConnection.cpp @@ -325,6 +325,7 @@ bool HTSPConnection::ReadMessage() htsmsg_destroy(msg); return true; } + Logger::Log(LogLevel::LEVEL_TRACE, "receive message [%s]", method); /* Pass (if return is true, message is finished) */ @@ -365,7 +366,8 @@ bool HTSPConnection::SendMessage0(const char* method, htsmsg_t* msg) if (c != static_cast(len)) { - Logger::Log(LogLevel::LEVEL_ERROR, "failed to write (%s)", m_socket->GetError().c_str()); + Logger::Log(LogLevel::LEVEL_ERROR, "Command %s failed: failed to write (%s)", method, + m_socket->GetError().c_str()); if (!m_suspended) Disconnect(); @@ -394,7 +396,7 @@ htsmsg_t* HTSPConnection::SendAndWait0(const char* method, htsmsg_t* msg, int iR if (!SendMessage0(method, msg)) { m_messages.erase(seq); - Logger::Log(LogLevel::LEVEL_ERROR, "failed to transmit"); + Logger::Log(LogLevel::LEVEL_ERROR, "Command %s failed: failed to transmit", method); return nullptr; } @@ -581,7 +583,6 @@ void HTSPConnection::Register() /* Send Auth */ Logger::Log(LogLevel::LEVEL_DEBUG, "sending auth"); - if (!SendAuth(user, pass)) { SetState(PVR_CONNECTION_STATE_ACCESS_DENIED); @@ -595,6 +596,7 @@ void HTSPConnection::Register() Logger::Log(LogLevel::LEVEL_DEBUG, "registered"); SetState(PVR_CONNECTION_STATE_CONNECTED); + m_ready = true; m_regCond.Broadcast(); return; @@ -603,7 +605,7 @@ void HTSPConnection::Register() fail: if (!m_suspended) { - /* Don't immediately reconnect (spare server CPU cycles)*/ + /* Don't immediately reconnect (spare server CPU cycles) */ Sleep(SLOW_RECONNECT_INTERVAL); Disconnect(); } diff --git a/src/tvheadend/HTSPDemuxer.cpp b/src/tvheadend/HTSPDemuxer.cpp index 0b9dc048..8e578706 100644 --- a/src/tvheadend/HTSPDemuxer.cpp +++ b/src/tvheadend/HTSPDemuxer.cpp @@ -49,12 +49,14 @@ HTSPDemuxer::~HTSPDemuxer() { } -void HTSPDemuxer::Connected() +void HTSPDemuxer::RebuildState() { /* Re-subscribe */ if (m_subscription.IsActive()) { Logger::Log(LogLevel::LEVEL_DEBUG, "demux re-starting stream"); + + CLockObject lock(m_conn.Mutex()); m_subscription.SendSubscribe(0, 0, true); m_subscription.SendSpeed(0, true); diff --git a/src/tvheadend/HTSPDemuxer.h b/src/tvheadend/HTSPDemuxer.h index 98adf587..9a3071a7 100644 --- a/src/tvheadend/HTSPDemuxer.h +++ b/src/tvheadend/HTSPDemuxer.h @@ -46,7 +46,7 @@ class HTSPDemuxer ~HTSPDemuxer(); bool ProcessMessage(const std::string& method, htsmsg_t* m); - void Connected(); + void RebuildState(); bool Open(uint32_t channelId, tvheadend::eSubscriptionWeight weight = tvheadend::SUBSCRIPTION_WEIGHT_NORMAL); diff --git a/src/tvheadend/HTSPVFS.cpp b/src/tvheadend/HTSPVFS.cpp index ceb8ee0d..77dca113 100644 --- a/src/tvheadend/HTSPVFS.cpp +++ b/src/tvheadend/HTSPVFS.cpp @@ -47,7 +47,7 @@ HTSPVFS::~HTSPVFS() { } -void HTSPVFS::Connected() +void HTSPVFS::RebuildState() { /* Re-open */ if (m_fileId != 0) diff --git a/src/tvheadend/HTSPVFS.h b/src/tvheadend/HTSPVFS.h index c44ccbcd..96ba8d8f 100644 --- a/src/tvheadend/HTSPVFS.h +++ b/src/tvheadend/HTSPVFS.h @@ -34,7 +34,7 @@ class HTSPVFS HTSPVFS(HTSPConnection& conn); ~HTSPVFS(); - void Connected(); + void RebuildState(); bool Open(const kodi::addon::PVRRecording& rec); void Close(); diff --git a/src/tvheadend/TimeRecordings.cpp b/src/tvheadend/TimeRecordings.cpp index 74427386..2c979bd8 100644 --- a/src/tvheadend/TimeRecordings.cpp +++ b/src/tvheadend/TimeRecordings.cpp @@ -30,7 +30,7 @@ TimeRecordings::~TimeRecordings() { } -void TimeRecordings::Connected() +void TimeRecordings::RebuildState() { /* Flag all async fields in case they've been deleted */ for (auto& rec : m_timeRecordings) diff --git a/src/tvheadend/TimeRecordings.h b/src/tvheadend/TimeRecordings.h index 68a82806..bb492344 100644 --- a/src/tvheadend/TimeRecordings.h +++ b/src/tvheadend/TimeRecordings.h @@ -33,7 +33,7 @@ class TimeRecordings ~TimeRecordings(); /* state updates */ - void Connected(); + void RebuildState(); void SyncDvrCompleted(); /* data access */ diff --git a/src/tvheadend/utilities/AsyncState.h b/src/tvheadend/utilities/AsyncState.h index 93e32339..9d3a8ce8 100644 --- a/src/tvheadend/utilities/AsyncState.h +++ b/src/tvheadend/utilities/AsyncState.h @@ -21,10 +21,11 @@ namespace utilities enum eAsyncState { ASYNC_NONE = 0, - ASYNC_CHN = 1, - ASYNC_DVR = 2, - ASYNC_EPG = 3, - ASYNC_DONE = 4 + ASYNC_INIT = 1, + ASYNC_CHN = 2, + ASYNC_DVR = 3, + ASYNC_EPG = 4, + ASYNC_DONE = 5 }; /** From a61cf5e15d924abcfedaafe5d6e7532fc49da9ea Mon Sep 17 00:00:00 2001 From: Kai Sommerfeld Date: Sat, 29 Aug 2020 23:26:43 +0200 Subject: [PATCH 2/4] 7.1.4: Refactor subscription seek code --- pvr.hts/changelog.txt | 1 + src/tvheadend/HTSPDemuxer.cpp | 75 ++++++++++++++++++++++++---------- src/tvheadend/HTSPDemuxer.h | 5 +-- src/tvheadend/Subscription.cpp | 15 +++---- 4 files changed, 61 insertions(+), 35 deletions(-) diff --git a/pvr.hts/changelog.txt b/pvr.hts/changelog.txt index 1996334b..80eae1c9 100644 --- a/pvr.hts/changelog.txt +++ b/pvr.hts/changelog.txt @@ -1,5 +1,6 @@ 7.1.4 - Fix deadlock while reconnecting to backend after a backend response timeout (Fixes #465) +- Refactor subscription seek code 7.1.3 - Withdraw v7.1.2 due to new deadlocks it introduces, for example during initial connect to backend. diff --git a/src/tvheadend/HTSPDemuxer.cpp b/src/tvheadend/HTSPDemuxer.cpp index 8e578706..c729fcf1 100644 --- a/src/tvheadend/HTSPDemuxer.cpp +++ b/src/tvheadend/HTSPDemuxer.cpp @@ -35,8 +35,7 @@ using namespace tvheadend::utilities; HTSPDemuxer::HTSPDemuxer(IHTSPDemuxPacketHandler& demuxPktHdl, HTSPConnection& conn) : m_conn(conn), m_pktBuffer(static_cast(-1)), - m_seekTime(INVALID_SEEKTIME), - m_seeking(false), + m_seektime(nullptr), m_subscription(conn), m_lastUse(0), m_startTime(0), @@ -85,7 +84,7 @@ void HTSPDemuxer::Abort0() m_streams.clear(); m_streamStat.clear(); m_rdsIdx = 0; - m_seeking = false; + m_seektime = nullptr; } @@ -166,36 +165,68 @@ void HTSPDemuxer::Abort() ResetStatus(); } +namespace tvheadend +{ + +class SubscriptionSeekTime +{ +public: + SubscriptionSeekTime() = default; + + ~SubscriptionSeekTime() + { + Set(INVALID_SEEKTIME); // ensure signal is sent + } + + int64_t Get(CMutex& mutex, uint32_t timeout) + { + m_cond.Wait(mutex, m_flag, timeout); + m_flag = false; + return m_seektime; + } + + void Set(int64_t seektime) + { + m_seektime = seektime; + m_flag = true; + m_cond.Broadcast(); + } + +private: + CCondition m_cond; + bool m_flag = false; + int64_t m_seektime = INVALID_SEEKTIME; +}; + +} // namespace tvheadend + bool HTSPDemuxer::Seek(double time, bool, double& startpts) { + CLockObject lock(m_conn.Mutex()); + if (!m_subscription.IsActive()) return false; - m_seekTime = 0; - m_seeking = true; + SubscriptionSeekTime seekTime; + m_seektime = &seekTime; + if (!m_subscription.SendSeek(time)) - { - m_seeking = false; return false; - } /* Wait for time */ - CLockObject lock(m_conn.Mutex()); + int64_t seekedTo = + (*m_seektime).Get(m_conn.Mutex(), Settings::GetInstance().GetResponseTimeout()); + m_seektime = nullptr; - if (!m_seekCond.Wait(m_conn.Mutex(), m_seekTime, Settings::GetInstance().GetResponseTimeout())) + if (seekedTo == INVALID_SEEKTIME) { Logger::Log(LogLevel::LEVEL_ERROR, "failed to get subscriptionSeek response"); - m_seeking = false; Flush(); /* try to resync */ return false; } - m_seeking = false; - if (m_seekTime == INVALID_SEEKTIME) - return false; - /* Store */ - startpts = TVH_TO_DVD_TIME(m_seekTime - 1); + startpts = TVH_TO_DVD_TIME(seekedTo); Logger::Log(LogLevel::LEVEL_TRACE, "demux seek startpts = %lf", startpts); return true; @@ -526,7 +557,7 @@ void HTSPDemuxer::ParseMuxPacket(htsmsg_t* m) if (!type) type = '_'; - bool ignore = m_seeking; + bool ignore = m_seektime != nullptr; Logger::Log(LogLevel::LEVEL_TRACE, "demux pkt idx %d:%d type %c pts %lf len %lld%s", idx, pkt->iStreamId, type, pkt->pts, static_cast(binlen), @@ -792,25 +823,25 @@ void HTSPDemuxer::ParseSourceInfo(htsmsg_t* m) void HTSPDemuxer::ParseSubscriptionStop(htsmsg_t*) { - //CLockObject lock(m_mutex); } void HTSPDemuxer::ParseSubscriptionSkip(htsmsg_t* m) { CLockObject lock(m_conn.Mutex()); + if (m_seektime == nullptr) + return; + int64_t s64 = 0; if (htsmsg_get_s64(m, "time", &s64)) { - m_seekTime = INVALID_SEEKTIME; + (*m_seektime).Set(INVALID_SEEKTIME); } else { - m_seekTime = s64 < 0 ? 1 : s64 + 1; /* it must not be zero! */ + (*m_seektime).Set(s64 < 0 ? 0 : s64); Flush(); /* flush old packets (with wrong pts) */ } - m_seeking = false; - m_seekCond.Broadcast(); } void HTSPDemuxer::ParseSubscriptionSpeed(htsmsg_t* m) diff --git a/src/tvheadend/HTSPDemuxer.h b/src/tvheadend/HTSPDemuxer.h index 9a3071a7..bffd37ae 100644 --- a/src/tvheadend/HTSPDemuxer.h +++ b/src/tvheadend/HTSPDemuxer.h @@ -35,6 +35,7 @@ namespace tvheadend { class HTSPConnection; +class SubscriptionSeekTime; /* * HTSP Demuxer - live streams @@ -110,9 +111,7 @@ class HTSPDemuxer P8PLATFORM::SyncedBuffer m_pktBuffer; std::vector m_streams; std::map m_streamStat; - int64_t m_seekTime; - P8PLATFORM::CCondition m_seekCond; - bool m_seeking; + std::atomic m_seektime; tvheadend::status::SourceInfo m_sourceInfo; tvheadend::status::Quality m_signalInfo; tvheadend::status::TimeshiftStatus m_timeshiftStatus; diff --git a/src/tvheadend/Subscription.cpp b/src/tvheadend/Subscription.cpp index e2068c46..3853355a 100644 --- a/src/tvheadend/Subscription.cpp +++ b/src/tvheadend/Subscription.cpp @@ -180,17 +180,12 @@ bool Subscription::SendSeek(double time) Logger::Log(LogLevel::LEVEL_DEBUG, "demux send seek %d", time); /* Send and Wait */ - { - CLockObject lock(m_conn.Mutex()); - m = m_conn.SendAndWait("subscriptionSeek", m); - } - if (m) - { - htsmsg_destroy(m); - return true; - } + m = m_conn.SendAndWait("subscriptionSeek", m); + if (!m) + return false; - return false; + htsmsg_destroy(m); + return true; } void Subscription::SendSpeed(int32_t speed, bool restart) From 1428d019842b844016e0989fd82dcaa6f3767139 Mon Sep 17 00:00:00 2001 From: Kai Sommerfeld Date: Sun, 30 Aug 2020 00:55:04 +0200 Subject: [PATCH 3/4] 7.1.4: Refactor subscription weight code --- pvr.hts/changelog.txt | 1 + src/tvheadend/HTSPDemuxer.cpp | 2 ++ src/tvheadend/Subscription.cpp | 5 +---- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pvr.hts/changelog.txt b/pvr.hts/changelog.txt index 80eae1c9..2f8231d6 100644 --- a/pvr.hts/changelog.txt +++ b/pvr.hts/changelog.txt @@ -1,6 +1,7 @@ 7.1.4 - Fix deadlock while reconnecting to backend after a backend response timeout (Fixes #465) - Refactor subscription seek code +- Refactor subscription weight code 7.1.3 - Withdraw v7.1.2 due to new deadlocks it introduces, for example during initial connect to backend. diff --git a/src/tvheadend/HTSPDemuxer.cpp b/src/tvheadend/HTSPDemuxer.cpp index c729fcf1..1e9be18d 100644 --- a/src/tvheadend/HTSPDemuxer.cpp +++ b/src/tvheadend/HTSPDemuxer.cpp @@ -269,6 +269,8 @@ void HTSPDemuxer::FillBuffer(bool mode) void HTSPDemuxer::Weight(enum eSubscriptionWeight weight) { + CLockObject lock(m_conn.Mutex()); + if (!m_subscription.IsActive() || m_subscription.GetWeight() == static_cast(weight)) return; diff --git a/src/tvheadend/Subscription.cpp b/src/tvheadend/Subscription.cpp index 3853355a..16a8e2a7 100644 --- a/src/tvheadend/Subscription.cpp +++ b/src/tvheadend/Subscription.cpp @@ -221,10 +221,7 @@ void Subscription::SendWeight(uint32_t weight) Logger::Log(LogLevel::LEVEL_DEBUG, "demux send weight %u", GetWeight()); /* Send and Wait */ - { - CLockObject lock(m_conn.Mutex()); - m = m_conn.SendAndWait("subscriptionChangeWeight", m); - } + m = m_conn.SendAndWait("subscriptionChangeWeight", m); if (m) htsmsg_destroy(m); } From 41830a4ce14695b07ac368660358b7e243303471 Mon Sep 17 00:00:00 2001 From: Kai Sommerfeld Date: Mon, 31 Aug 2020 19:09:18 +0200 Subject: [PATCH 4/4] 7.1.4: Fix HTSP connection thread not ended while in supended state --- pvr.hts/changelog.txt | 1 + src/tvheadend/HTSPConnection.cpp | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pvr.hts/changelog.txt b/pvr.hts/changelog.txt index 2f8231d6..a31f7ffc 100644 --- a/pvr.hts/changelog.txt +++ b/pvr.hts/changelog.txt @@ -2,6 +2,7 @@ - Fix deadlock while reconnecting to backend after a backend response timeout (Fixes #465) - Refactor subscription seek code - Refactor subscription weight code +- Fix HTSP connection thread not ended while in supended state 7.1.3 - Withdraw v7.1.2 due to new deadlocks it introduces, for example during initial connect to backend. diff --git a/src/tvheadend/HTSPConnection.cpp b/src/tvheadend/HTSPConnection.cpp index 15a4e6ba..ebc51a4f 100644 --- a/src/tvheadend/HTSPConnection.cpp +++ b/src/tvheadend/HTSPConnection.cpp @@ -645,14 +645,15 @@ void* HTSPConnection::Process() } } - while (m_suspended) + while (m_suspended && !IsStopped()) { - Logger::Log(LogLevel::LEVEL_DEBUG, "suspended. Waiting for wakeup..."); - /* Wait for wakeup */ Sleep(1000); } + if (IsStopped()) + break; + if (!log) { Logger::Log(LogLevel::LEVEL_DEBUG, "connecting to %s:%d", host.c_str(), port); @@ -691,6 +692,7 @@ void* HTSPConnection::Process() continue; } + Logger::Log(LogLevel::LEVEL_DEBUG, "connected"); log = false; retryAttempt = 0;