VPLAY-12682:SoC-independent underflow detection - follow-up improvements#1024
VPLAY-12682:SoC-independent underflow detection - follow-up improvements#1024varshnie wants to merge 4 commits intodev_sprint_25_2from
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request implements follow-up improvements to the AampUnderflowMonitor feature, focusing on lifecycle management changes and code simplification. However, the PR introduces critical thread-safety issues by removing mutex protections that were essential for safe concurrent access.
Changes:
- Moved AampUnderflowMonitor initialization from lazy creation in StartUnderflowMonitor to eager creation in StreamAbstractionAAMP constructor
- Removed mUnderflowMonitorMutex from StreamAbstractionAAMP class and all lifecycle method synchronization
- Cached raw pointers in AampUnderflowMonitor::Run() method to avoid repeated mutex locking
- Renamed parameter from bufferingStopped to bufferingStart for clarity
- Added config check wrapper around SetBufferingState logic
- Moved StartUnderflowMonitor calls from central TuneHelper to protocol-specific Start() methods
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| streamabstraction.cpp | Moved monitor initialization to constructor; removed mutex protections from lifecycle methods; simplified StartUnderflowMonitor |
| StreamAbstractionAAMP.h | Removed mUnderflowMonitorMutex member variable |
| priv_aamp.h | Renamed parameter bufferingStopped to bufferingStart in SendBufferChangeEvent |
| priv_aamp.cpp | Updated parameter name; added ReportBufferEvent config check wrapping SetBufferingState logic; removed monitor startup from TuneHelper |
| AampUnderflowMonitor.h | Removed @fn tags from Doxygen comments |
| AampUnderflowMonitor.cpp | Cached mAamp and mStream pointers locally in Run(); removed mutex protections from multiple pointer accesses |
| fragmentcollector_progressive.cpp | Added StartUnderflowMonitor call to protocol-specific Start() method |
| fragmentcollector_mpd.cpp | Added StartUnderflowMonitor call to protocol-specific Start() method |
| fragmentcollector_hls.cpp | Added StartUnderflowMonitor call to protocol-specific Start() method |
| AampConfig.cpp | Added documentation comment for enableAampUnderflowMonitor config option |
Comments suppressed due to low confidence (1)
streamabstraction.cpp:3043
- Missing synchronization: The removal of the mutex lock from StopUnderflowMonitor creates a race condition with StartUnderflowMonitor and IsUnderflowMonitorRunning(). Without the mutex, concurrent access to mUnderflowMonitor (checking, calling methods, and resetting) is not thread-safe.
Re-introduce the mUnderflowMonitorMutex to protect access to mUnderflowMonitor in all three lifecycle methods.
if (mUnderflowMonitor)
{
mUnderflowMonitor->Stop();
mUnderflowMonitor.reset();
AAMPLOG_INFO("Stopped AampUnderflowMonitor for video");
}
58f23ec to
025049b
Compare
025049b to
861853a
Compare
861853a to
a35fd92
Compare
a35fd92 to
dea5fb9
Compare
dea5fb9 to
0f5d441
Compare
0f5d441 to
70b421a
Compare
70b421a to
799335c
Compare
799335c to
700053b
Compare
700053b to
2bd4db9
Compare
2bd4db9 to
12bfdcf
Compare
739b3b0 to
0e89ff1
Compare
0e89ff1 to
b1f7e34
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 19 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
test/utests/fakes/FakeAampUnderflowMonitor.cpp:54
- This fake file defines the production
AampUnderflowMonitormethods (ctor/dtor/Start/Stop/Run). Because thefakeslibrary is linked by most unit test executables, any test that also compiles the realAampUnderflowMonitor.cppwill hit ODR / multiple-definition linker failures. Consider renaming this to a separate fake class (instead of redefiningAampUnderflowMonitor), or moving it out of the always-linkedfakeslibrary so only specific tests override the implementation.
#include "AampUnderflowMonitor.h"
#include "MockAampUnderflowMonitor.h"
MockAampUnderflowMonitor *g_mockAampUnderflowMonitor = nullptr;
AampUnderflowMonitor::AampUnderflowMonitor(StreamAbstractionAAMP* stream, PrivateInstanceAAMP* aamp) : mStream(stream), mAamp(aamp)
{
}
AampUnderflowMonitor::~AampUnderflowMonitor()
{
}
void AampUnderflowMonitor::Start()
{
mRunning.store(true);
if(g_mockAampUnderflowMonitor != nullptr)
{
g_mockAampUnderflowMonitor->Start();
}
}
void AampUnderflowMonitor::Stop()
{
mRunning.store(false);
if(g_mockAampUnderflowMonitor != nullptr)
{
g_mockAampUnderflowMonitor->Stop();
}
}
void AampUnderflowMonitor::Run()
{
}
b1f7e34 to
1e51788
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 19 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
StreamAbstractionAAMP.h:2042
mUnderflowMonitoris astd::unique_ptrthat is started/stopped and also reset in the implementation, and the class-level mutex that previously serialized these operations was removed. Given the monitor pointer can now be reset, this member needs explicit synchronization (or a stable lifetime guarantee) to avoid races when accessed from multiple threads via the public Start/Stop/IsRunning wrappers.
protected:
/**
* Underflow monitor instance owned by Stream; manages detection and
* handling of underflow conditions.
*/
std::unique_ptr<class AampUnderflowMonitor> mUnderflowMonitor;
8fdc204 to
57735dc
Compare
Reason for change:follow-up improvements for AampUnderflowMonitor Risks: Medium Signed-off-by: varshnie <varshniblue14@gmail.com> VPLAY-12605:[L1 Test] AampUnderflowMonitor Tests Reason for change:Added L1 for AampUnderflowMonitor Test Procedure:Refer Jira Ticket Risks: Medium Signed-off-by: varshnie <varshniblue14@gmail.com> Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…tionAAMP without stopping the underflow monitor first. Signed-off-by: Philip Stroffolino <philip_stroffolino@cable.comcast.com>
5060af3 to
d3f384b
Compare
…monitor in streamabstarction stop
d3f384b to
f24277b
Compare
| aamp->DisableDownloads(); | ||
| ReassessAndResumeAudioTrack(true); | ||
| AbortWaitForAudioTrackCatchup(false); | ||
|
|
||
| if(ISCONFIGSET(eAAMPConfig_EnableAampUnderflowMonitor)) | ||
| { | ||
| StopUnderflowMonitor(); | ||
| } |
There was a problem hiding this comment.
StopUnderflowMonitor() is invoked after DisableDownloads() and other teardown steps. While the underflow monitor thread is still running it can call ResumeTrackDownloads()/SetBufferingState(), which can counteract Stop()’s attempt to quiesce downloads and increase teardown-time races. Consider stopping the underflow monitor at the beginning of Stop() (before DisableDownloads and before track teardown), and ideally keying off mUnderflowMonitor’s existence rather than a separate config check.
| if (!aamp->IsLocalAAMPTsb() || aamp->mAampTsbLanguageChangeInProgress) | ||
| { | ||
| aamp->DisableDownloads(); | ||
| mCdaiObject->AbortWaitForNextAdResolved(); | ||
| aamp->mAampTsbLanguageChangeInProgress = false; | ||
| if(ISCONFIGSET(eAAMPConfig_EnableAampUnderflowMonitor)) | ||
| { | ||
| StopUnderflowMonitor(); | ||
| } |
There was a problem hiding this comment.
StopUnderflowMonitor() happens after DisableDownloads()/AbortWaitForNextAdResolved() in this shutdown path. Since the monitor thread may call ResumeTrackDownloads()/SetBufferingState while Stop() is in progress, it’s safer to stop/join the monitor thread at the start of Stop() (before disabling downloads / tearing down tracks), and to guard on mUnderflowMonitor being non-null.
| * @note Must be called before Stop() or before destroying the StreamAbstractionAAMP | ||
| * instance. The underflow monitor holds raw pointers back into the stream and | ||
| * the AAMP instance; stopping it joins the background thread and prevents any | ||
| * use-after-free that would otherwise arise during teardown. |
There was a problem hiding this comment.
This note says StopUnderflowMonitor “must be called before Stop()”, but the current shutdown flow calls StopUnderflowMonitor from within the various Stop() implementations (and not necessarily before Stop()). Please update the note to match the actual lifecycle contract (e.g., “Stop() will stop the monitor; ensure it is stopped before destruction”), or enforce it by stopping the monitor in the StreamAbstractionAAMP destructor as a safety net.
| * @note Must be called before Stop() or before destroying the StreamAbstractionAAMP | |
| * instance. The underflow monitor holds raw pointers back into the stream and | |
| * the AAMP instance; stopping it joins the background thread and prevents any | |
| * use-after-free that would otherwise arise during teardown. | |
| * @note Stops the underflow monitor thread and joins it. The monitor holds | |
| * raw pointers back into the stream and the AAMP instance, so it must | |
| * be stopped (either explicitly via this method or implicitly from | |
| * Stop()) before destroying the StreamAbstractionAAMP instance to | |
| * avoid use-after-free during teardown. |
| auto start = std::chrono::steady_clock::now(); | ||
| while (!bufferingStarted.load() && (std::chrono::steady_clock::now() - start) < std::chrono::milliseconds(1000)) | ||
| { | ||
| std::this_thread::yield(); |
There was a problem hiding this comment.
This loop busy-waits with std::this_thread::yield() for up to 1s, which can burn CPU (especially since the test fakes’ interruptibleMsSleep() is a no-op and the monitor thread can spin). Consider using a small sleep_for (e.g., 1–5ms) or a condition_variable/latch to wait for the expected call.
| std::this_thread::yield(); | |
| std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
| auto start = std::chrono::steady_clock::now(); | ||
| while (!bufferingEnded.load() && (std::chrono::steady_clock::now() - start) < std::chrono::milliseconds(1000)) | ||
| { | ||
| std::this_thread::yield(); |
There was a problem hiding this comment.
This second busy-wait loop also yields in a tight loop for up to 1s. To keep the unit test suite CPU-friendly and reduce flakiness under load, prefer a blocking primitive (condition_variable) or at least a small sleep_for in the polling loop.
| std::this_thread::yield(); | |
| std::this_thread::sleep_for(std::chrono::milliseconds(1)); |
Reason for change:follow-up improvements for AampUnderflowMonitor
Risks: Medium
Signed-off-by: varshnie varshniblue14@gmail.com