VPLAY-11126 [AAMP TSB] TsbReader waits for fragments in a tight loop#606
VPLAY-11126 [AAMP TSB] TsbReader waits for fragments in a tight loop#606hthakkar-synamedia wants to merge 28 commits intodev_sprint_25_2from
Conversation
Reason for Change: When TsbReader tries to read the fragments that are not downloaded yet by the FetcherLoop, it enters into a quick tight loop. Fix here implements producer consumer design and wait for a conditional variable until new fragment is available. Test Procedure: - Refer Ticket Risks:low
test/utests/tests/FragmentCollectorMpdTests/FragmentCollectorMpdTestCases.cpp
Outdated
Show resolved
Hide resolved
Reason for Change: When TsbReader tries to read the fragments that are not downloaded yet by the FetcherLoop, it enters into a quick tight loop. Fix here implements producer consumer design and wait for a conditional variable until new fragment is available. Test Procedure: - Refer Ticket Risks:low
|
Addressed the comments |
hthakkar-synamedia
left a comment
There was a problem hiding this comment.
Addressed the comments
Reason for Change: When TsbReader tries to read the fragments that are not downloaded yet by the FetcherLoop, it enters into a quick tight loop. Fix here implements producer consumer design and wait for a conditional variable until new fragment is available. Test Procedure: - Refer Ticket Risks:low
hthakkar-synamedia
left a comment
There was a problem hiding this comment.
fixed typo and modified the RaiseNewVideoTsbContentNotification function
Reason for Change: When TsbReader tries to read the fragments that are not downloaded yet by the FetcherLoop, it enters into a quick tight loop. Fix here implements producer consumer design and wait for a conditional variable until new fragment is available. Test Procedure: - Refer Ticket Risks:low
There was a problem hiding this comment.
Pull Request Overview
This PR enhances the TSB (Time Shift Buffer) fragment fetching mechanism by adding a return value to AdvanceTsbFetch to indicate whether a segment was successfully cached, and implements a condition variable-based waiting mechanism to efficiently handle scenarios where no new fragments are available in the TSB.
Key Changes:
- Modified
AdvanceTsbFetchto returnboolindicating if a fragment was cached - Added condition variable
mNewVideoTsbContentCVto signal when new video content is written to TSB - Implemented
WaitForNewVideoTsbFragment()to block until new content is available or downloads are disabled - Replaced polling with event-driven waiting in the TsbReader loop
Reviewed Changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| fragmentcollector_mpd.h | Updated method signature and documentation for AdvanceTsbFetch to return bool |
| fragmentcollector_mpd.cpp | Implemented return value logic, added segment tracking, and replaced sleep with condition variable waits |
| AampTSBSessionManager.h | Added new methods and members for condition variable-based notification |
| AampTSBSessionManager.cpp | Implemented wait/notify mechanism and initialized new member variable |
| priv_aamp.cpp | Added notification to unblock waiting threads when downloads are disabled |
| test/utests/tests/FragmentCollectorMpdTests/FragmentCollectorMpdTestCases.cpp | Updated tests to capture and verify the return value |
| test/utests/fakes/FakeTSBSessionManager.cpp | Added stub implementations for new public methods |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Reason for Change: When TsbReader tries to read the fragments that are not downloaded yet by the FetcherLoop, it enters into a quick tight loop. Fix here implements producer consumer design and wait for a conditional variable until new fragment is available. Test Procedure: - Refer Ticket Risks:low
Reason for Change: When TsbReader tries to read the fragments that are not downloaded yet by the FetcherLoop, it enters into a quick tight loop. Fix here implements producer consumer design and wait for a conditional variable until new fragment is available. Test Procedure: - Refer Ticket Risks:low Merge branch 'dev_sprint_25_2' of github.com:rdkcentral/aamp into feature/VPLAY-11126
There was a problem hiding this comment.
Pull Request Overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Reason for Change: When TsbReader tries to read the fragments that are not downloaded yet by the FetcherLoop, it enters into a quick tight loop. Fix here implements producer consumer design and wait for a conditional variable until new fragment is available. Test Procedure: - Refer Ticket Risks:low
Reason for Change: When TsbReader tries to read the fragments that are not downloaded yet by the FetcherLoop, it enters into a quick tight loop. Fix here implements producer consumer design and wait for a conditional variable until new fragment is available. Test Procedure: - Refer Ticket Risks:low
Avoid race where the TsbReader thread reads predicate between flag being set and the notify firing. Before joining tsbReaderThreadID, NotifyVideoTsbWaiters() is now called, avoiding potential deadlock: Avoid tight loop and log spam when rate <= AAMP_NORMAL_PLAY_RATE and no segment is found. Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
test/utests/tests/AampTsbSessionManagerTests_Mocked/FunctionalTests.cpp
Outdated
Show resolved
Hide resolved
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
| if (segmentFound) | ||
| { | ||
| aamp->interruptibleMsSleep(50); //To Avoid tight loop adding a small delay | ||
| } | ||
| // AAMP could reach the end of the TSB only when doing FF (rate > AAMP_NORMAL_PLAY_RATE) | ||
| else if (aamp->rate > AAMP_NORMAL_PLAY_RATE) | ||
| { | ||
| // All the segments in TSB have been sent to gstreamer; avoid indefinite | ||
| // blocking in WaitForVideoTsbContentOrAbort by using a bounded, interruptible | ||
| // sleep before retrying the fetch loop. | ||
| aamp->interruptibleMsSleep(50); | ||
| } | ||
| else | ||
| { | ||
| // Unexpected: no segment found at normal/slow rate outside of EOS. Sleep to | ||
| // avoid a tight loop and throttle log output until the condition resolves. | ||
| AAMPLOG_TRACE("No segment found for rate <= AAMP_NORMAL_PLAY_RATE, retrying"); | ||
| aamp->interruptibleMsSleep(50); |
There was a problem hiding this comment.
The PR description says TsbReader will block on a condition variable until new fragments are available, but this loop still only polls with interruptibleMsSleep(50) and never calls AampTSBSessionManager::WaitForVideoTsbContentOrAbort(). As a result, the newly added Wait/Notify mechanism is unused in production and the tight-loop/polling behavior remains (the comment even references WaitForVideoTsbContentOrAbort without actually using it). Consider calling WaitForVideoTsbContentOrAbort() when no segments were cached and the cache is not full, and ensure the wait can be interrupted on abort/stop paths.
There was a problem hiding this comment.
is this valid issue? If so, how important to address?
There was a problem hiding this comment.
I do not think this is a valid issue
| // Unblock TsbReader if it is waiting for new video TSB content (e.g. during FF | ||
| // on local AAMP TSB where DisableDownloads is not called and therefore | ||
| // NotifyVideoTsbWaiters is not triggered via the downloads-disabled path). | ||
| if (AampTSBSessionManager *tsbMgr = aamp->GetTSBSessionManager()) | ||
| { | ||
| tsbMgr->NotifyVideoTsbWaiters(); | ||
| } |
There was a problem hiding this comment.
This stop-path comment says it is unblocking TsbReader waiting on WaitForVideoTsbContentOrAbort(), but StreamAbstractionAAMP_MPD::TsbReader() never calls WaitForVideoTsbContentOrAbort() (it only uses WaitForManifestUpdate() / WaitForCachedFragmentChunkInjected() / sleeps). Either wire TsbReader() up to the new condition variable as intended, or adjust/remove this comment + notification so it matches actual behavior.
Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
test/utests/tests/AampTsbSessionManagerTests_Mocked/FunctionalTests.cpp
Outdated
Show resolved
Hide resolved
| // Brief sleep to allow thread to enter waiting state | ||
| // Note: This is a timing assumption, but std::thread provides no "is waiting" status check | ||
| std::this_thread::sleep_for(std::chrono::milliseconds(10)); |
There was a problem hiding this comment.
This test relies on a fixed sleep to “let the consumer thread enter waiting state”. Fixed timing sleeps can be flaky across different CI loads and platforms. Prefer a deterministic handshake (e.g., a promise/condition_variable that the consumer signals after it is ready to wait) so the test does not depend on scheduling.
| std::thread consumerThread([this]() { | ||
| mAampTSBSessionManager->WaitForVideoTsbContentOrAbort(); | ||
| }); | ||
|
|
||
| // Brief sleep to allow thread to enter waiting state | ||
| // Note: This is a timing assumption, but std::thread provides no "is waiting" status check | ||
| std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||
|
|
||
| mAampTSBSessionManager->NotifyVideoTsbWaiter(); | ||
|
|
||
| consumerThread.join(); |
There was a problem hiding this comment.
If WaitForVideoTsbContentOrAbort() fails to unblock, this join will hang the entire test binary indefinitely. Consider adding a bounded wait (e.g., run the wait in a future and ASSERT that it completes within a timeout) so failures are reported rather than deadlocking CI.
| EXPECT_CALL(*g_mockTSBReader, IsEos()).WillOnce(Return(false)); | ||
| EXPECT_CALL(*g_mockTSBSessionManager, PushNextTsbFragment(mediaStreamContext, _)).WillOnce(Return(true)); | ||
|
|
||
| // Call the protected method through testable wrapper | ||
| mMpdStream->TestAdvanceTsbFetch(trackIdx, trickPlay, delta, waitForFreeFrag, bCacheFullState); | ||
| bool result = mMpdStream->TestAdvanceTsbFetch(trackIdx, trickPlay, delta, waitForFreeFrag, bCacheFullState); |
There was a problem hiding this comment.
The expectations are set on g_mockTSBReader, but GetTsbReader is configured to return a real AampTsbReader (tsbReader). As a result, the EXPECT_CALLs on g_mockTSBReader will never be satisfied and this test will fail. Return g_mockTSBReader (as a shared_ptr) from GetTsbReader or set expectations against the actual object being used.
| // Call the protected method through testable wrapper | ||
| mMpdStream->TestAdvanceTsbFetch(trackIdx, trickPlay, delta, waitForFreeFrag, bCacheFullState); | ||
| bool result = mMpdStream->TestAdvanceTsbFetch(trackIdx, trickPlay, delta, waitForFreeFrag, bCacheFullState); |
There was a problem hiding this comment.
This test allocates MediaStreamContext with new and never deletes it. Please switch to RAII (e.g., std::unique_ptr) or ensure it is deleted at the end of the test to avoid leaking across the unit test process.
| // Verify that PushNextTsbFragment is NOT called when track is disabled | ||
| EXPECT_CALL(*g_mockTSBSessionManager, PushNextTsbFragment(mediaStreamContext, _)).Times(0); | ||
|
|
||
| // Call the protected method through testable wrapper | ||
| mMpdStream->TestAdvanceTsbFetch(trackIdx, trickPlay, delta, waitForFreeFrag, bCacheFullState); | ||
| bool result = mMpdStream->TestAdvanceTsbFetch(trackIdx, trickPlay, delta, waitForFreeFrag, bCacheFullState); |
There was a problem hiding this comment.
Same issue as above: g_mockTSBReader has EXPECT_CALL(TrackEnabled) but GetTsbReader returns a real AampTsbReader instance (tsbReader). The TrackEnabled expectation will be unmet and the test will fail. Configure GetTsbReader to return g_mockTSBReader (or otherwise ensure expectations are set on the object actually used).
| mAampTSBSessionManager->NotifyVideoTsbWaiter(); | ||
|
|
||
| // Spawn a thread that waits for the notification | ||
| std::thread consumerThread([this]() { | ||
| mAampTSBSessionManager->WaitForVideoTsbContentOrAbort(); | ||
| }); | ||
|
|
||
| consumerThread.join(); |
There was a problem hiding this comment.
Same as the previous test: this thread join is unbounded. Please add a bounded wait/timeout-based assertion (e.g., future.wait_for) so a regression cannot deadlock the full test suite.
Reason for Change: When TsbReader tries to read the fragments that are not downloaded yet by the FetcherLoop, it enters into a quick tight loop. Fix here implements producer consumer design and wait for a conditional variable until new fragment is available.
Added L1 microtest
Test Procedure: - Refer Ticket
Risks:low