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:
parent
0a05e4e34a
commit
92d1bcff4c
6 changed files with 299 additions and 53 deletions
192
src/errors.rs
192
src/errors.rs
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
28
src/input.rs
28
src/input.rs
|
|
@ -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,
|
||||||
|
|
|
||||||
90
src/main.rs
90
src/main.rs
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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>,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue