feat: quiesce the VM and unmount windows before native application exit
This commit is contained in:
@@ -233,6 +233,7 @@ struct Session {
|
||||
layout_cancels: usize,
|
||||
paint_cancels: usize,
|
||||
paint_slices: usize,
|
||||
exiting: bool,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Session {
|
||||
@@ -771,6 +772,9 @@ fn create_window(
|
||||
"window creation is forbidden during read-only rendering",
|
||||
));
|
||||
}
|
||||
if state.exiting {
|
||||
return Err(invalid("runtime is exiting; window creation is forbidden"));
|
||||
}
|
||||
if state.presentations.len() >= 16 {
|
||||
return Err(VmError::ExecutionLimit("16 window budget"));
|
||||
}
|
||||
@@ -1812,6 +1816,77 @@ fn project_tabs(
|
||||
}
|
||||
|
||||
impl UiBridge {
|
||||
/// Detach all occurrences without running layout/paint. All windows are
|
||||
/// already closed before source unmount hooks run, so they cannot remount.
|
||||
pub fn shutdown(&self, vm: &mut Vm, callbacks: bool) -> Result<(usize, usize, usize)> {
|
||||
let tasks = self.tasks.quiesce(vm)?;
|
||||
let mut drivers = Vec::new();
|
||||
let mut mounts = 0;
|
||||
let prepared = (|| -> Result<usize> {
|
||||
let mut state = self
|
||||
.session
|
||||
.lock()
|
||||
.map_err(|_| anyhow::anyhow!("UI session poisoned"))?;
|
||||
state.exiting = true;
|
||||
let ids: Vec<_> = state.presentations.keys().copied().collect();
|
||||
for id in &ids {
|
||||
let view = state.presentations.get_mut(id).expect("known window");
|
||||
let desktop = view.desktop;
|
||||
mounts += view.mounts.active.len();
|
||||
if callbacks {
|
||||
if let Some(hooks) = view.mounts.synchronize(
|
||||
vm,
|
||||
desktop,
|
||||
None,
|
||||
Vec::new(),
|
||||
&crate::document::Frame::default(),
|
||||
false,
|
||||
)? {
|
||||
drivers.push((*id, hooks));
|
||||
}
|
||||
} else {
|
||||
view.mounts.retire(vm, desktop)?;
|
||||
}
|
||||
let window = object(Some(&vm.get_attr(desktop, "window")?))?;
|
||||
crate::window::retire(vm, window)?;
|
||||
vm.set_attr(desktop, "pending", Value::Nil)?;
|
||||
vm.set_attr(desktop, "focus", Value::Nil)?;
|
||||
view.pending = Some(None);
|
||||
view.active = None;
|
||||
view.closing = true;
|
||||
}
|
||||
Ok(ids.len())
|
||||
})();
|
||||
let mut result = prepared.map(|windows| (windows, mounts, tasks));
|
||||
for (id, hooks) in drivers {
|
||||
if result.is_ok() {
|
||||
let call = (|| -> Result<()> {
|
||||
self.select_window(id)?;
|
||||
let value = vm
|
||||
.call_with_limits(
|
||||
hooks,
|
||||
&[],
|
||||
endor_core::ExecutionLimits {
|
||||
max_frames: 128,
|
||||
max_instructions: 1_000_000,
|
||||
},
|
||||
)
|
||||
.map_err(|error| crate::endor_host::runtime_error(vm, error))?;
|
||||
vm.release_value(value)?;
|
||||
Ok(())
|
||||
})();
|
||||
if let Err(error) = call {
|
||||
result = Err(error);
|
||||
}
|
||||
}
|
||||
if let Err(error) = vm.release(hooks) {
|
||||
result = Err(error.into());
|
||||
}
|
||||
}
|
||||
self.select_window(crate::window::MAIN_WINDOW)?;
|
||||
result
|
||||
}
|
||||
|
||||
pub fn has_cooperative_tasks(&self) -> bool {
|
||||
self.tasks.has_cooperative_work()
|
||||
}
|
||||
@@ -2902,6 +2977,7 @@ pub fn install(vm: &mut Vm, roots: &mut Vec<ObjectRef>) -> Result<UiBridge> {
|
||||
layout_cancels: 0,
|
||||
paint_cancels: 0,
|
||||
paint_slices: 0,
|
||||
exiting: false,
|
||||
}));
|
||||
let mut public_functions = Vec::new();
|
||||
let mut factories = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user