Fix (and test) for a deadlock that can happen while waiting for protocol info (#12633)

# Description

The local socket PR introduced a `Waitable` type, which could either
hold a value or be waited on until a value is available. Unlike a
channel, it would always return that value once set.

However, one issue with this design was that there was no way to detect
whether a value would ever be written. This splits the writer into a
different type `WaitableMut`, so that when it is dropped, waiting
threads can fail (because they'll never get a value).

# Tests + Formatting

A test has been added to `stress_internals` to make sure this fails in
the right way.

- 🟢 `toolkit fmt`
- 🟢 `toolkit clippy`
- 🟢 `toolkit test`
- 🟢 `toolkit test stdlib`
This commit is contained in:
Devyn Cairns
2024-04-24 06:44:04 -07:00
committed by GitHub
parent 0f645b3bb6
commit c52884b3c8
8 changed files with 184 additions and 57 deletions

View File

@ -1,3 +1,5 @@
use std::{sync::mpsc, time::Duration};
use nu_test_support::nu_with_plugins;
fn ensure_stress_env_vars_unset() {
@ -75,6 +77,30 @@ fn test_failing_local_socket_fallback() {
assert!(result.out.contains("local_socket_path: None"));
}
#[test]
fn test_exit_before_hello_stdio() {
ensure_stress_env_vars_unset();
// This can deadlock if not handled properly, so we try several times and timeout
for _ in 0..5 {
let (tx, rx) = mpsc::channel();
std::thread::spawn(move || {
let result = nu_with_plugins!(
cwd: ".",
envs: vec![
("STRESS_EXIT_BEFORE_HELLO", "1"),
],
plugin: ("nu_plugin_stress_internals"),
"stress_internals"
);
let _ = tx.send(result);
});
let result = rx
.recv_timeout(Duration::from_secs(15))
.expect("timed out. probably a deadlock");
assert!(!result.status.success());
}
}
#[test]
fn test_exit_early_stdio() {
ensure_stress_env_vars_unset();