fix(tauri): deterministic CEF teardown on full app close (#1120)#1189
fix(tauri): deterministic CEF teardown on full app close (#1120)#1189senamakel merged 2 commits intotinyhumansai:mainfrom
Conversation
…1120) - Poll until closed CEF webview labels drain (bounded) instead of a fixed sleep. - Tear down provider scanners and iMessage registry during early teardown. - Have shutdown_all return labels for close coordination; expand shutdown logs. - Refresh app/src-tauri/Cargo.lock for 0.53.13 / current openhuman dependency graph. Co-authored-by: Cursor <cursoragent@cursor.com>
📝 WalkthroughWalkthroughScanner registries for multiple providers were refactored to store per-account tokio AbortHandle vectors and actively abort running scanner tasks on forget/forget_all. App early teardown was changed to close CEF webviews and poll until they drain (removing fixed sleeps) and to synchronously trigger scanner teardowns before CEF shutdown. ChangesScanner Lifecycle & Task Cancellation
CEF Shutdown Orchestration
Sequence DiagramssequenceDiagram
participant Client as Caller (forget/forget_all)
participant Reg as ScannerRegistry
participant Handle as AbortHandle(s)
participant Task as Scanner Task
participant Tokio as Tokio Executor
Client->>Reg: forget(account_id)
Reg->>Handle: lookup Vec<AbortHandle>
Reg->>Handle: call abort() on each
Handle->>Task: cancellation signal
Tokio->>Task: task polled -> stops
Task-->>Reg: stopped (implicit)
sequenceDiagram
participant User as User quits
participant App as App (lib.rs)
participant WA as WebviewAccountsState
participant Reg as ScannerRegistry(s)
participant Web as CEF Webviews
participant CEF as CEF Runtime
User->>App: ExitRequested
App->>WA: close_early_cef_webviews()
WA->>Reg: teardown_all_account_scanners() -> forget_all()
Reg->>Reg: abort stored scanner handles
App->>Web: pending_cef_webview_labels()
loop poll until budget or empty
App->>Web: get_webview(label)
alt exists
App->>App: sleep interval, retry
else closed
App->>App: proceed
end
end
App->>CEF: cef::shutdown()
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src-tauri/src/lib.rs`:
- Around line 1034-1048: The sync waiter wait_for_cef_webviews_to_close_sync
blocks the Tauri main event-loop with std::thread::sleep and must not be used
from RunEvent::ExitRequested or the tray quit handler; replace its blocking
polling by either calling the existing async variant
wait_for_cef_webviews_to_close_async (via perform_early_teardown_async) so
sleeps use tokio::time::sleep and don't block the event loop, or remove the
synchronous polling entirely and rely on the event-loop sweep; update
invocations in the ExitRequested and tray quit handler to use the async teardown
path (or spawn a dedicated background thread to run the current polling logic
off the main thread) and ensure any shared helpers (pending_cef_webview_labels)
remain accessible to the chosen path.
In `@app/src-tauri/src/webview_accounts/mod.rs`:
- Around line 635-642: The shutdown code currently only pushes label into
closed_labels when wv.close() succeeds, which means failed closes are not
tracked by the shutdown poll; change the logic so you record the label as soon
as app.get_webview(&label) returns Some (i.e. immediately push label into
closed_labels or a "tracked" set after get_webview succeeds), then attempt
wv.close() and log any Err from wv.close() without removing the label, ensuring
get_webview, wv.close, and closed_labels are the referenced symbols to modify so
failed closes remain visible to the teardown logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: c3a4d123-775f-421b-bee7-344fca04bdd4
⛔ Files ignored due to path filters (1)
app/src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (7)
app/src-tauri/src/discord_scanner/mod.rsapp/src-tauri/src/imessage_scanner/mod.rsapp/src-tauri/src/lib.rsapp/src-tauri/src/slack_scanner/mod.rsapp/src-tauri/src/telegram_scanner/mod.rsapp/src-tauri/src/webview_accounts/mod.rsapp/src-tauri/src/whatsapp_scanner/mod.rs
…i#1189) - Record acct_* labels in shutdown_all before close() so failed closes stay in the async drain poll set. - Remove std::thread::sleep-based wait from perform_early_teardown_sync so RunEvent::ExitRequested and tray quit do not block the CEF/UI event loop; document that async teardown still uses tokio polling where safe. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
♻️ Duplicate comments (1)
app/src-tauri/src/lib.rs (1)
1004-1012:⚠️ Potential issue | 🟠 Major | ⚡ Quick winTrack the prewarm label even when
close()fails.This only adds
cef-prewarmtoclosed_labelsonOk(()). If the webview still exists butwv.close()errors, the async drain poll loses visibility into that live CEF child and shutdown can still race on it. This is the same failure mode you just fixed for account webviews inapp/src-tauri/src/webview_accounts/mod.rs.Proposed fix
fn close_early_cef_webviews<R: tauri::Runtime>(app: &AppHandle<R>) -> Vec<String> { let mut closed_labels = Vec::new(); - if teardown_cef_prewarm(app).is_ok() { + if app.get_webview(CEF_PREWARM_LABEL).is_some() { closed_labels.push(CEF_PREWARM_LABEL.to_string()); + if let Err(err) = teardown_cef_prewarm(app) { + log::warn!("[cef-prewarm] early teardown close failed: {err}"); + } } if let Some(state) = app.try_state::<webview_accounts::WebviewAccountsState>() { closed_labels.extend(state.shutdown_all(app)); } closed_labels🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src-tauri/src/lib.rs` around lines 1004 - 1012, The function close_early_cef_webviews currently only pushes CEF_PREWARM_LABEL when teardown_cef_prewarm(app) returns Ok, which loses visibility if the prewarm webview exists but teardown errors; change the logic so CEF_PREWARM_LABEL is appended to closed_labels regardless of teardown_cef_prewarm's Result (push before or in both branches), and preserve/report the error from teardown_cef_prewarm (e.g., log or ignore after recording the label) so the prewarm child remains tracked even when teardown fails; refer to close_early_cef_webviews, teardown_cef_prewarm, and CEF_PREWARM_LABEL to locate and update the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@app/src-tauri/src/lib.rs`:
- Around line 1004-1012: The function close_early_cef_webviews currently only
pushes CEF_PREWARM_LABEL when teardown_cef_prewarm(app) returns Ok, which loses
visibility if the prewarm webview exists but teardown errors; change the logic
so CEF_PREWARM_LABEL is appended to closed_labels regardless of
teardown_cef_prewarm's Result (push before or in both branches), and
preserve/report the error from teardown_cef_prewarm (e.g., log or ignore after
recording the label) so the prewarm child remains tracked even when teardown
fails; refer to close_early_cef_webviews, teardown_cef_prewarm, and
CEF_PREWARM_LABEL to locate and update the code.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 28faa4d4-ca90-4bfa-a3fe-6a01eefe6008
📒 Files selected for processing (2)
app/src-tauri/src/lib.rsapp/src-tauri/src/webview_accounts/mod.rs
* feat(remotion): Ghosty character library with transparent MOV variants (tinyhumansai#1059) Co-authored-by: WOZCODE <contact@withwoz.com> * feat(composio/gmail): sync into memory tree (Slack-parity) (tinyhumansai#1056) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(scheduler-gate): throttle background AI on battery / busy CPU (tinyhumansai#1062) * fix(core,cef): run core in-process and stop orphaning CEF helpers on Cmd+Q (tinyhumansai#1061) * ci: add dedicated staging release workflow (tinyhumansai#1066) * fix(sentry): Rust source context + per-release deploy marker (tinyhumansai#405) (tinyhumansai#1067) * fix(welcome): re-enable OAuth buttons with focus/timeout recovery (tinyhumansai#1049) (tinyhumansai#1069) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(dependencies): update pnpm-lock.yaml and Cargo.lock for package… (tinyhumansai#1082) * fix(onboarding): personalize welcome agent greeting with user identity (tinyhumansai#1078) * fix(chat): make agent message bubbles fit content width (tinyhumansai#1083) * Feat/dmg checks (tinyhumansai#1084) * fix(linux): Add X11 platform flags to .deb package launcher (tinyhumansai#1087) Co-authored-by: unn-Known1 <unn-known1@users.noreply.github.com> * fix(sentry): auto-send React events; collapse core→tauri for desktop (tinyhumansai#1086) Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * fix(cef): run blank reload guard on the CEF UI thread (tinyhumansai#1092) * fix(app): reload webview instead of restart_app in dev mode (tinyhumansai#1068) (tinyhumansai#1071) * fix(linux): deliver X11 ozone flags via custom .desktop template (tinyhumansai#1091) * fix(webview-accounts): retry data-dir purge so CEF handle race doesn't leak cookies (tinyhumansai#1076) (tinyhumansai#1081) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * fix(webview/slack): media perms + deep-link isolation (tinyhumansai#1074) (tinyhumansai#1080) Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * ci(release): split staging vs production workflows; promote staging tags (tinyhumansai#1094) * Update release-staging.yml (tinyhumansai#1097) * chore(staging): v0.53.5 * chore(staging): v0.53.6 * ci(staging): cut staging from main; add act local-debug helper (tinyhumansai#1099) * chore(staging): v0.53.7 * fix(ci): correct sentry-cli download URL and trap scope (tinyhumansai#1100) * chore(staging): v0.53.8 * feat(chat): forward thread_id to backend for KV cache locality (tinyhumansai#1095) * fix(ci): bump pinned sentry-cli to 3.4.1 (2.34.2 was never published) (tinyhumansai#1102) * chore(staging): v0.53.9 * fix(ci): drop bash trap in upload_sentry_symbols.sh; inline cleanup (tinyhumansai#1103) * chore(staging): v0.53.10 * refactor(session): flatten session_raw/, switch md to YYYY_MM_DD (tinyhumansai#1098) * Add full Composio managed-auth toolkit catalog (tinyhumansai#1093) * ci: add diff-aware 80% coverage gate (Vitest + cargo-llvm-cov) (tinyhumansai#1104) * feat(scripts): pnpm work + pnpm debug for agent-driven workflows (tinyhumansai#1105) * ci: pull pnpm into CI image, drop redundant setup steps (tinyhumansai#1107) * docs: add Cursor Cloud specific instructions to AGENTS.md (tinyhumansai#1106) Co-authored-by: Cursor Agent <cursoragent@cursor.com> * chore(staging): v0.53.11 * docs: surface 80% coverage gate and scripts/debug runners (tinyhumansai#1108) * feat(app): show Composio integrations as sorted icon grid on Skills (tinyhumansai#1109) Co-authored-by: Cursor Agent <cursoragent@cursor.com> * feat(composio): client-side trigger enable/disable toggles (tinyhumansai#1110) * feat(skills): channels grid + integrations card polish; tolerant Composio trigger decode (tinyhumansai#1112) * chore(staging): v0.53.12 * feat(home): early-bird banner + assistant→agent terminology (tinyhumansai#1113) * feat(updater): in-app auto-update with auto-download + restart prompt (tinyhumansai#677) (tinyhumansai#1114) * chore(claude): add ship-and-babysit slash command (tinyhumansai#1115) * feat(home): EarlyBirdyBanner + agent terminology + LinkedIn enrichment model pin (tinyhumansai#1118) * fix(chat): single onboarding thread in sidebar after wizard (tinyhumansai#1116) Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Steven Enamakel <senamakel@users.noreply.github.com> * fix: filter out global namespace from citation chips (tinyhumansai#1124) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> * feat(nav): enable Memory tab in BottomTabBar (tinyhumansai#1125) * feat(memory): singleton ingestion + status RPC + UI pill (tinyhumansai#1126) * feat(human): mascot tab with viseme-driven lipsync (staging only) (tinyhumansai#1127) * Fix CEF zombie processes on full app close and restart (tinyhumansai#1128) Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * Update issue templates for GitHub issue types (tinyhumansai#1146) * feat(human): expand mascot expressions and tighten reply-speech state machine (tinyhumansai#1147) * feat(memory): ingestion pipeline + tree-architecture docs + ops/schemas split (tinyhumansai#1142) * feat(threads): surface live subagent work in parent thread (tinyhumansai#1122) (tinyhumansai#1159) * fix(human): keep mascot mouth animating when TTS ships no viseme data (tinyhumansai#1160) * feat(composio): consume backend markdownFormatted for LLM output (tinyhumansai#1165) * fix(subagent): lazy-register toolkit actions filtered out of fuzzy top-K (tinyhumansai#1162) * feat(memory): user-facing long-term memory window preset (tinyhumansai#1137) (tinyhumansai#1161) * fix(tauri-shell): proactively kill stale openhuman RPC on startup (tinyhumansai#1166) * chore(staging): v0.53.13 * fix(composio): per-action tool consumes backend markdownFormatted (tinyhumansai#1167) * fix(threads): persist selectedThreadId across reloads (tinyhumansai#1168) * feat(memory_tree): switch embed model to bge-m3 (1024-dim, 8K context) (tinyhumansai#1174) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(agent): drop redundant [Memory context] recall injection (tinyhumansai#1173) * chore(memory_tree): drop body-read timeouts on Ollama HTTP calls (tinyhumansai#1171) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(transcript): emit thread_id + fix orchestrator missing cost (tinyhumansai#1169) * fix(composio/gmail): phase out html2md, prefer text/plain MIME part (tinyhumansai#1170) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(tools): markdown output for internal tool results (tinyhumansai#1172) * feat(security): enforce prompt-injection guard before model and tool execution (tinyhumansai#1175) * fix(cef): popup paint dies after first frame — skip blank-page guard for popups (tinyhumansai#1079) (tinyhumansai#1182) Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> * chore(sentry): rename OPENHUMAN_SENTRY_DSN → OPENHUMAN_CORE_SENTRY_DSN (tinyhumansai#1186) * feat(remotion): add yellow mascot character with all animation variants (tinyhumansai#1193) Co-authored-by: Neel Mistry <neelmistry@Neels-MacBook-Pro.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(composio): hide raw connection ID, derive friendly label (tinyhumansai#1153) (tinyhumansai#1185) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * fix(windows): align install.ps1 MSI with per-machine scope (tinyhumansai#913) (tinyhumansai#1187) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tauri): deterministic CEF teardown on full app close (tinyhumansai#1120) (tinyhumansai#1189) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(composio): cap Gmail HTML body before strip (crash mitigation) (tinyhumansai#1191) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(auth): stop stale chat threads after signup (tinyhumansai#1192) Co-authored-by: Cursor <cursoragent@cursor.com> * feat(sentry): staging-only "Trigger Sentry Test" button (tinyhumansai#1072) (tinyhumansai#1183) * chore(staging): v0.53.14 * chore(staging): v0.53.15 * feat(composio): format trigger slugs into human-readable labels (tinyhumansai#1129) (tinyhumansai#1179) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * fix(ui): hide unsupported permission UI on non-macOS for Screen Intelligence (tinyhumansai#1194) Co-authored-by: Cursor <cursoragent@cursor.com> * chore(tauri-shell): retire embedded Gmail webview-account flow (tinyhumansai#1181) * feat(onboarding): replace welcome-agent bot with react-joyride walkthrough (tinyhumansai#1180) * chore(release): v0.53.16 * fix(threads): preserve selectedThreadId on cold-boot identity hydration (tinyhumansai#1196) * feat(core): version/shutdown/update RPCs + mid-thread integration refresh (tinyhumansai#1195) * fix(mascot): swap to yellow mascot via @remotion/player (tinyhumansai#1200) * feat(memory_tree): cloud-default LLM, queue priority, entity filter, Memory tab UI (tinyhumansai#1198) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Persist turn state + restore conversation history on cold-boot (tinyhumansai#1202) * feat(mascot): floating desktop mascot via native NSPanel + WKWebView (macOS) (tinyhumansai#1203) * fix(memory/tree): emit summary children as Obsidian wikilinks (tinyhumansai#1210) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(tools): coding-harness baseline primitives (tinyhumansai#1205) (tinyhumansai#1208) * docs: add Codex PR checklist for remote agents --------- Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Co-authored-by: WOZCODE <contact@withwoz.com> Co-authored-by: sanil-23 <sanil@vezures.xyz> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Cyrus Gray <144336577+graycyrus@users.noreply.github.com> Co-authored-by: CodeGhost21 <164498022+CodeGhost21@users.noreply.github.com> Co-authored-by: oxoxDev <164490987+oxoxDev@users.noreply.github.com> Co-authored-by: Mega Mind <146339422+M3gA-Mind@users.noreply.github.com> Co-authored-by: Gaurang Patel <ptelgm.yt@gmail.com> Co-authored-by: unn-Known1 <unn-known1@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Steven Enamakel <senamakel@users.noreply.github.com> Co-authored-by: Steven Enamakel's Droid <enamakel.agent@tinyhumans.ai> Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> Co-authored-by: YellowSnnowmann <167776381+YellowSnnowmann@users.noreply.github.com> Co-authored-by: Neil <neil@maha.xyz> Co-authored-by: Neel Mistry <neelmistry@Neels-MacBook-Pro.local> Co-authored-by: obchain <167975049+obchain@users.noreply.github.com> Co-authored-by: Jwalin Shah <jshah1331@gmail.com>
Summary
acct_*, prewarm) can finish closing beforecef::shutdown().WebviewAccountsState::shutdown_allso shutdown ordering is observable and coordinated.log::info!diagnostics around webview drain timing and pending labels.app/src-tauri/Cargo.lockso it matches the current0.53.13manifests and dependency graph (including removal of stalehtml2mdentries).Problem
Issue #1120: full app quit could race CEF teardown—child account webviews, CDP tasks, and fixed short sleeps did not reliably guarantee browsers were winding down before the runtime shut CEF down, risking helper processes and shutdown ordering issues.
Solution
Centralize early teardown: close prewarm + account webviews (collecting labels), stop long-lived scanner tasks, then poll
AppHandle::get_webviewfor those labels until they disappear or a ~300ms budget expires, with a short initial yield for the UI thread. Unit tests inwebview_accountscover label bookkeeping forshutdown_all.Submission Checklist
docs/TESTING-STRATEGY.md—webview_accountstests extended for returned close labels / invariants.pnpm test:coverageandpnpm test:rustcompleted successfully. CI Coverage Gate (diff-cover ≥ 80%) passed (merged Vitestlcov+cargo llvm-covfor core + Tauri per.github/workflows/coverage.yml).docs/TEST-COVERAGE-MATRIX.mdreflect this change — N/A: Tauri-shell shutdown behaviour only; no matrix row change.## Related— N/A: see above.docs/TESTING-STRATEGY.md)docs/RELEASE-MANUAL-SMOKE.md) — N/A: no release doc change required.Closes #NNNin the## RelatedsectionImpact
perform_early_teardown_*.Related
Summary by CodeRabbit
New Features
Bug Fixes
Tests