fix(mac): take the rule-list locks in one order, so a rule check and a config load cannot deadlock

BEHAVIOUR (lock order only). window.rs (should_manage rule check) took the
regex list, then ignore, then manage, then floating; static_config.rs
(apply_globals) took ignore, regex, manage, floating. Two threads, one
checking a window and one loading the config, could each hold what the
other waits for. Same defect as the KDE fix ef1d3ef0; the order is now the
KDE one minus the lists macOS lacks: floating, (session floating),
ignore, manage, (workspace rules), regex. Both sites acquire in that order
and the order is documented at the statics in lib.rs. The other holders
(process_event.rs floating -> regex, process_command.rs floating ->
session floating, window_manager.rs workspace rules -> regex) already
follow it; every other site takes a single rule list.

Verified: macOS fake-CC type-check (tools/fast-check) PASS. Not verified:
cannot run on Linux; a deadlock is not reproducible by reading anyway. Gate
needed: none runnable here; hand check on a Mac (docs/dev/macos-handcheck.md):
reload the config while windows open/close.
This commit is contained in:
LGUG2Z committed 2026-10-31 09:54:18 -07:00
1 parent 633e7453cd
commit 01b9cbed91
3 files changed
+14 -5

No files matched your search

+4
View File
@@ -122,6 +122,10 @@ lazy_static! {
Arc::new(Mutex::new(HashMap::new()));
static ref WORKSPACE_MATCHING_RULES: Arc<Mutex<Vec<WorkspaceMatchingRule>>> =
Arc::new(Mutex::new(Vec::new()));
// LOCK ORDER for the rule lists (as in the KDE tree, minus the lists macOS does not have):
// FLOATING_APPLICATIONS, SESSION_FLOATING_APPLICATIONS, IGNORE_IDENTIFIERS, MANAGE_IDENTIFIERS,
// WORKSPACE_MATCHING_RULES, REGEX_IDENTIFIERS. Take the ones you need in that order and never
// the other way round; a thread that does can deadlock with one that does not.
static ref REGEX_IDENTIFIERS: Arc<Mutex<HashMap<String, Regex>>> =
Arc::new(Mutex::new(HashMap::new()));
static ref MANAGE_IDENTIFIERS: Arc<Mutex<Vec<MatchingRule>>> = Arc::new(Mutex::new(vec![]));
@@ -545,16 +545,18 @@ impl ApplyGlobals for StaticConfig {
// transparency_manager::TRANSPARENCY_ALPHA
// .store(self.transparency_alpha.unwrap_or(200), Ordering::SeqCst);
// the order every other place takes them in (floating, ignore, manage, regex; see lib.rs):
// a thread that takes two in another order can wait for one that holds what it needs
let mut floating_applications = FLOATING_APPLICATIONS.lock();
let mut ignore_identifiers = IGNORE_IDENTIFIERS.lock();
let mut regex_identifiers = REGEX_IDENTIFIERS.lock();
let mut manage_identifiers = MANAGE_IDENTIFIERS.lock();
let mut regex_identifiers = REGEX_IDENTIFIERS.lock();
// let mut tray_and_multi_window_identifiers = TRAY_AND_MULTI_WINDOW_IDENTIFIERS.lock();
// let mut object_name_change_identifiers = OBJECT_NAME_CHANGE_ON_LAUNCH.lock();
// let mut object_name_change_title_ignore_list = OBJECT_NAME_CHANGE_TITLE_IGNORE_LIST.lock();
// let mut layered_identifiers = LAYERED_WHITELIST.lock();
// let mut transparency_blacklist = TRANSPARENCY_BLACKLIST.lock();
// let mut slow_application_identifiers = SLOW_APPLICATION_IDENTIFIERS.lock();
let mut floating_applications = FLOATING_APPLICATIONS.lock();
// let mut no_titlebar_applications = NO_TITLEBAR.lock();
let mut titleless_applications = TITLELESS_APPLICATIONS.lock();
let mut tabbed_applications = TABBED_APPLICATIONS.lock();
+6 -3
View File
@@ -1641,9 +1641,14 @@ fn window_is_eligible(
}
}
// Every place that holds two of the rule lists takes them in this order: floating, ignore,
// manage, regex (see the statics in lib.rs). A thread that takes two in another order can wait
// for one that holds what it needs, and both wait for ever.
let floating_identifiers = FLOATING_APPLICATIONS.lock();
let ignore_identifiers = IGNORE_IDENTIFIERS.lock();
let manage_identifiers = MANAGE_IDENTIFIERS.lock();
let regex_identifiers = REGEX_IDENTIFIERS.lock();
let ignore_identifiers = IGNORE_IDENTIFIERS.lock();
let should_ignore = if let Some(rule) = should_act(
title,
exe_name,
@@ -1658,7 +1663,6 @@ fn window_is_eligible(
false
};
let manage_identifiers = MANAGE_IDENTIFIERS.lock();
let managed_override = if let Some(rule) = should_act(
title,
exe_name,
@@ -1673,7 +1677,6 @@ fn window_is_eligible(
false
};
let floating_identifiers = FLOATING_APPLICATIONS.lock();
if let Some(rule) = should_act(
title,
exe_name,