feat(infrastructure): introduce ErrorContext for tracking errors across threads (#83)

* Implement ErrorContext for tracking errors across threads

* reorder Instruction and ErrorContext

* Add ContextType, AppContext, ScreenContext, PtyContext

* Use ArrayVec in ErrorContext

* increase MAX_THREAD_CALL_STACK to 6

* Custom implement Debug for ErrorContext ad ContextType and color output

* Use os_input instead of println!()

* Use array instead of ArrayVec and some cleanup

* Introduce SenderWithContext

* Keep arrayvec at v0.5.1
This commit is contained in:
Kunal Mohan 2020-12-09 22:31:26 +05:30 committed by GitHub
parent 0a05e4e34a
commit 92d1bcff4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 299 additions and 53 deletions

View file

@ -1,10 +1,17 @@
use crate::AppInstruction; use crate::pty_bus::PtyInstruction;
use crate::screen::ScreenInstruction;
use crate::{AppInstruction, SenderWithContext, OPENCALLS};
use backtrace::Backtrace; use backtrace::Backtrace;
use std::fmt::{Display, Error, Formatter};
use std::panic::PanicInfo; use std::panic::PanicInfo;
use std::sync::mpsc::SyncSender;
use std::{process, thread}; use std::{process, thread};
pub fn handle_panic(info: &PanicInfo<'_>, send_app_instructions: &SyncSender<AppInstruction>) { const MAX_THREAD_CALL_STACK: usize = 6;
pub fn handle_panic(
info: &PanicInfo<'_>,
send_app_instructions: &SenderWithContext<AppInstruction>,
) {
let backtrace = Backtrace::new(); let backtrace = Backtrace::new();
let thread = thread::current(); let thread = thread::current();
let thread = thread.name().unwrap_or("unnamed"); let thread = thread.name().unwrap_or("unnamed");
@ -14,9 +21,12 @@ pub fn handle_panic(info: &PanicInfo<'_>, send_app_instructions: &SyncSender<App
None => info.payload().downcast_ref::<String>().map(|s| &**s), None => info.payload().downcast_ref::<String>().map(|s| &**s),
}; };
let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
let backtrace = match (info.location(), msg) { let backtrace = match (info.location(), msg) {
(Some(location), Some(msg)) => format!( (Some(location), Some(msg)) => format!(
"\nthread '{}' panicked at '{}': {}:{}\n{:?}", "{}\n\u{1b}[0;0mError: \u{1b}[0;31mthread '{}' panicked at '{}': {}:{}\n\u{1b}[0;0m{:?}",
err_ctx,
thread, thread,
msg, msg,
location.file(), location.file(),
@ -24,17 +34,21 @@ pub fn handle_panic(info: &PanicInfo<'_>, send_app_instructions: &SyncSender<App
backtrace backtrace
), ),
(Some(location), None) => format!( (Some(location), None) => format!(
"\nthread '{}' panicked: {}:{}\n{:?}", "{}\n\u{1b}[0;0mError: \u{1b}[0;31mthread '{}' panicked: {}:{}\n\u{1b}[0;0m{:?}",
err_ctx,
thread, thread,
location.file(), location.file(),
location.line(), location.line(),
backtrace backtrace
), ),
(None, Some(msg)) => format!( (None, Some(msg)) => format!(
"\nthread '{}' panicked at '{}'\n{:?}", "{}\n\u{1b}[0;0mError: \u{1b}[0;31mthread '{}' panicked at '{}'\n\u{1b}[0;0m{:?}",
thread, msg, backtrace err_ctx, thread, msg, backtrace
),
(None, None) => format!(
"{}\n\u{1b}[0;0mError: \u{1b}[0;31mthread '{}' panicked\n\u{1b}[0;0m{:?}",
err_ctx, thread, backtrace
), ),
(None, None) => format!("\nthread '{}' panicked\n{:?}", thread, backtrace),
}; };
if thread == "main" { if thread == "main" {
@ -46,3 +60,165 @@ pub fn handle_panic(info: &PanicInfo<'_>, send_app_instructions: &SyncSender<App
.unwrap(); .unwrap();
} }
} }
#[derive(Clone, Copy)]
pub struct ErrorContext {
calls: [ContextType; MAX_THREAD_CALL_STACK],
}
impl ErrorContext {
pub fn new() -> Self {
Self {
calls: [ContextType::Empty; MAX_THREAD_CALL_STACK],
}
}
pub fn add_call(&mut self, call: ContextType) {
for ctx in self.calls.iter_mut() {
if *ctx == ContextType::Empty {
*ctx = call;
break;
}
}
OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self);
}
}
impl Display for ErrorContext {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
writeln!(f, "Originating Thread(s):")?;
for (index, ctx) in self.calls.iter().enumerate() {
if *ctx == ContextType::Empty {
break;
}
writeln!(f, "\u{1b}[0;0m{}. {}", index + 1, ctx)?;
}
Ok(())
}
}
#[derive(Copy, Clone, PartialEq)]
pub enum ContextType {
Screen(ScreenContext),
Pty(PtyContext),
App(AppContext),
IPCServer,
StdinHandler,
AsyncTask,
Empty,
}
impl Display for ContextType {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
let purple = "\u{1b}[1;35m";
let green = "\u{1b}[0;32m";
match *self {
ContextType::Screen(c) => write!(f, "{}screen_thread: {}{:?}", purple, green, c),
ContextType::Pty(c) => write!(f, "{}pty_thread: {}{:?}", purple, green, c),
ContextType::App(c) => write!(f, "{}main_thread: {}{:?}", purple, green, c),
ContextType::IPCServer => write!(f, "{}ipc_server: {}AcceptInput", purple, green),
ContextType::StdinHandler => {
write!(f, "{}stdin_handler_thread: {}AcceptInput", purple, green)
}
ContextType::AsyncTask => {
write!(f, "{}stream_terminal_bytes: {}AsyncTask", purple, green)
}
ContextType::Empty => write!(f, ""),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ScreenContext {
HandlePtyEvent,
Render,
NewPane,
HorizontalSplit,
VerticalSplit,
WriteCharacter,
ResizeLeft,
ResizeRight,
ResizeDown,
ResizeUp,
MoveFocus,
MoveFocusLeft,
MoveFocusDown,
MoveFocusUp,
MoveFocusRight,
Quit,
ScrollUp,
ScrollDown,
ClearScroll,
CloseFocusedPane,
ToggleActiveTerminalFullscreen,
ClosePane,
ApplyLayout,
}
impl From<&ScreenInstruction> for ScreenContext {
fn from(screen_instruction: &ScreenInstruction) -> Self {
match *screen_instruction {
ScreenInstruction::Pty(..) => ScreenContext::HandlePtyEvent,
ScreenInstruction::Render => ScreenContext::Render,
ScreenInstruction::NewPane(_) => ScreenContext::NewPane,
ScreenInstruction::HorizontalSplit(_) => ScreenContext::HorizontalSplit,
ScreenInstruction::VerticalSplit(_) => ScreenContext::VerticalSplit,
ScreenInstruction::WriteCharacter(_) => ScreenContext::WriteCharacter,
ScreenInstruction::ResizeLeft => ScreenContext::ResizeLeft,
ScreenInstruction::ResizeRight => ScreenContext::ResizeRight,
ScreenInstruction::ResizeDown => ScreenContext::ResizeDown,
ScreenInstruction::ResizeUp => ScreenContext::ResizeUp,
ScreenInstruction::MoveFocus => ScreenContext::MoveFocus,
ScreenInstruction::MoveFocusLeft => ScreenContext::MoveFocusLeft,
ScreenInstruction::MoveFocusDown => ScreenContext::MoveFocusDown,
ScreenInstruction::MoveFocusUp => ScreenContext::MoveFocusUp,
ScreenInstruction::MoveFocusRight => ScreenContext::MoveFocusRight,
ScreenInstruction::Quit => ScreenContext::Quit,
ScreenInstruction::ScrollUp => ScreenContext::ScrollUp,
ScreenInstruction::ScrollDown => ScreenContext::ScrollDown,
ScreenInstruction::ClearScroll => ScreenContext::ClearScroll,
ScreenInstruction::CloseFocusedPane => ScreenContext::CloseFocusedPane,
ScreenInstruction::ToggleActiveTerminalFullscreen => {
ScreenContext::ToggleActiveTerminalFullscreen
}
ScreenInstruction::ClosePane(_) => ScreenContext::ClosePane,
ScreenInstruction::ApplyLayout(_) => ScreenContext::ApplyLayout,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PtyContext {
SpawnTerminal,
SpawnTerminalVertically,
SpawnTerminalHorizontally,
ClosePane,
Quit,
}
impl From<&PtyInstruction> for PtyContext {
fn from(pty_instruction: &PtyInstruction) -> Self {
match *pty_instruction {
PtyInstruction::SpawnTerminal(_) => PtyContext::SpawnTerminal,
PtyInstruction::SpawnTerminalVertically(_) => PtyContext::SpawnTerminalVertically,
PtyInstruction::SpawnTerminalHorizontally(_) => PtyContext::SpawnTerminalHorizontally,
PtyInstruction::ClosePane(_) => PtyContext::ClosePane,
PtyInstruction::Quit => PtyContext::Quit,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AppContext {
Exit,
Error,
}
impl From<&AppInstruction> for AppContext {
fn from(app_instruction: &AppInstruction) -> Self {
match *app_instruction {
AppInstruction::Exit => AppContext::Exit,
AppInstruction::Error(_) => AppContext::Error,
}
}
}

View file

@ -1,28 +1,27 @@
/// Module for handling input /// Module for handling input
use std::sync::mpsc::{Sender, SyncSender}; use crate::errors::ContextType;
use crate::os_input_output::OsApi; use crate::os_input_output::OsApi;
use crate::pty_bus::PtyInstruction; use crate::pty_bus::PtyInstruction;
use crate::screen::ScreenInstruction; use crate::screen::ScreenInstruction;
use crate::AppInstruction;
use crate::CommandIsExecuting; use crate::CommandIsExecuting;
use crate::{AppInstruction, SenderWithContext, OPENCALLS};
struct InputHandler { struct InputHandler {
mode: InputMode, mode: InputMode,
os_input: Box<dyn OsApi>, os_input: Box<dyn OsApi>,
command_is_executing: CommandIsExecuting, command_is_executing: CommandIsExecuting,
send_screen_instructions: Sender<ScreenInstruction>, send_screen_instructions: SenderWithContext<ScreenInstruction>,
send_pty_instructions: Sender<PtyInstruction>, send_pty_instructions: SenderWithContext<PtyInstruction>,
send_app_instructions: SyncSender<AppInstruction>, send_app_instructions: SenderWithContext<AppInstruction>,
} }
impl InputHandler { impl InputHandler {
fn new( fn new(
os_input: Box<dyn OsApi>, os_input: Box<dyn OsApi>,
command_is_executing: CommandIsExecuting, command_is_executing: CommandIsExecuting,
send_screen_instructions: Sender<ScreenInstruction>, send_screen_instructions: SenderWithContext<ScreenInstruction>,
send_pty_instructions: Sender<PtyInstruction>, send_pty_instructions: SenderWithContext<PtyInstruction>,
send_app_instructions: SyncSender<AppInstruction>, send_app_instructions: SenderWithContext<AppInstruction>,
) -> Self { ) -> Self {
InputHandler { InputHandler {
mode: InputMode::Normal, mode: InputMode::Normal,
@ -36,6 +35,11 @@ impl InputHandler {
/// Main event loop /// Main event loop
fn get_input(&mut self) { fn get_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);
loop { loop {
match self.mode { match self.mode {
InputMode::Normal => self.read_normal_mode(), InputMode::Normal => self.read_normal_mode(),
@ -263,9 +267,9 @@ pub enum InputMode {
pub fn input_loop( pub fn input_loop(
os_input: Box<dyn OsApi>, os_input: Box<dyn OsApi>,
command_is_executing: CommandIsExecuting, command_is_executing: CommandIsExecuting,
send_screen_instructions: Sender<ScreenInstruction>, send_screen_instructions: SenderWithContext<ScreenInstruction>,
send_pty_instructions: Sender<PtyInstruction>, send_pty_instructions: SenderWithContext<PtyInstruction>,
send_app_instructions: SyncSender<AppInstruction>, send_app_instructions: SenderWithContext<AppInstruction>,
) { ) {
let _handler = InputHandler::new( let _handler = InputHandler::new(
os_input, os_input,

View file

@ -15,13 +15,14 @@ mod utils;
use std::io::Write; use std::io::Write;
use std::os::unix::net::UnixStream; use std::os::unix::net::UnixStream;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::mpsc::{channel, sync_channel, Receiver, Sender, SyncSender}; use std::sync::mpsc::{channel, sync_channel, Receiver, SendError, Sender, SyncSender};
use std::thread; use std::thread;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use structopt::StructOpt; use structopt::StructOpt;
use crate::command_is_executing::CommandIsExecuting; use crate::command_is_executing::CommandIsExecuting;
use crate::errors::{AppContext, ContextType, ErrorContext, PtyContext, ScreenContext};
use crate::input::input_loop; use crate::input::input_loop;
use crate::layout::Layout; use crate::layout::Layout;
use crate::os_input_output::{get_os_input, OsApi}; use crate::os_input_output::{get_os_input, OsApi};
@ -31,6 +32,9 @@ use crate::utils::{
consts::{MOSAIC_IPC_PIPE, MOSAIC_TMP_DIR, MOSAIC_TMP_LOG_DIR}, consts::{MOSAIC_IPC_PIPE, MOSAIC_TMP_DIR, MOSAIC_TMP_LOG_DIR},
logging::*, logging::*,
}; };
use std::cell::RefCell;
thread_local!(static OPENCALLS: RefCell<ErrorContext> = RefCell::new(ErrorContext::new()));
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
enum ApiCommand { enum ApiCommand {
@ -40,6 +44,38 @@ enum ApiCommand {
MoveFocus, MoveFocus,
} }
#[derive(Clone)]
enum SenderType<T: Clone> {
Sender(Sender<(T, ErrorContext)>),
SyncSender(SyncSender<(T, ErrorContext)>),
}
#[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 }
}
pub fn send(&self, event: T) -> Result<(), SendError<(T, ErrorContext)>> {
match self.sender {
SenderType::Sender(ref s) => s.send((event, self.err_ctx)),
SenderType::SyncSender(ref s) => s.send((event, self.err_ctx)),
}
}
pub fn update(&mut self, new_ctx: ErrorContext) {
self.err_ctx = new_ctx;
}
}
unsafe impl<T: Clone> Send for SenderWithContext<T> {}
unsafe impl<T: Clone> Sync for SenderWithContext<T> {}
#[derive(StructOpt, Debug, Default)] #[derive(StructOpt, Debug, Default)]
#[structopt(name = "mosaic")] #[structopt(name = "mosaic")]
pub struct Opt { pub struct Opt {
@ -94,6 +130,7 @@ pub fn main() {
} }
} }
#[derive(Clone)]
pub enum AppInstruction { pub enum AppInstruction {
Exit, Exit,
Error(String), Error(String),
@ -107,17 +144,24 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
let full_screen_ws = os_input.get_terminal_size_using_fd(0); let full_screen_ws = os_input.get_terminal_size_using_fd(0);
os_input.into_raw_mode(0); os_input.into_raw_mode(0);
let (send_screen_instructions, receive_screen_instructions): ( let (send_screen_instructions, receive_screen_instructions): (
Sender<ScreenInstruction>, Sender<(ScreenInstruction, ErrorContext)>,
Receiver<ScreenInstruction>, Receiver<(ScreenInstruction, ErrorContext)>,
) = channel(); ) = 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_pty_instructions, receive_pty_instructions): ( let (send_pty_instructions, receive_pty_instructions): (
Sender<PtyInstruction>, Sender<(PtyInstruction, ErrorContext)>,
Receiver<PtyInstruction>, Receiver<(PtyInstruction, ErrorContext)>,
) = channel(); ) = channel();
let mut send_pty_instructions =
SenderWithContext::new(err_ctx, SenderType::Sender(send_pty_instructions));
let (send_app_instructions, receive_app_instructions): ( let (send_app_instructions, receive_app_instructions): (
SyncSender<AppInstruction>, SyncSender<(AppInstruction, ErrorContext)>,
Receiver<AppInstruction>, Receiver<(AppInstruction, ErrorContext)>,
) = sync_channel(0); ) = sync_channel(0);
let send_app_instructions =
SenderWithContext::new(err_ctx, SenderType::SyncSender(send_app_instructions));
let mut screen = Screen::new( let mut screen = Screen::new(
receive_screen_instructions, receive_screen_instructions,
send_pty_instructions.clone(), send_pty_instructions.clone(),
@ -156,10 +200,12 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
} }
loop { loop {
let event = pty_bus let (event, mut err_ctx) = pty_bus
.receive_pty_instructions .receive_pty_instructions
.recv() .recv()
.expect("failed to receive event on channel"); .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 { match event {
PtyInstruction::SpawnTerminal(file_to_open) => { PtyInstruction::SpawnTerminal(file_to_open) => {
pty_bus.spawn_terminal(file_to_open); pty_bus.spawn_terminal(file_to_open);
@ -190,10 +236,13 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
.spawn({ .spawn({
let mut command_is_executing = command_is_executing.clone(); let mut command_is_executing = command_is_executing.clone();
move || loop { move || loop {
let event = screen let (event, mut err_ctx) = screen
.receiver .receiver
.recv() .recv()
.expect("failed to receive event on channel"); .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 { match event {
ScreenInstruction::Pty(pid, vte_event) => { ScreenInstruction::Pty(pid, vte_event) => {
screen.handle_pty_event(pid, vte_event); screen.handle_pty_event(pid, vte_event);
@ -388,12 +437,16 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
.name("ipc_server".to_string()) .name("ipc_server".to_string())
.spawn({ .spawn({
use std::io::Read; use std::io::Read;
let send_pty_instructions = send_pty_instructions.clone(); let mut send_pty_instructions = send_pty_instructions.clone();
let send_screen_instructions = send_screen_instructions.clone(); let mut send_screen_instructions = send_screen_instructions.clone();
move || { move || {
std::fs::remove_file(MOSAIC_IPC_PIPE).ok(); std::fs::remove_file(MOSAIC_IPC_PIPE).ok();
let listener = std::os::unix::net::UnixListener::bind(MOSAIC_IPC_PIPE) let listener = std::os::unix::net::UnixListener::bind(MOSAIC_IPC_PIPE)
.expect("could not listen on ipc socket"); .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() { for stream in listener.incoming() {
match stream { match stream {
@ -456,9 +509,13 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
#[warn(clippy::never_loop)] #[warn(clippy::never_loop)]
loop { loop {
let app_instruction = receive_app_instructions let (app_instruction, mut err_ctx) = receive_app_instructions
.recv() .recv()
.expect("failed to receive app instruction on channel"); .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 { match app_instruction {
AppInstruction::Exit => { AppInstruction::Exit => {
let _ = send_screen_instructions.send(ScreenInstruction::Quit); let _ = send_screen_instructions.send(ScreenInstruction::Quit);
@ -466,10 +523,15 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: Opt) {
break; break;
} }
AppInstruction::Error(backtrace) => { AppInstruction::Error(backtrace) => {
os_input.unset_raw_mode(0);
println!("{}", backtrace);
let _ = send_screen_instructions.send(ScreenInstruction::Quit); let _ = send_screen_instructions.send(ScreenInstruction::Quit);
let _ = send_pty_instructions.send(PtyInstruction::Quit); let _ = send_pty_instructions.send(PtyInstruction::Quit);
os_input.unset_raw_mode(0);
let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
let error = format!("{}\n{}", goto_start_of_last_line, backtrace);
let _ = os_input
.get_stdout_writer()
.write(error.as_bytes())
.unwrap();
for thread_handler in active_threads { for thread_handler in active_threads {
let _ = thread_handler.join(); let _ = thread_handler.join();
} }

View file

@ -4,15 +4,16 @@ use ::async_std::task::*;
use ::std::collections::HashMap; use ::std::collections::HashMap;
use ::std::os::unix::io::RawFd; use ::std::os::unix::io::RawFd;
use ::std::pin::*; use ::std::pin::*;
use ::std::sync::mpsc::{Receiver, Sender}; use ::std::sync::mpsc::Receiver;
use ::std::time::{Duration, Instant}; use ::std::time::{Duration, Instant};
use ::vte; use ::vte;
use std::path::PathBuf; use std::path::PathBuf;
use crate::errors::{ContextType, ErrorContext};
use crate::layout::Layout; use crate::layout::Layout;
use crate::os_input_output::OsApi; use crate::os_input_output::OsApi;
use crate::utils::logging::debug_to_file; use crate::utils::logging::debug_to_file;
use crate::ScreenInstruction; use crate::{ScreenInstruction, SenderWithContext, OPENCALLS};
pub struct ReadFromPid { pub struct ReadFromPid {
pid: RawFd, pid: RawFd,
@ -75,11 +76,11 @@ pub enum VteEvent {
struct VteEventSender { struct VteEventSender {
id: RawFd, id: RawFd,
sender: Sender<ScreenInstruction>, sender: SenderWithContext<ScreenInstruction>,
} }
impl VteEventSender { impl VteEventSender {
pub fn new(id: RawFd, sender: Sender<ScreenInstruction>) -> Self { pub fn new(id: RawFd, sender: SenderWithContext<ScreenInstruction>) -> Self {
VteEventSender { id, sender } VteEventSender { id, sender }
} }
} }
@ -151,8 +152,8 @@ pub enum PtyInstruction {
} }
pub struct PtyBus { pub struct PtyBus {
pub send_screen_instructions: Sender<ScreenInstruction>, pub send_screen_instructions: SenderWithContext<ScreenInstruction>,
pub receive_pty_instructions: Receiver<PtyInstruction>, pub receive_pty_instructions: Receiver<(PtyInstruction, ErrorContext)>,
pub id_to_child_pid: HashMap<RawFd, RawFd>, pub id_to_child_pid: HashMap<RawFd, RawFd>,
os_input: Box<dyn OsApi>, os_input: Box<dyn OsApi>,
debug_to_file: bool, debug_to_file: bool,
@ -160,12 +161,15 @@ pub struct PtyBus {
fn stream_terminal_bytes( fn stream_terminal_bytes(
pid: RawFd, pid: RawFd,
send_screen_instructions: Sender<ScreenInstruction>, mut send_screen_instructions: SenderWithContext<ScreenInstruction>,
os_input: Box<dyn OsApi>, os_input: Box<dyn OsApi>,
debug: bool, debug: bool,
) { ) {
let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
task::spawn({ task::spawn({
async move { async move {
err_ctx.add_call(ContextType::AsyncTask);
send_screen_instructions.update(err_ctx);
let mut vte_parser = vte::Parser::new(); let mut vte_parser = vte::Parser::new();
let mut vte_event_sender = VteEventSender::new(pid, send_screen_instructions.clone()); let mut vte_event_sender = VteEventSender::new(pid, send_screen_instructions.clone());
let mut terminal_bytes = ReadFromPid::new(&pid, os_input); let mut terminal_bytes = ReadFromPid::new(&pid, os_input);
@ -233,8 +237,8 @@ fn stream_terminal_bytes(
impl PtyBus { impl PtyBus {
pub fn new( pub fn new(
receive_pty_instructions: Receiver<PtyInstruction>, receive_pty_instructions: Receiver<(PtyInstruction, ErrorContext)>,
send_screen_instructions: Sender<ScreenInstruction>, send_screen_instructions: SenderWithContext<ScreenInstruction>,
os_input: Box<dyn OsApi>, os_input: Box<dyn OsApi>,
debug_to_file: bool, debug_to_file: bool,
) -> Self { ) -> Self {

View file

@ -1,16 +1,16 @@
use std::collections::{BTreeMap, HashSet}; use std::collections::{BTreeMap, HashSet};
use std::io::Write; use std::io::Write;
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use std::sync::mpsc::{Receiver, Sender, SyncSender}; use std::sync::mpsc::Receiver;
use crate::boundaries::Boundaries; use crate::boundaries::Boundaries;
use crate::boundaries::Rect; use crate::boundaries::Rect;
use crate::errors::ErrorContext;
use crate::layout::Layout; use crate::layout::Layout;
use crate::os_input_output::OsApi; use crate::os_input_output::OsApi;
use crate::pty_bus::{PtyInstruction, VteEvent}; use crate::pty_bus::{PtyInstruction, VteEvent};
use crate::terminal_pane::{PositionAndSize, TerminalPane}; use crate::terminal_pane::{PositionAndSize, TerminalPane};
use crate::utils::logging::debug_log_to_file; use crate::{AppInstruction, SenderWithContext};
use crate::AppInstruction;
/* /*
* Screen * Screen
@ -78,10 +78,10 @@ pub enum ScreenInstruction {
} }
pub struct Screen { pub struct Screen {
pub receiver: Receiver<ScreenInstruction>, pub receiver: Receiver<(ScreenInstruction, ErrorContext)>,
max_panes: Option<usize>, max_panes: Option<usize>,
send_pty_instructions: Sender<PtyInstruction>, pub send_pty_instructions: SenderWithContext<PtyInstruction>,
send_app_instructions: SyncSender<AppInstruction>, pub send_app_instructions: SenderWithContext<AppInstruction>,
full_screen_ws: PositionAndSize, full_screen_ws: PositionAndSize,
terminals: BTreeMap<RawFd, TerminalPane>, // BTreeMap because we need a predictable order when changing focus terminals: BTreeMap<RawFd, TerminalPane>, // BTreeMap because we need a predictable order when changing focus
panes_to_hide: HashSet<RawFd>, panes_to_hide: HashSet<RawFd>,
@ -92,9 +92,9 @@ pub struct Screen {
impl Screen { impl Screen {
pub fn new( pub fn new(
receive_screen_instructions: Receiver<ScreenInstruction>, receive_screen_instructions: Receiver<(ScreenInstruction, ErrorContext)>,
send_pty_instructions: Sender<PtyInstruction>, send_pty_instructions: SenderWithContext<PtyInstruction>,
send_app_instructions: SyncSender<AppInstruction>, send_app_instructions: SenderWithContext<AppInstruction>,
full_screen_ws: &PositionAndSize, full_screen_ws: &PositionAndSize,
os_api: Box<dyn OsApi>, os_api: Box<dyn OsApi>,
max_panes: Option<usize>, max_panes: Option<usize>,

View file

@ -37,7 +37,7 @@ pub fn debug_log_to_file_without_newline(message: String) -> io::Result<()> {
file.write_all(message.as_bytes()) file.write_all(message.as_bytes())
} }
pub fn debug_log_to_file_pid_3(message: String, pid: RawFd) -> io::Result<()> { pub fn _debug_log_to_file_pid_3(message: String, pid: RawFd) -> io::Result<()> {
if pid == 3 { if pid == 3 {
debug_log_to_file(message) debug_log_to_file(message)
} else { } else {