Fix problems with tests run in parallel (https://github.com/enso-org/ide/pull/332)

Global spawner was made thread local.

Original commit: 8931df9b44
This commit is contained in:
Adam Obuchowicz 2020-04-01 17:33:04 +02:00 committed by GitHub
parent 160811f506
commit 97c146fbdc
2 changed files with 40 additions and 35 deletions

View File

@ -26,11 +26,16 @@ use crate::prelude::*;
use futures::task::LocalSpawnExt; use futures::task::LocalSpawnExt;
use futures::task::LocalSpawn; use futures::task::LocalSpawn;
/// Global spawner handle. thread_local! {
/// /// Global spawner handle.
/// It should be set up once, as part of the IDE initialization routine and ///
/// remain accessible indefinitely. /// It should be set up once, as part of the IDE initialization routine and
static mut SPAWNER: Option<Box<dyn LocalSpawn>> = None; /// remain accessible indefinitely.
///
/// This is made thread local for tests which may be run in parallel; Each test should set
/// executor independently.
static SPAWNER: RefCell<Option<Box<dyn LocalSpawn>>> = default();
}
/// Sets the global spawner. It will remain accessible until it is set again to /// Sets the global spawner. It will remain accessible until it is set again to
/// something else. /// something else.
@ -40,29 +45,26 @@ static mut SPAWNER: Option<Box<dyn LocalSpawn>> = None;
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub fn set_spawner(spawner_to_set:impl LocalSpawn + 'static) { pub fn set_spawner(spawner_to_set:impl LocalSpawn + 'static) {
// Note [Global Executor Safety] // Note [Global Executor Safety]
unsafe { SPAWNER.with(|s| *s.borrow_mut() = Some(Box::new(spawner_to_set)));
SPAWNER = Some(Box::new(spawner_to_set));
}
} }
// Note [Global Executor Safety]
// =============================
// This is safe, because the global mutable state is only accessed through the
// two functions provided in this module, the code will be used only in a web
// single-threaded environment (so no race conditions are possible), and the
// functions do not leak reference to the global spawner.
/// Spawns a task using the global spawner. /// Spawns a task using the global spawner.
/// Panics, if called when there is no global spawner set or if it fails to /// Panics, if called when there is no global spawner set or if it fails to
/// spawn task (e.g. because the connected executor was prematurely dropped). /// spawn task (e.g. because the connected executor was prematurely dropped).
#[allow(unsafe_code)] #[allow(unsafe_code)]
pub fn spawn(f:impl Future<Output=()> + 'static) { pub fn spawn(f:impl Future<Output=()> + 'static) {
// Note [Global Executor Safety] SPAWNER.with(|spawner| {
let spawner = unsafe {
let error_msg = "No global executor has been provided."; let error_msg = "No global executor has been provided.";
SPAWNER.as_mut().expect(error_msg) // Note [Global Executor Safety]
}; let mut borrowed = spawner.borrow_mut();
let unwrapped = borrowed.as_mut().expect(error_msg);
let error_msg = "Failed to spawn the task. Global executor might have been dropped."; let error_msg = "Failed to spawn the task. Global executor might have been dropped.";
spawner.spawn_local(f).expect(error_msg); unwrapped.spawn_local(f).expect(error_msg);
});
} }
// Note [Global Executor Safety]
// =============================
// This borrowing is safe, because the global mutable state is only accessed through the
// two functions provided in this module, and the functions do not leak reference to the global
// spawner.

View File

@ -219,7 +219,7 @@ mod test {
#[test] #[test]
fn notifying() { fn notifying() {
let mut test = TestWithLocalPoolExecutor::set_up(); let mut test = TestWithLocalPoolExecutor::set_up();
test.run_task(async { test.run_task(async {
let module = Module::default(); let module = Module::default();
let mut text_subscription = module.subscribe_text_notifications(); let mut text_subscription = module.subscribe_text_notifications();
@ -259,21 +259,24 @@ mod test {
#[test] #[test]
fn handling_metadata() { fn handling_metadata() {
let module = Module::default(); let mut test = TestWithLocalPoolExecutor::set_up();
test.run_task(async {
let module = Module::default();
let id = Uuid::new_v4(); let id = Uuid::new_v4();
let initial_md = module.node_metadata(id.clone()); let initial_md = module.node_metadata(id.clone());
assert!(initial_md.is_err()); assert!(initial_md.is_err());
let md_to_set = NodeMetadata {position:Some(Position::new(1.0, 2.0))}; let md_to_set = NodeMetadata {position:Some(Position::new(1.0, 2.0))};
module.set_node_metadata(id.clone(),md_to_set.clone()); module.set_node_metadata(id.clone(),md_to_set.clone());
assert_eq!(md_to_set.position, module.node_metadata(id.clone()).unwrap().position); assert_eq!(md_to_set.position, module.node_metadata(id.clone()).unwrap().position);
let new_pos = Position::new(4.0, 5.0); let new_pos = Position::new(4.0, 5.0);
module.with_node_metadata(id.clone(), |md| { module.with_node_metadata(id.clone(), |md| {
assert_eq!(md_to_set.position, md.position); assert_eq!(md_to_set.position, md.position);
md.position = Some(new_pos); md.position = Some(new_pos);
});
assert_eq!(Some(new_pos), module.node_metadata(id).unwrap().position);
}); });
assert_eq!(Some(new_pos), module.node_metadata(id).unwrap().position);
} }
} }