From 5f66dab47e7ae0f4f66e28f26aa13df1293a1eab Mon Sep 17 00:00:00 2001 From: Nejuma28 Date: Wed, 8 Jul 2026 16:45:12 +0530 Subject: [PATCH 1/2] RDKEMW-20687: Bring in fix for WPEWebProcess crashed with fingerprint 80095400 to 8.6 Reason for change : Added fix for the race condition by using weakpointer instead of raw pointer Priority : P1 Test Steps: Mentioned in ticket Signed off by : Nejuma T N --- InterfacePlayerRDK.cpp | 142 +++++++++++++++++++++++++++++---- InterfacePlayerRDK.h | 25 +++++- test/utests/fakes/FakeGLib.cpp | 9 ++- test/utests/mocks/MockGLib.h | 1 + 4 files changed, 158 insertions(+), 19 deletions(-) diff --git a/InterfacePlayerRDK.cpp b/InterfacePlayerRDK.cpp index 48796ceb..28834e79 100644 --- a/InterfacePlayerRDK.cpp +++ b/InterfacePlayerRDK.cpp @@ -90,7 +90,8 @@ const char * CipherTypeToString(CipherType type) InterfacePlayerRDK::InterfacePlayerRDK(bool isRialto) : mProtectionLock(), mPauseInjector(false), mSourceSetupMutex(), stopCallback(NULL), tearDownCb(NULL), notifyFirstFrameCallback(NULL), mSourceSetupCV(), mScheduler(), callbackMap(), setupStreamCallbackMap(), mDrmSystem(NULL), mEncrypt(NULL), mDRMSessionManager(NULL), -trickTeardown(false), mFirstFrameRequired(false), mResumeInjector(false), PipelineSetToReady(false), mSchedulerStarted(false) +trickTeardown(false), mFirstFrameRequired(false), mResumeInjector(false), PipelineSetToReady(false), mSchedulerStarted(false), +mProgressCallbackContext(std::make_shared(this)) { interfacePlayerPriv = new InterfacePlayerPriv(isRialto); MW_LOG_MIL("InterfacePlayerRDK constructed using external library"); @@ -106,6 +107,7 @@ trickTeardown(false), mFirstFrameRequired(false), mResumeInjector(false), Pipeli /* InterfacePlayerRDK destructor*/ InterfacePlayerRDK::~InterfacePlayerRDK() { + CancelProgressCallbackContext(); DestroyPipeline(); if (mDrmSystem) { @@ -803,17 +805,40 @@ void MonitorAV( InterfacePlayerRDK *pInterfacePlayerRDK ) */ gboolean InterfacePlayerRDK::ProgressCallbackOnTimeout(gpointer user_data) { - InterfacePlayerRDK *pInterfacePlayerRDK = (InterfacePlayerRDK *)user_data; - InterfacePlayerPriv* privatePlayer = nullptr; - if (pInterfacePlayerRDK) + auto *weakContext = static_cast *>(user_data); + if (!weakContext) { - privatePlayer = pInterfacePlayerRDK->GetPrivatePlayer(); - if (pInterfacePlayerRDK->m_gstConfigParam->monitorAV) + return G_SOURCE_REMOVE; + } + auto callbackContext = weakContext->lock(); + if (!callbackContext) + { + return G_SOURCE_REMOVE; + } + InterfacePlayerRDK *pInterfacePlayerRDK = nullptr; + { + std::unique_lock lock(callbackContext->mutex); + if (callbackContext->cancelled || !callbackContext->player) { - MonitorAV(pInterfacePlayerRDK); + return G_SOURCE_REMOVE; + } + callbackContext->activeCallbacks++; + pInterfacePlayerRDK = callbackContext->player; + } + if (pInterfacePlayerRDK->m_gstConfigParam->monitorAV) + { + MonitorAV(pInterfacePlayerRDK); + } + pInterfacePlayerRDK->TriggerEvent(InterfaceCB::progressCb); + InterfacePlayerPriv *privatePlayer = pInterfacePlayerRDK->GetPrivatePlayer(); + MW_LOG_TRACE("current %d, stored %d ", g_source_get_id(g_main_current_source()), privatePlayer->gstPrivateContext->periodicProgressCallbackIdleTaskId); + { + std::lock_guard lock(callbackContext->mutex); + callbackContext->activeCallbacks--; + if (callbackContext->cancelled && (0 == callbackContext->activeCallbacks)) + { + callbackContext->cv.notify_all(); } - pInterfacePlayerRDK->TriggerEvent(InterfaceCB::progressCb); - MW_LOG_TRACE("current %d, stored %d ", g_source_get_id(g_main_current_source()), privatePlayer->gstPrivateContext->periodicProgressCallbackIdleTaskId); } return G_SOURCE_CONTINUE; } @@ -838,8 +863,10 @@ gboolean InterfacePlayerRDK::IdleCallback(gpointer user_data) double reportProgressInterval = pInterfacePlayerRDK->m_gstConfigParam->progressTimer; reportProgressInterval *= 1000; //convert s to ms + auto callbackContext = pInterfacePlayerRDK->GetOrCreateProgressCallbackContext(); + auto *timerUserData = new std::weak_ptr(callbackContext); GSourceFunc timerFunc = ProgressCallbackOnTimeout; - pInterfacePlayerRDK->TimerAdd(timerFunc, (int)reportProgressInterval, privatePlayer->gstPrivateContext->periodicProgressCallbackIdleTaskId, user_data, "periodicProgressCallbackIdleTask"); + pInterfacePlayerRDK->TimerAdd(timerFunc, (int)reportProgressInterval, privatePlayer->gstPrivateContext->periodicProgressCallbackIdleTaskId, timerUserData, "periodicProgressCallbackIdleTask", DestroyProgressCallbackUserData); } else { @@ -1438,6 +1465,9 @@ void InterfacePlayerRDK::Stop(bool keepLastFrame) interfacePlayerPriv->gstPrivateContext->syncControl.disable(); interfacePlayerPriv->gstPrivateContext->aSyncControl.disable(); + // Stop async worker before removing idle/timer tasks to prevent + // new tasks from being scheduled concurrently during teardown. + mScheduler.StopScheduler(); { std::unique_lock sourceSetupLock(mSourceSetupMutex); mSourceSetupCV.notify_all(); @@ -1454,8 +1484,7 @@ void InterfacePlayerRDK::Stop(bool keepLastFrame) interfacePlayerPriv->gstPrivateContext->firstAudioFrameReceived = false ; } IdleTaskRemove(interfacePlayerPriv->gstPrivateContext->firstProgressCallbackIdleTask); - - this->TimerRemove(interfacePlayerPriv->gstPrivateContext->periodicProgressCallbackIdleTaskId, "periodicProgressCallbackIdleTaskId"); + CancelProgressCallbackContext(); if (interfacePlayerPriv->gstPrivateContext->bufferingTimeoutTimerId) { MW_LOG_MIL("InterfacePlayerRDK: Remove bufferingTimeoutTimerId %d", interfacePlayerPriv->gstPrivateContext->bufferingTimeoutTimerId); @@ -1538,6 +1567,8 @@ void InterfacePlayerRDK::Stop(bool keepLastFrame) // Reset mp4demux playback semantic shim on pipeline event reset interfacePlayerPriv->gstPrivateContext->isMp4DemuxPlayback = false; + // Restart scheduler so the same InterfacePlayerRDK instance can be reused. + mScheduler.StartScheduler(); } void InterfacePlayerRDK::ResetGstEvents() @@ -3754,24 +3785,101 @@ bool InterfacePlayerRDK::CheckDiscontinuity(int mediaType, int streamFormat , bo /** * @brief TimerAdd - add a new glib timer in thread safe manner */ -void InterfacePlayerRDK::TimerAdd(GSourceFunc funcPtr, int repeatTimeout, guint& taskId, gpointer user_data, const char* timerName) +std::shared_ptr InterfacePlayerRDK::GetOrCreateProgressCallbackContext() +{ + std::lock_guard lock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex); + bool createNewContext = false; + if (!mProgressCallbackContext) + { + createNewContext = true; + } + else + { + std::lock_guard contextLock(mProgressCallbackContext->mutex); + if (mProgressCallbackContext->cancelled) + { + createNewContext = true; + } + } + if (createNewContext) + { + mProgressCallbackContext = std::make_shared(this); + } + return mProgressCallbackContext; +} + +void InterfacePlayerRDK::CancelProgressCallbackContext() +{ + std::shared_ptr callbackContext; + { + std::lock_guard lock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex); + callbackContext = mProgressCallbackContext; + } + if (!callbackContext) + { + return; + } + { + std::lock_guard lock(callbackContext->mutex); + callbackContext->cancelled = true; + callbackContext->player = nullptr; + } + this->TimerRemove(interfacePlayerPriv->gstPrivateContext->periodicProgressCallbackIdleTaskId, "periodicProgressCallbackIdleTaskId"); + std::unique_lock lock(callbackContext->mutex); + if (!callbackContext->cv.wait_for(lock, std::chrono::seconds(5), [&callbackContext]() { + return 0 == callbackContext->activeCallbacks; + })) { + MW_LOG_WARN("Teardown timeout: callback context still has active callbacks (%zu). Possible I/O blockage or deadlock detected.", callbackContext->activeCallbacks); + } + lock.unlock(); + std::lock_guard taskLock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex); + if (mProgressCallbackContext == callbackContext) + { + mProgressCallbackContext.reset(); + } +} + +void InterfacePlayerRDK::DestroyProgressCallbackUserData(gpointer user_data) +{ + delete static_cast *>(user_data); +} + +void InterfacePlayerRDK::TimerAdd(GSourceFunc funcPtr, int repeatTimeout, guint& taskId, gpointer user_data, const char* timerName, GDestroyNotify destroyNotify) { std::lock_guard lock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex); if (funcPtr && user_data) { if (0 == taskId) { - /* Sets the function pointed by functPtr to be called at regular intervals of repeatTimeout, supplying user_data to the function */ - taskId = g_timeout_add(repeatTimeout, funcPtr, user_data); + if (destroyNotify) + { + taskId = g_timeout_add_full(G_PRIORITY_DEFAULT, repeatTimeout, funcPtr, user_data, destroyNotify); + if (0 == taskId) + { + destroyNotify(user_data); + } + } + else + { + taskId = g_timeout_add(repeatTimeout, funcPtr, user_data); + } MW_LOG_INFO("InterfacePlayerRDK: Added timer '%.50s', %d", (nullptr!=timerName) ? timerName : "unknown" , taskId); } else { + if (destroyNotify) + { + destroyNotify(user_data); + } MW_LOG_INFO("InterfacePlayerRDK: Timer '%.50s' already added, taskId=%d", (nullptr!=timerName) ? timerName : "unknown", taskId); } } else { + if (destroyNotify && user_data) + { + destroyNotify(user_data); + } MW_LOG_ERR("Bad pointer. funcPtr = %p, user_data=%p",funcPtr,user_data); } } @@ -3923,7 +4031,7 @@ void InterfacePlayerRDK::NotifyFirstFrame(int mediaType) void InterfacePlayerRDK::TriggerEvent(InterfaceCB event) { auto it = callbackMap.find(event); - if (it != callbackMap.end()) + if ((it != callbackMap.end()) && it->second) { it->second(); } @@ -3935,7 +4043,7 @@ void InterfacePlayerRDK::TriggerEvent(InterfaceCB event) void InterfacePlayerRDK::TriggerEvent(InterfaceCB event, int data) { auto it = setupStreamCallbackMap.find(event); - if (it != setupStreamCallbackMap.end()) + if ((it != setupStreamCallbackMap.end()) && it->second) { it->second(data); } diff --git a/InterfacePlayerRDK.h b/InterfacePlayerRDK.h index ba5a0908..36d271cc 100644 --- a/InterfacePlayerRDK.h +++ b/InterfacePlayerRDK.h @@ -33,8 +33,26 @@ #include #include #include +#include #include #include "SocUtils.h" + +class InterfacePlayerRDK; + +struct ProgressCallbackContext +{ + std::mutex mutex; + std::condition_variable cv; + InterfacePlayerRDK *player; + bool cancelled; + size_t activeCallbacks; + + explicit ProgressCallbackContext(InterfacePlayerRDK *playerInstance) + : player(playerInstance), cancelled(false), activeCallbacks(0) + { + } +}; + #include "GstUtils.h" #include "DemuxDataTypes.h" #include "MediaSample.h" @@ -163,6 +181,11 @@ class InterfacePlayerRDK bool trickTeardown; std::mutex mMutex; std::map configMap; + std::shared_ptr mProgressCallbackContext; + + std::shared_ptr GetOrCreateProgressCallbackContext(); + void CancelProgressCallbackContext(); + static void DestroyProgressCallbackUserData(gpointer user_data); public: Configs *m_gstConfigParam; @@ -726,7 +749,7 @@ class InterfacePlayerRDK * @param[in] timerName name of the timer being added * @param[out] taskId id of the timer to be returned */ - void TimerAdd(GSourceFunc funcPtr, int repeatTimeout, guint &taskId, gpointer user_data, const char *timerName = nullptr); + void TimerAdd(GSourceFunc funcPtr, int repeatTimeout, guint &taskId, gpointer user_data, const char *timerName = nullptr, GDestroyNotify destroyNotify = nullptr); /** * @fn TimerIsRunning * @param[in] taskId id of the timer to be removed diff --git a/test/utests/fakes/FakeGLib.cpp b/test/utests/fakes/FakeGLib.cpp index 5cdc7399..45d4f2bc 100644 --- a/test/utests/fakes/FakeGLib.cpp +++ b/test/utests/fakes/FakeGLib.cpp @@ -285,7 +285,14 @@ int g_strcmp0(const char *str1, const char *str2) guint g_timeout_add_full(gint priority, guint interval, GSourceFunc function, gpointer data, GDestroyNotify notify) { - return 0; + guint retval = 0; + + if (g_mockGLib != nullptr) + { + retval = g_mockGLib->g_timeout_add_full(priority, interval, function, data, notify); + } + + return retval; } void g_usleep(gulong microseconds) diff --git a/test/utests/mocks/MockGLib.h b/test/utests/mocks/MockGLib.h index 45c5e870..a4508d9b 100644 --- a/test/utests/mocks/MockGLib.h +++ b/test/utests/mocks/MockGLib.h @@ -31,6 +31,7 @@ class MockGLib public: MOCK_METHOD(GParamSpec*, g_object_class_find_property, (GObjectClass* oclass, const gchar* property_name)); MOCK_METHOD(guint, g_timeout_add, (guint interval, GSourceFunc function, gpointer data)); + MOCK_METHOD(guint, g_timeout_add_full, (gint priority, guint interval, GSourceFunc function, gpointer data, GDestroyNotify notify)); MOCK_METHOD(gboolean, g_source_remove, (guint tag)); MOCK_METHOD(gpointer, g_malloc, (gsize n_bytes)); MOCK_METHOD(void, g_free, (gpointer mem)); From 5c18f583491bad4c4ae7cdce025df570770f3fb6 Mon Sep 17 00:00:00 2001 From: Nejuma28 Date: Mon, 27 Jul 2026 16:55:39 +0530 Subject: [PATCH 2/2] RDKEMW-20661: Bring in fix for WPEWebProcess crashed with fingerprint 80095400 to develop --- InterfacePlayerRDK.cpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/InterfacePlayerRDK.cpp b/InterfacePlayerRDK.cpp index ef18ceec..63c3497e 100644 --- a/InterfacePlayerRDK.cpp +++ b/InterfacePlayerRDK.cpp @@ -3824,14 +3824,31 @@ void InterfacePlayerRDK::CancelProgressCallbackContext() callbackContext->cancelled = true; callbackContext->player = nullptr; } + const guint progressTimerId = interfacePlayerPriv->gstPrivateContext->periodicProgressCallbackIdleTaskId; this->TimerRemove(interfacePlayerPriv->gstPrivateContext->periodicProgressCallbackIdleTaskId, "periodicProgressCallbackIdleTaskId"); - std::unique_lock lock(callbackContext->mutex); - if (!callbackContext->cv.wait_for(lock, std::chrono::seconds(5), [&callbackContext]() { - return 0 == callbackContext->activeCallbacks; - })) { - MW_LOG_WARN("Teardown timeout: callback context still has active callbacks (%zu). Possible I/O blockage or deadlock detected.", callbackContext->activeCallbacks); + + // Avoid deadlock if cancellation is triggered re-entrantly from within + // the progress timer callback itself. + GSource *currentSource = g_main_current_source(); + if (currentSource && (g_source_get_id(currentSource) == progressTimerId)) + { + std::lock_guard taskLock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex); + if (mProgressCallbackContext == callbackContext) + { + mProgressCallbackContext.reset(); + } + return; + } + + { + std::unique_lock lock(callbackContext->mutex); + constexpr auto kTeardownTimeout = std::chrono::seconds(5); + if (!callbackContext->cv.wait_for(lock, kTeardownTimeout, [&callbackContext]() { + return 0 == callbackContext->activeCallbacks; + })) { + MW_LOG_WARN("Teardown timeout: callback context still has active callbacks (%zu). Possible I/O blockage or deadlock detected.", callbackContext->activeCallbacks); + } } - lock.unlock(); std::lock_guard taskLock(interfacePlayerPriv->gstPrivateContext->TaskControlMutex); if (mProgressCallbackContext == callbackContext) {