Repository navigation
Conversation
_BBS_Backup_Manager's worker loop copied m_post_callback to a local, unlocked, did file IO, then invoked the stale copy. If MainFrame:: shutdown() swapped in nullptr during that window, the worker still called the old callback, wxPostEvent-ing into an already destroyed MainFrame (EXC_BAD_ACCESS, Sentry #7134242393). Serialize callback swaps against in-flight invocations with m_post_callback_mutex and re-read m_post_callback under it, so once set_backup_callback(nullptr) returns the worker can never invoke an old callback. Lock order: m_mutex -> m_post_callback_mutex. Co-Authored-By: Claude Code <noreply@anthropic.com>
|
Backup UI task stall —
This is not idle UI bookkeeping — it is the main path of the auto-backup pipeline:
So after a language switch, project auto-backup stops writing to disk entirely (and Pre-fix, this same window manifested as the UAF crash; this fix trades the crash for the stall, which is the right direction — but the invariant ( |
4f3e868 to
81f2a6a
Compare
The notification for queued ui tasks is edge-triggered: the worker calls the post callback only when m_ui_tasks transitions to size()==1. After ba2107d, a task queued while the callback is null (during MainFrame::shutdown, e.g. language switch via GUI_App::recreate_GUI) skips that single notification with no way to re-arm it. Every later push sees size()>=2, so EVT_BACKUP_POST never fires again and the auto-backup pipeline stalls until process exit: export_3mf(Backup) (the only SaveStrategy::Backup call site), m_temp_model clone cleanup and RemoveBackup directory cleanup all stop running. set_post_callback now plays catch-up when installing a non-null callback: if tasks are already queued, it re-announces them by calling c(0) under m_mutex. The post-callback lock is released first so the worker's lock order (m_mutex -> m_post_callback_mutex) is preserved with no hold-and-wait overlap; duplicate notifications are harmless because run_backup_ui_tasks drains the queue as a batch. Co-Authored-By: Claude Code <noreply@anthropic.com>
81f2a6a to
d4e268e
Compare
|
VERDICT: APPROVE_WITH_COMMENTS Findings
Checklist coverageApplied: N/A or no-op for this diff: ABI compatibility, layering/dependency inversion, compile-time programming, templates, STL algorithms/containers, Qt6/QML, direct wxWidgets API usage (no wx calls appear in the diff itself), performance optimization, parsing/validation, format strings, integer safety beyond the callback boolean check. Action items
Limitations: full AI 评审(advisory),由 dev-guidelines harness 流水线生成 · head |
…race-05af Port Snapmaker/OrcaSlicer Snapmaker#1004: stop backup thread wxPostEvent after shutdown
AI review (PR Snapmaker#1004) flagged that the catch-up path called the arbitrary callback c under m_mutex. Only the GUI thread drains m_ui_tasks, so the queue cannot empty between the check and the call: check under the lock, invoke after releasing it. The worker-side notification keeps invoking under m_mutex (and m_post_callback_mutex): there the locked call is load-bearing -- copying the callback and invoking after unlock would reopen the stale-invocation window fixed by ba2107d. Document the boost synchronization choice on the member per the same review. Co-Authored-By: Claude Code <noreply@anthropic.com>
AI review (PR Snapmaker#1004) flagged that the catch-up path called the arbitrary callback c under m_mutex. Only the GUI thread drains m_ui_tasks, so the queue cannot empty between the check and the call: check under the lock, invoke after releasing it. The worker-side notification keeps invoking under m_mutex (and m_post_callback_mutex): there the locked call is load-bearing -- copying the callback and invoking after unlock would reopen the stale-invocation window fixed by ba2107d. Document the boost synchronization choice on the member per the same review. Co-Authored-By: Claude Code <noreply@anthropic.com>
dcd042e to
5df8387
Compare
|
VERDICT: APPROVE_WITH_COMMENTS Findings
Checklist coverageApplied:
N/A for this diff:
Action items
LimitationsOnly the provided diff was reviewed; the full AI 评审(advisory),由 dev-guidelines harness 流水线生成 · head |
Fixes the crash tracked in Sentry issue #7134242393 (SNAPMAKER_ORCA-7R, "wxPostEvent", EXC_BAD_ACCESS, still active on 2.4.0). The reported stack is the backup worker thread (boost::thread_proxy -> _BBS_Backup_Manager::operator() -> std::function -> MainFrame ctor lambda -> wxPostEvent) — a data race between the backup worker and the UI thread, platform-neutral UB.
This PR now carries a second commit addressing review feedback: completing the notification invariant so auto-backup survives the language-switch recreate — same subsystem, same window, see the follow-up section below.
Cause
_BBS_Backup_Manager's worker loop snapshotsm_post_callbackinto a local while holdingm_mutex, releases the lock forprocess_task(), then invokes the stale copy after re-locking. MeanwhileMainFrame::shutdown()— called on app exit and on language-switch recreate — callsSlic3r::set_backup_callback(nullptr)and destroys MainFrame. If shutdown lands inside that window, the worker still calls the old callback, whichwxPostEvents into the already-destroyed MainFrame → use-after-free.The change
Add
m_post_callback_mutexserializing callback swaps against in-flight invocations (bbs_3mf.cpponly, no public API change):set_post_callback()swaps under the new mutexm_post_callbackunder it at fire time — no stale local copyGuarantee: once
set_backup_callback(nullptr)returns, the worker can never invoke an old callback. Lock orderm_mutex -> m_post_callback_mutexis documented at the member;m_mutexcritical sections are otherwise unchanged (the callback was already invoked under it). The UI-thread read inprocess_ui_task()(callback(1)) is untouched — same-thread with the only writer, no race there.Verification
A/B tested on Windows (MSVC 2022, RelWithDebInfo) with a test-only build aid: a 2.5 s sleep in the worker's post-task window plus error-level logging at the callback swap/invoke sites (never committed). Quit during a backup cycle, 5 trials per build:
set_backup_callback(nullptr)landed mid-window in 4/5 runs; in every one the worker invoked the stale callback 150–540 ms after the swap, entering the lambda that captures the dying MainFrame (logged). Across runs this manifested as silent invalid calls, hangs at exit, and abnormal exit codes — the same UAF Sentry reports as EXC_BAD_ACCESS on macOS.m_post_callbackas null under the new mutex in 5/5 and skipped the call — zero invocations after the swap, no hangs or crashes, all exits clean.Sentry cross-check: the issue's newest event stack matches the fixed path frame-by-frame (worker → std::function → MainFrame lambda → wxPostEvent).