Fix deadlock when importing universal LC_* variable

The C++ version of this code simply copied the entire uvar table.
Today we take a reference. It's not clear which one is better.

Removal of locale variables like LC_ALL triggers variable change handlers
which call EnvStackImpl::get. This deadlocks because we still hold the lock
to protect the reference to all uvars.  Work around this.

Closes #10513
This commit is contained in:
Johannes Altmanninger 2024-05-21 23:03:37 +02:00
parent d07d0170ad
commit 2fa98ec20c
2 changed files with 25 additions and 13 deletions

View file

@ -738,8 +738,6 @@ pub fn env_init(paths: Option<&ConfigPaths>, do_uvars: bool, default_paths: bool
if !do_uvars {
UVAR_SCOPE_IS_GLOBAL.store(true);
} else {
// let vars = EnvStack::principal();
// Set up universal variables using the default path.
let mut callbacks = CallbackDataList::new();
uvars().initialize(&mut callbacks);
@ -749,18 +747,24 @@ pub fn env_init(paths: Option<&ConfigPaths>, do_uvars: bool, default_paths: bool
// Do not import variables that have the same name and value as
// an exported universal variable. See issues #5258 and #5348.
let uvars_locked = uvars();
let table = uvars_locked.get_table();
for (name, uvar) in table {
if !uvar.exports() {
continue;
}
let globals_to_skip = {
let mut to_skip = vec![];
let uvars_locked = uvars();
for (name, uvar) in uvars_locked.get_table() {
if !uvar.exports() {
continue;
}
// Look for a global exported variable with the same name.
let global = EnvStack::globals().getf(name, EnvMode::GLOBAL | EnvMode::EXPORT);
if global.is_some() && global.unwrap().as_string() == uvar.as_string() {
EnvStack::globals().remove(name, EnvMode::GLOBAL | EnvMode::EXPORT);
// Look for a global exported variable with the same name.
let global = EnvStack::globals().getf(name, EnvMode::GLOBAL | EnvMode::EXPORT);
if global.is_some() && global.unwrap().as_string() == uvar.as_string() {
to_skip.push(name.to_owned());
}
}
to_skip
};
for name in &globals_to_skip {
EnvStack::globals().remove(name, EnvMode::GLOBAL | EnvMode::EXPORT);
}
// Import any abbreviations from uvars.
@ -769,7 +773,8 @@ pub fn env_init(paths: Option<&ConfigPaths>, do_uvars: bool, default_paths: bool
let prefix_len = prefix.char_count();
let from_universal = true;
let mut abbrs = abbrs_get_set();
for (name, uvar) in table {
let uvars_locked = uvars();
for (name, uvar) in uvars_locked.get_table() {
if !name.starts_with(prefix) {
continue;
}

View file

@ -994,4 +994,11 @@ set -e nonevent
# CHECK: ONEVENT VARIABLE SET nonevent
# CHECK: ONEVENT VARIABLE ERASE nonevent
mkdir -p empty
env -u XDG_CONFIG_HOME HOME=$PWD/empty LC_ALL=en_US.UTF-8 $FISH -c 'set -Ux LC_ALL en_US.UTF-8'
env -u XDG_CONFIG_HOME HOME=$PWD/empty LC_ALL=en_US.UTF-8 $FISH -c 'set -S LC_ALL'
# CHECK: $LC_ALL: set in universal scope, exported, with 1 elements
# CHECK: $LC_ALL[1]: |en_US.UTF-8|
# CHECK: $LC_ALL: originally inherited as |en_US.UTF-8|
exit 0