Skip to content

VPAAMP-1495 uveExecuter quit unexpectedly - #2130

Merged
pstroffolino merged 5 commits into
dev_sprint_25_2from
feature/VPAAMP-1495
Oct 26, 2026
Merged

pstroffolino merged 5 commits into
dev_sprint_25_2from
feature/VPAAMP-1495

Conversation

@pstroffolino

Copy link
Copy Markdown
Contributor

VPAAMP-1495 uveExecuter quit unexpectedly
Reason for Change: avoid error popup when uveExecutor completes

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Shutdown handling can leave a worker accessing destroyed player state and does not reliably prevent termination.

2 open issues
What changed in this PR

This PR aims to prevent the macOS crash popup when uveExecutor finishes by changing pause-position monitor shutdown in AAMP.

Changes:

  • Catch exceptions from monitor shutdown during player destruction.
  • Log mutex errors and detach the monitor thread when they occur.
File Description
priv_aamp.cpp Adds exception handling and a thread-detachment fallback during monitor shutdown.

Review effort: Balanced

Comment thread priv_aamp.cpp Outdated
Comment thread priv_aamp.cpp Outdated
StopPausePositionMonitoring() acquires a mutex via std::unique_lock.
During process exit, _aamp_term() calls Stop() then deletes the player.
In a race between AAMP internal threads and _aamp_term(), the mutex can
be in an invalid state when the destructor calls
StopPausePositionMonitoring() a second time, causing std::unique_lock to
throw std::system_error ("mutex lock failed: Invalid argument").

Destructors are implicitly noexcept, so an uncaught exception propagates
to std::terminate() and aborts the process, even though the session has
already finished successfully.

Fix 1 - StopPausePositionMonitoring (primary fix):
  Wrap the std::unique_lock construction in try/catch(std::system_error).
  When the mutex is invalid, detach the thread instead of blocking on
  join(); the thread exits imminently with the process.

Fix 2 - PrivateInstanceAAMP destructor (defence-in-depth):
  Wrap the StopPausePositionMonitoring call in try/catch(...) so no
  exception can escape the destructor body and reach std::terminate().

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
pstroffolino and others added 4 commits October 26, 2026 11:55
…r, flag teardown exceptions

Reason for change: uveExecuter intermittently aborts (SIGABRT via std::terminate)
in ~PrivateInstanceAAMP during process teardown. The previous patch caught the
exception in the destructor and detached the monitor thread on mutex failure;
review noted that a still-joinable std::thread member terminates anyway and a
detached worker keeps using the destroyed object.

Changes:
- StopPausePositionMonitoring never detaches; it cancels under the monitor
  mutex (notify_all) and always joins the worker.
- New mPausePositionLifecycleMutex serializes Start/StopPausePositionMonitoring
  so a monitor cannot be restarted while another thread stops/joins it
  (StopPausePositionMonitoringLocked used by Start).
- Destructor keeps a last-resort try/catch that calls AampTeardownExceptionHook,
  which prints an "AAMP_TEARDOWN_EXCEPTION:" marker to stderr (breakpoint-able)
  so occurrences are visible to test harnesses.

Known open issue: an abort is still seen intermittently (crash report
2026-10-26 13:12). Stack: __cxa_finalize_ranges -> _aamp_term ->
~PlayerInstanceAAMP -> ~PrivateInstanceAAMP -> std::terminate, i.e. the player
is destroyed during static destruction at process exit; investigation ongoing.

Test Procedure: aamp_test_internal tools/ppjstestrunner run_ppjs_test_runner.sh
(basic, switching, stitching, seek), repeated; check for AAMP_TEARDOWN_EXCEPTION
and ~/Library/Logs/DiagnosticReports/uveExecuter-*.ips.
Risks: Low-Medium - teardown/threading change in PauseAt monitor.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
_aamp_term() is __attribute__((destructor(101))): priority 101 is the
lowest, so it fires after all C++ static-storage-duration objects are
destroyed.  When it called `delete _allocated_aamp` at that point,
~PrivateInstanceAAMP() tried to lock `gMutex` (static std::mutex,
already destroyed) -- std::system_error escaped the noexcept destructor
-- std::terminate() -- SIGABRT.

Fix 1 (UVEBinding.cpp / uveExecuter): call aamp_UnloadJSController(ctx)
before every JSGlobalContextRelease() path.  aamp_UnloadJSController ->
aamp_UnloadJS -> AAMP_finalize stops and deletes _allocated_aamp (sets
it NULL) while all statics are still alive.  _aamp_term then finds NULL
and returns immediately.

Fix 2 (jsbindings.cpp): move ClearAAMPPlayerInstances() inside the
`if (_allocated_aamp != NULL)` guard in _aamp_term.
AAMPMediaPlayer_JS::_jsMediaPlayerInstances is a static that is also
destroyed before destructor(101) runs; calling size() on it then is UB.
After the explicit teardown from Fix 1, _aamp_term is a pure no-op so
the call is skipped entirely.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@pstroffolino
pstroffolino merged commit 710f21c into dev_sprint_25_2 Oct 26, 2026
9 checks passed
@pstroffolino
pstroffolino deleted the feature/VPAAMP-1495 branch October 26, 2026 19:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants