wip: here goes the os_thread and OsContext

This commit is contained in:
denis 2021-03-18 12:20:23 +02:00 committed by Kunal Mohan
parent d8986351ed
commit daddac65aa
2 changed files with 270 additions and 178 deletions

View file

@ -1,7 +1,7 @@
//! Error context system based on a thread-local representation of the call stack, itself based on //! Error context system based on a thread-local representation of the call stack, itself based on
//! the instructions that are sent between threads. //! the instructions that are sent between threads.
use super::{AppInstruction, ASYNCOPENCALLS, OPENCALLS}; use super::{os_input_output::OsApiInstruction, AppInstruction, OPENCALLS};
use crate::pty_bus::PtyInstruction; use crate::pty_bus::PtyInstruction;
use crate::screen::ScreenInstruction; use crate::screen::ScreenInstruction;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -138,6 +138,8 @@ pub enum ContextType {
Screen(ScreenContext), Screen(ScreenContext),
/// A PTY-related call. /// A PTY-related call.
Pty(PtyContext), Pty(PtyContext),
/// An OS-related call.
Os(OsContext),
/// A plugin-related call. /// A plugin-related call.
Plugin(PluginContext), Plugin(PluginContext),
/// An app-related call. /// An app-related call.
@ -158,7 +160,7 @@ impl Display for ContextType {
match *self { match *self {
ContextType::Screen(c) => write!(f, "{}screen_thread: {}{:?}", purple, green, c), ContextType::Screen(c) => write!(f, "{}screen_thread: {}{:?}", purple, green, c),
ContextType::Pty(c) => write!(f, "{}pty_thread: {}{:?}", purple, green, c), ContextType::Pty(c) => write!(f, "{}pty_thread: {}{:?}", purple, green, c),
ContextType::Os(c) => write!(f, "{}os_thread: {}{:?}", purple, green, c),
ContextType::Plugin(c) => write!(f, "{}plugin_thread: {}{:?}", purple, green, c), ContextType::Plugin(c) => write!(f, "{}plugin_thread: {}{:?}", purple, green, c),
ContextType::App(c) => write!(f, "{}main_thread: {}{:?}", purple, green, c), ContextType::App(c) => write!(f, "{}main_thread: {}{:?}", purple, green, c),
ContextType::IpcServer => write!(f, "{}ipc_server: {}AcceptInput", purple, green), ContextType::IpcServer => write!(f, "{}ipc_server: {}AcceptInput", purple, green),
@ -293,6 +295,41 @@ impl From<&PtyInstruction> for PtyContext {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum OsContext {
SpawnTerminal,
GetTerminalSizeUsingFd,
SetTerminalSizeUsingFd,
SetRawMode,
UnsetRawMode,
ReadFromTtyStdout,
WriteToTtyStdin,
TcDrain,
Kill,
ReadFromStdin,
GetStdoutWriter,
BoxClone,
}
impl From<&OsApiInstruction> for OsContext {
fn from(os_instruction: &OsApiInstruction) -> Self {
match *os_instruction {
OsApiInstruction::SpawnTerminal(_) => OsContext::SpawnTerminal,
OsApiInstruction::GetTerminalSizeUsingFd(_) => OsContext::GetTerminalSizeUsingFd,
OsApiInstruction::SetTerminalSizeUsingFd(_, _, _) => OsContext::SetTerminalSizeUsingFd,
OsApiInstruction::SetRawMode(_) => OsContext::SetRawMode,
OsApiInstruction::UnsetRawMode(_) => OsContext::UnsetRawMode,
OsApiInstruction::ReadFromTtyStdout(_, _) => OsContext::ReadFromTtyStdout,
OsApiInstruction::WriteToTtyStdin(_, _) => OsContext::WriteToTtyStdin,
OsApiInstruction::TcDrain(_) => OsContext::TcDrain,
OsApiInstruction::Kill(_) => OsContext::Kill,
OsApiInstruction::ReadFromStdin => OsContext::ReadFromStdin,
OsApiInstruction::GetStdoutWriter => OsContext::GetStdoutWriter,
OsApiInstruction::BoxClone => OsContext::BoxClone
}
}
}
// FIXME: This whole pattern *needs* a macro eventually, it's soul-crushing to write // FIXME: This whole pattern *needs* a macro eventually, it's soul-crushing to write
use crate::wasm_vm::PluginInstruction; use crate::wasm_vm::PluginInstruction;

View file

@ -1,9 +1,9 @@
use crate::cli::CliArgs; use crate::cli::CliArgs;
use crate::common::{ use crate::common::{
ChannelWithContext, ClientInstruction, IpcSenderWithContext, SenderType, SenderWithContext, ChannelWithContext, ClientInstruction, IpcSenderWithContext, SenderType, SenderWithContext,
ServerInstruction, ServerInstruction,
}; };
use crate::errors::{ContextType, ErrorContext, PtyContext}; use crate::errors::{ContextType, ErrorContext, OsContext, PtyContext};
use crate::os_input_output::{OsApi, OsApiInstruction}; use crate::os_input_output::{OsApi, OsApiInstruction};
use crate::panes::PaneId; use crate::panes::PaneId;
use crate::pty_bus::{PtyBus, PtyInstruction}; use crate::pty_bus::{PtyBus, PtyInstruction};
@ -15,185 +15,240 @@ use std::sync::mpsc::channel;
use std::thread; use std::thread;
pub fn start_server(os_input: Box<dyn OsApi>, opts: CliArgs) -> (thread::JoinHandle<()>, String) { pub fn start_server(os_input: Box<dyn OsApi>, opts: CliArgs) -> (thread::JoinHandle<()>, String) {
let (send_pty_instructions, receive_pty_instructions): ChannelWithContext<PtyInstruction> = let (send_pty_instructions, receive_pty_instructions): ChannelWithContext<PtyInstruction> =
channel(); channel();
let mut send_pty_instructions = SenderWithContext::new( let mut send_pty_instructions = SenderWithContext::new(
ErrorContext::new(), ErrorContext::new(),
SenderType::Sender(send_pty_instructions), SenderType::Sender(send_pty_instructions),
); );
#[cfg(not(test))] #[cfg(not(test))]
let (server_name, server_buffer) = ( let (server_name, server_buffer) = (
String::from(ZELLIJ_IPC_PIPE), String::from(ZELLIJ_IPC_PIPE),
SharedRingBuffer::create(ZELLIJ_IPC_PIPE, 8192).unwrap(), SharedRingBuffer::create(ZELLIJ_IPC_PIPE, 8192).unwrap(),
); );
#[cfg(test)] #[cfg(test)]
let (server_name, server_buffer) = SharedRingBuffer::create_temp(8192).unwrap(); let (server_name, server_buffer) = SharedRingBuffer::create_temp(8192).unwrap();
let (send_os_instructions, receive_os_instructions): ChannelWithContext<OsApiInstruction> = channel(); let (send_os_instructions, receive_os_instructions): ChannelWithContext<OsApiInstruction> =
let mut send_os_instructions = SenderWithContext::new( channel();
ErrorContext::new(), let mut send_os_instructions = SenderWithContext::new(
SenderType::Sender(send_os_instructions), ErrorContext::new(),
); SenderType::Sender(send_os_instructions),
);
// Don't use default layouts in tests, but do everywhere else // Don't use default layouts in tests, but do everywhere else
#[cfg(not(test))] #[cfg(not(test))]
let default_layout = Some(PathBuf::from("default")); let default_layout = Some(PathBuf::from("default"));
#[cfg(test)] #[cfg(test)]
let default_layout = None; let default_layout = None;
let maybe_layout = opts.layout.or(default_layout); let maybe_layout = opts.layout.or(default_layout);
let send_server_instructions = IpcSenderWithContext::new(server_buffer.clone()); let send_server_instructions = IpcSenderWithContext::new(server_buffer.clone());
let mut pty_bus = PtyBus::new( let mut pty_bus = PtyBus::new(
receive_pty_instructions, receive_pty_instructions,
os_input.clone(), os_input.clone(),
send_server_instructions, send_server_instructions,
opts.debug, opts.debug,
); );
let pty_thread = thread::Builder::new() let pty_thread = thread::Builder::new()
.name("pty".to_string()) .name("pty".to_string())
.spawn(move || loop { .spawn(move || loop {
let (event, mut err_ctx) = 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))); err_ctx.add_call(ContextType::Pty(PtyContext::from(&event)));
match event { match event {
PtyInstruction::SpawnTerminal(file_to_open) => { PtyInstruction::SpawnTerminal(file_to_open) => {
let pid = pty_bus.spawn_terminal(file_to_open); let pid = pty_bus.spawn_terminal(file_to_open);
pty_bus pty_bus
.send_server_instructions .send_server_instructions
.send(ServerInstruction::ToScreen(ScreenInstruction::NewPane( .send(ServerInstruction::ToScreen(ScreenInstruction::NewPane(
PaneId::Terminal(pid), PaneId::Terminal(pid),
))) )))
.unwrap(); .unwrap();
} }
PtyInstruction::SpawnTerminalVertically(file_to_open) => { PtyInstruction::SpawnTerminalVertically(file_to_open) => {
let pid = pty_bus.spawn_terminal(file_to_open); let pid = pty_bus.spawn_terminal(file_to_open);
pty_bus pty_bus
.send_server_instructions .send_server_instructions
.send(ServerInstruction::ToScreen( .send(ServerInstruction::ToScreen(
ScreenInstruction::VerticalSplit(PaneId::Terminal(pid)), ScreenInstruction::VerticalSplit(PaneId::Terminal(pid)),
)) ))
.unwrap(); .unwrap();
} }
PtyInstruction::SpawnTerminalHorizontally(file_to_open) => { PtyInstruction::SpawnTerminalHorizontally(file_to_open) => {
let pid = pty_bus.spawn_terminal(file_to_open); let pid = pty_bus.spawn_terminal(file_to_open);
pty_bus pty_bus
.send_server_instructions .send_server_instructions
.send(ServerInstruction::ToScreen( .send(ServerInstruction::ToScreen(
ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)), ScreenInstruction::HorizontalSplit(PaneId::Terminal(pid)),
)) ))
.unwrap(); .unwrap();
} }
PtyInstruction::NewTab => { PtyInstruction::NewTab => {
if let Some(layout) = maybe_layout.clone() { if let Some(layout) = maybe_layout.clone() {
pty_bus.spawn_terminals_for_layout(layout); pty_bus.spawn_terminals_for_layout(layout);
} else { } else {
let pid = pty_bus.spawn_terminal(None); let pid = pty_bus.spawn_terminal(None);
pty_bus pty_bus
.send_server_instructions .send_server_instructions
.send(ServerInstruction::ToScreen(ScreenInstruction::NewTab(pid))) .send(ServerInstruction::ToScreen(ScreenInstruction::NewTab(pid)))
.unwrap(); .unwrap();
} }
} }
PtyInstruction::ClosePane(id) => { PtyInstruction::ClosePane(id) => {
pty_bus.close_pane(id); pty_bus.close_pane(id);
pty_bus pty_bus
.send_server_instructions .send_server_instructions
.send(ServerInstruction::DoneClosingPane) .send(ServerInstruction::DoneClosingPane)
.unwrap(); .unwrap();
} }
PtyInstruction::CloseTab(ids) => { PtyInstruction::CloseTab(ids) => {
pty_bus.close_tab(ids); pty_bus.close_tab(ids);
pty_bus pty_bus
.send_server_instructions .send_server_instructions
.send(ServerInstruction::DoneClosingPane) .send(ServerInstruction::DoneClosingPane)
.unwrap(); .unwrap();
} }
PtyInstruction::Exit => { PtyInstruction::Exit => {
break; break;
} }
} }
}) })
.unwrap(); .unwrap();
let join_handle = thread::Builder::new() let os_thread = thread::Builder::new()
.name("ipc_server".to_string()) .name("os".to_string())
.spawn({ .spawn({
let recv_server_instructions = IpcReceiver::new(server_buffer); let mut os_input = os_input.clone();
// Fixme: We cannot use uninitialised sender, therefore this Vec. move || loop {
// For now, We make sure that the first message is `NewClient` so there are no out of bound panics. let (event, mut err_ctx) = receive_os_instructions
let mut send_client_instructions: Vec<IpcSenderWithContext> = Vec::with_capacity(1); .recv()
move || loop { .expect("failed to receive an event on the channel");
let (mut err_ctx, instruction): (ErrorContext, ServerInstruction) = err_ctx.add_call(ContextType::Os(OsContext::from(&event)));
recv_server_instructions.recv().unwrap(); match event {
err_ctx.add_call(ContextType::IPCServer); OsApiInstruction::SpawnTerminal(file_to_open) => {
send_pty_instructions.update(err_ctx); os_input.spawn_terminal(file_to_open);
if send_client_instructions.len() == 1 { }
send_client_instructions[0].update(err_ctx); OsApiInstruction::GetTerminalSizeUsingFd(fd) => {
} os_input.get_terminal_size_using_fd(fd);
}
OsApiInstruction::SetTerminalSizeUsingFd(fd, cols, rows) => {
os_input.set_terminal_size_using_fd(fd, cols, rows);
}
OsApiInstruction::SetRawMode(fd) => {
os_input.set_raw_mode(fd);
}
OsApiInstruction::UnsetRawMode(fd) => {
os_input.unset_raw_mode(fd);
}
OsApiInstruction::ReadFromTtyStdout(fd, mut buf) => {
let slice = buf.as_mut_slice();
os_input.read_from_tty_stdout(fd, slice).unwrap();
}
OsApiInstruction::WriteToTtyStdin(fd, mut buf) => {
let slice = buf.as_mut_slice();
os_input.write_to_tty_stdin(fd, slice).unwrap();
}
OsApiInstruction::TcDrain(fd) => {
os_input.tcdrain(fd).unwrap();
}
OsApiInstruction::Kill(pid) => {
os_input.kill(pid).unwrap();
}
OsApiInstruction::ReadFromStdin => {
os_input.read_from_stdin();
}
OsApiInstruction::GetStdoutWriter => {
os_input.get_stdout_writer();
}
OsApiInstruction::BoxClone => {
os_input.box_clone();
}
}
}
})
.unwrap();
match instruction { let join_handle = thread::Builder::new()
ServerInstruction::OpenFile(file_name) => { .name("ipc_server".to_string())
let path = PathBuf::from(file_name); .spawn({
send_pty_instructions let recv_server_instructions = IpcReceiver::new(server_buffer);
.send(PtyInstruction::SpawnTerminal(Some(path))) // Fixme: We cannot use uninitialised sender, therefore this Vec.
.unwrap(); // For now, We make sure that the first message is `NewClient` so there are no out of bound panics.
} let mut send_client_instructions: Vec<IpcSenderWithContext> = Vec::with_capacity(1);
ServerInstruction::SplitHorizontally => { move || loop {
send_pty_instructions let (mut err_ctx, instruction): (ErrorContext, ServerInstruction) =
.send(PtyInstruction::SpawnTerminalHorizontally(None)) recv_server_instructions.recv().unwrap();
.unwrap(); err_ctx.add_call(ContextType::IPCServer);
} send_pty_instructions.update(err_ctx);
ServerInstruction::SplitVertically => { if send_client_instructions.len() == 1 {
send_pty_instructions send_client_instructions[0].update(err_ctx);
.send(PtyInstruction::SpawnTerminalVertically(None)) }
.unwrap();
} match instruction {
ServerInstruction::MoveFocus => { ServerInstruction::OpenFile(file_name) => {
send_client_instructions[0] let path = PathBuf::from(file_name);
.send(ClientInstruction::ToScreen(ScreenInstruction::MoveFocus)) send_pty_instructions
.unwrap(); .send(PtyInstruction::SpawnTerminal(Some(path)))
} .unwrap();
ServerInstruction::NewClient(buffer_path) => { }
send_pty_instructions.send(PtyInstruction::NewTab).unwrap(); ServerInstruction::SplitHorizontally => {
send_client_instructions.push(IpcSenderWithContext::new( send_pty_instructions
SharedRingBuffer::open(&buffer_path).unwrap(), .send(PtyInstruction::SpawnTerminalHorizontally(None))
)); .unwrap();
} }
ServerInstruction::ToPty(instr) => { ServerInstruction::SplitVertically => {
send_pty_instructions.send(instr).unwrap(); send_pty_instructions
} .send(PtyInstruction::SpawnTerminalVertically(None))
ServerInstruction::ToScreen(instr) => { .unwrap();
send_client_instructions[0] }
.send(ClientInstruction::ToScreen(instr)) ServerInstruction::MoveFocus => {
.unwrap(); send_client_instructions[0]
} .send(ClientInstruction::ToScreen(ScreenInstruction::MoveFocus))
ServerInstruction::OsApi(instr) => { .unwrap();
send_os_instructions.send(instr).unwrap(); }
} ServerInstruction::NewClient(buffer_path) => {
ServerInstruction::DoneClosingPane => { send_pty_instructions.send(PtyInstruction::NewTab).unwrap();
send_client_instructions[0] send_client_instructions.push(IpcSenderWithContext::new(
.send(ClientInstruction::DoneClosingPane) SharedRingBuffer::open(&buffer_path).unwrap(),
.unwrap(); ));
} }
ServerInstruction::ClosePluginPane(pid) => { ServerInstruction::ToPty(instr) => {
send_client_instructions[0] send_pty_instructions.send(instr).unwrap();
.send(ClientInstruction::ClosePluginPane(pid)) }
.unwrap(); ServerInstruction::ToScreen(instr) => {
} send_client_instructions[0]
ServerInstruction::Exit => { .send(ClientInstruction::ToScreen(instr))
let _ = send_pty_instructions.send(PtyInstruction::Exit); .unwrap();
let _ = pty_thread.join(); }
let _ = send_client_instructions[0].send(ClientInstruction::Exit); ServerInstruction::OsApi(instr) => {
break; send_os_instructions.send(instr).unwrap();
} }
} ServerInstruction::DoneClosingPane => {
} send_client_instructions[0]
}) .send(ClientInstruction::DoneClosingPane)
.unwrap(); .unwrap();
(join_handle, server_name) }
ServerInstruction::ClosePluginPane(pid) => {
send_client_instructions[0]
.send(ClientInstruction::ClosePluginPane(pid))
.unwrap();
}
ServerInstruction::Exit => {
let _ = send_pty_instructions.send(PtyInstruction::Exit);
let _ = pty_thread.join();
let _ = os_thread.join();
let _ = send_client_instructions[0].send(ClientInstruction::Exit);
break;
}
}
}
})
.unwrap();
(join_handle, server_name)
} }