Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 59 additions & 11 deletions plugins/window-state/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -493,13 +493,38 @@ impl Builder {
#[cfg(not(target_env = "ohos"))]
let minimized = window_clone.is_minimized().unwrap_or_default();
if !minimized {
let mut c = cache.lock().unwrap();
if let Some(state) = c.get_mut(&label) {
state.prev_x = state.x;
state.prev_y = state.y;

state.x = position.x;
state.y = position.y;
// OHOS: use try_lock instead of a blocking lock. During a Moved
// (window drag) storm, another main-thread path may hold this lock and
// issue a synchronous NAPI call (OHOS window getters use
// get_main_thread_env + func.call, not a run_on_main_thread/recv
// channel); a blocking lock here would contend with it and stall the
// main thread, leading to appfreeze (THREAD_BLOCK_6S, symbolicated
// 2026-08-13). try_lock skips the contended frame instead of stalling.
// Skipping a frame does not lose the final state: the explicit save
// command re-queries the current position via update_state before
// persisting (OHOS skips the RunEvent::Exit auto-save). Other platforms
// short-circuit their event loop and stay non-blocking, so they keep
// the original lock.
#[cfg(target_env = "ohos")]
{
if let Ok(mut c) = cache.try_lock() {
if let Some(state) = c.get_mut(&label) {
state.prev_x = state.x;
state.prev_y = state.y;
state.x = position.x;
state.y = position.y;
}
}
}
#[cfg(not(target_env = "ohos"))]
{
let mut c = cache.lock().unwrap();
if let Some(state) = c.get_mut(&label) {
state.prev_x = state.x;
state.prev_y = state.y;
state.x = position.x;
state.y = position.y;
}
}
}
}
Expand Down Expand Up @@ -531,10 +556,33 @@ impl Builder {
!window_clone.is_minimized().unwrap_or_default() && !is_maximized
};
if save {
let mut c = cache.lock().unwrap();
if let Some(state) = c.get_mut(&label) {
state.width = size.width;
state.height = size.height;
// OHOS: use try_lock instead of a blocking lock. During a Resized

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

🟡 根因质疑 — 注释说 "another main-thread path may hold this lock and issue a synchronous NAPI call"。但 save_window_state 是 async command,跑在 tokio worker 线程而非主线程;worker 线程的 NAPI window getter 经 get_main_thread_env()(thread_local,非主线程返回 None)→ 直接 Err 返默认值,不会同步等主线程。详见总结评论里的完整分析。想确认:appfreeze 时持锁者到底是谁?

// (window scaling) storm, another main-thread path may hold this lock
// and issue a synchronous NAPI call (OHOS window getters use
// get_main_thread_env + func.call, not a run_on_main_thread/recv
// channel); a blocking lock here would contend and stall the main
// thread -> appfreeze (THREAD_BLOCK_6S, symbolicated at the original
// cache.lock().unwrap() on this line, 2026-08-13). try_lock skips the
// contended frame. Skipping a frame does not lose the final state: the
// explicit save command re-queries the current size via update_state
// before persisting (OHOS skips the RunEvent::Exit auto-save). Other
// platforms keep the original lock.
#[cfg(target_env = "ohos")]
{
if let Ok(mut c) = cache.try_lock() {
if let Some(state) = c.get_mut(&label) {
state.width = size.width;
state.height = size.height;
}
}
}
#[cfg(not(target_env = "ohos"))]
{
let mut c = cache.lock().unwrap();
if let Some(state) = c.get_mut(&label) {
state.width = size.width;
state.height = size.height;
}
}
}
}
Expand Down