make error model more autonomous

This commit is contained in:
Kunal Mohan 2021-04-09 21:52:46 +05:30
parent 313ac9f414
commit 380d69978c
4 changed files with 40 additions and 47 deletions

View file

@ -1,7 +1,7 @@
//! Error context system based on a thread-local representation of the call stack, itself based on
//! the instructions that are sent between threads.
use super::{AppInstruction, OPENCALLS};
use super::{AppInstruction, ASYNCOPENCALLS, OPENCALLS};
use crate::pty_bus::PtyInstruction;
use crate::screen::ScreenInstruction;
@ -72,6 +72,12 @@ pub fn handle_panic(
}
}
pub fn get_current_ctx() -> ErrorContext {
ASYNCOPENCALLS
.try_with(|ctx| *ctx.borrow())
.unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow()))
}
/// A representation of the call stack.
#[derive(Clone, Copy)]
pub struct ErrorContext {
@ -95,7 +101,9 @@ impl ErrorContext {
break;
}
}
OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self);
ASYNCOPENCALLS
.try_with(|ctx| *ctx.borrow_mut() = *self)
.unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self));
}
}

View file

@ -58,9 +58,6 @@ impl InputHandler {
fn handle_input(&mut self) {
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
err_ctx.add_call(ContextType::StdinHandler);
self.send_pty_instructions.update(err_ctx);
self.send_app_instructions.update(err_ctx);
self.send_screen_instructions.update(err_ctx);
let keybinds = self.config.keybinds.clone();
let alt_left_bracket = vec![27, 91];
loop {

View file

@ -25,9 +25,13 @@ use crate::cli::CliArgs;
use crate::common::input::config::Config;
use crate::layout::Layout;
use crate::panes::PaneId;
use async_std::task_local;
use command_is_executing::CommandIsExecuting;
use directories_next::ProjectDirs;
use errors::{AppContext, ContextType, ErrorContext, PluginContext, PtyContext, ScreenContext};
use errors::{
get_current_ctx, AppContext, ContextType, ErrorContext, PluginContext, PtyContext,
ScreenContext,
};
use input::handler::input_loop;
use os_input_output::OsApi;
use pty_bus::{PtyBus, PtyInstruction};
@ -72,32 +76,23 @@ enum SenderType<T: Clone> {
/// synchronously or asynchronously depending on the underlying [`SenderType`].
#[derive(Clone)]
pub struct SenderWithContext<T: Clone> {
err_ctx: ErrorContext,
sender: SenderType<T>,
}
impl<T: Clone> SenderWithContext<T> {
fn new(err_ctx: ErrorContext, sender: SenderType<T>) -> Self {
Self { err_ctx, sender }
fn new(sender: SenderType<T>) -> Self {
Self { sender }
}
/// Sends an event, along with the current [`ErrorContext`], on this
/// [`SenderWithContext`]'s channel.
pub fn send(&self, event: T) -> Result<(), mpsc::SendError<(T, ErrorContext)>> {
let err_ctx = get_current_ctx();
match self.sender {
SenderType::Sender(ref s) => s.send((event, self.err_ctx)),
SenderType::SyncSender(ref s) => s.send((event, self.err_ctx)),
SenderType::Sender(ref s) => s.send((event, err_ctx)),
SenderType::SyncSender(ref s) => s.send((event, err_ctx)),
}
}
/// Updates this [`SenderWithContext`]'s [`ErrorContext`]. This is the way one adds
/// a call to the error context.
///
/// Updating [`ErrorContext`]s works in this way so that these contexts are only ever
/// allocated on the stack (which is thread-specific), and not on the heap.
pub fn update(&mut self, new_ctx: ErrorContext) {
self.err_ctx = new_ctx;
}
}
unsafe impl<T: Clone> Send for SenderWithContext<T> {}
@ -109,6 +104,12 @@ thread_local!(
static OPENCALLS: RefCell<ErrorContext> = RefCell::default()
);
task_local! {
/// A key to some thread local storage (TLS) that holds a representation of the task's call
/// stack in the form of an [`ErrorContext`].
static ASYNCOPENCALLS: RefCell<ErrorContext> = RefCell::default()
}
/// Instructions related to the entire application.
#[derive(Clone)]
pub enum AppInstruction {
@ -140,25 +141,23 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let (send_screen_instructions, receive_screen_instructions): ChannelWithContext<
ScreenInstruction,
> = mpsc::channel();
let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
let mut send_screen_instructions =
SenderWithContext::new(err_ctx, SenderType::Sender(send_screen_instructions));
let send_screen_instructions =
SenderWithContext::new(SenderType::Sender(send_screen_instructions));
let (send_pty_instructions, receive_pty_instructions): ChannelWithContext<PtyInstruction> =
mpsc::channel();
let mut send_pty_instructions =
SenderWithContext::new(err_ctx, SenderType::Sender(send_pty_instructions));
let send_pty_instructions = SenderWithContext::new(SenderType::Sender(send_pty_instructions));
let (send_plugin_instructions, receive_plugin_instructions): ChannelWithContext<
PluginInstruction,
> = mpsc::channel();
let send_plugin_instructions =
SenderWithContext::new(err_ctx, SenderType::Sender(send_plugin_instructions));
SenderWithContext::new(SenderType::Sender(send_plugin_instructions));
let (send_app_instructions, receive_app_instructions): SyncChannelWithContext<AppInstruction> =
mpsc::sync_channel(0);
let send_app_instructions =
SenderWithContext::new(err_ctx, SenderType::SyncSender(send_app_instructions));
SenderWithContext::new(SenderType::SyncSender(send_app_instructions));
let mut pty_bus = PtyBus::new(
receive_pty_instructions,
@ -195,7 +194,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.recv()
.expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
pty_bus.send_screen_instructions.update(err_ctx);
match event {
PtyInstruction::SpawnTerminal(file_to_open) => {
let pid = pty_bus.spawn_terminal(file_to_open);
@ -272,8 +270,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.recv()
.expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Screen(ScreenContext::from(&event)));
screen.send_app_instructions.update(err_ctx);
screen.send_pty_instructions.update(err_ctx);
match event {
ScreenInstruction::PtyBytes(pid, vte_bytes) => {
let active_tab = screen.get_active_tab_mut().unwrap();
@ -433,9 +429,9 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
let wasm_thread = thread::Builder::new()
.name("wasm".to_string())
.spawn({
let mut send_pty_instructions = send_pty_instructions.clone();
let mut send_screen_instructions = send_screen_instructions.clone();
let mut send_app_instructions = send_app_instructions.clone();
let send_pty_instructions = send_pty_instructions.clone();
let send_screen_instructions = send_screen_instructions.clone();
let send_app_instructions = send_app_instructions.clone();
let store = Store::default();
let mut plugin_id = 0;
@ -445,9 +441,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.recv()
.expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Plugin(PluginContext::from(&event)));
send_screen_instructions.update(err_ctx);
send_pty_instructions.update(err_ctx);
send_app_instructions.update(err_ctx);
match event {
PluginInstruction::Load(pid_tx, path) => {
let project_dirs =
@ -556,16 +549,14 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.name("ipc_server".to_string())
.spawn({
use std::io::Read;
let mut send_pty_instructions = send_pty_instructions.clone();
let mut send_screen_instructions = send_screen_instructions.clone();
let send_pty_instructions = send_pty_instructions.clone();
let send_screen_instructions = send_screen_instructions.clone();
move || {
std::fs::remove_file(ZELLIJ_IPC_PIPE).ok();
let listener = std::os::unix::net::UnixListener::bind(ZELLIJ_IPC_PIPE)
.expect("could not listen on ipc socket");
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
err_ctx.add_call(ContextType::IpcServer);
send_pty_instructions.update(err_ctx);
send_screen_instructions.update(err_ctx);
for stream in listener.incoming() {
match stream {
@ -637,8 +628,6 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
.expect("failed to receive app instruction on channel");
err_ctx.add_call(ContextType::App(AppContext::from(&app_instruction)));
send_screen_instructions.update(err_ctx);
send_pty_instructions.update(err_ctx);
match app_instruction {
AppInstruction::Exit => {
break;

View file

@ -8,11 +8,11 @@ use ::std::sync::mpsc::Receiver;
use ::std::time::{Duration, Instant};
use std::path::PathBuf;
use super::{ScreenInstruction, SenderWithContext, OPENCALLS};
use super::{ScreenInstruction, SenderWithContext};
use crate::os_input_output::OsApi;
use crate::utils::logging::debug_to_file;
use crate::{
errors::{ContextType, ErrorContext},
errors::{get_current_ctx, ContextType, ErrorContext},
panes::PaneId,
};
use crate::{layout::Layout, wasm_vm::PluginInstruction};
@ -89,15 +89,14 @@ pub struct PtyBus {
fn stream_terminal_bytes(
pid: RawFd,
mut send_screen_instructions: SenderWithContext<ScreenInstruction>,
send_screen_instructions: SenderWithContext<ScreenInstruction>,
os_input: Box<dyn OsApi>,
debug: bool,
) -> JoinHandle<()> {
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
let mut err_ctx = get_current_ctx();
task::spawn({
async move {
err_ctx.add_call(ContextType::AsyncTask);
send_screen_instructions.update(err_ctx);
let mut terminal_bytes = ReadFromPid::new(&pid, os_input);
let mut last_byte_receive_time: Option<Instant> = None;