mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-15 16:26:48 +00:00
Unpack LineDiscipline
from Arc
This commit is contained in:
parent
9e2dde5ebb
commit
45258d0475
@ -6,7 +6,7 @@ use ostd::task::Task;
|
||||
|
||||
use crate::{
|
||||
current_userspace,
|
||||
device::tty::{line_discipline::LineDiscipline, new_job_control_and_ldisc},
|
||||
device::tty::line_discipline::LineDiscipline,
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
device::{Device, DeviceId, DeviceType},
|
||||
@ -18,6 +18,7 @@ use crate::{
|
||||
},
|
||||
prelude::*,
|
||||
process::{
|
||||
broadcast_signal_async,
|
||||
posix_thread::{AsPosixThread, AsThreadLocal},
|
||||
signal::{PollHandle, Pollable, Pollee},
|
||||
JobControl, Terminal,
|
||||
@ -43,10 +44,9 @@ pub struct PtyMaster {
|
||||
impl PtyMaster {
|
||||
pub fn new(ptmx: Arc<dyn Inode>, index: u32) -> Arc<Self> {
|
||||
Arc::new_cyclic(move |master| {
|
||||
let (job_control, ldisc) = new_job_control_and_ldisc();
|
||||
let slave = Arc::new_cyclic(move |weak_self| PtySlave {
|
||||
ldisc,
|
||||
job_control,
|
||||
ldisc: LineDiscipline::new(),
|
||||
job_control: JobControl::new(),
|
||||
master: master.clone(),
|
||||
weak_self: weak_self.clone(),
|
||||
});
|
||||
@ -166,11 +166,19 @@ impl FileIo for PtyMaster {
|
||||
let write_len = buf.len();
|
||||
let mut input = self.input.lock();
|
||||
for character in buf {
|
||||
self.slave.ldisc.push_char(character, |content| {
|
||||
for byte in content.as_bytes() {
|
||||
input.push_overwrite(*byte);
|
||||
}
|
||||
});
|
||||
self.slave.ldisc.push_char(
|
||||
character,
|
||||
|signum| {
|
||||
if let Some(foreground) = self.slave.job_control.foreground() {
|
||||
broadcast_signal_async(Arc::downgrade(&foreground), signum);
|
||||
}
|
||||
},
|
||||
|content| {
|
||||
for byte in content.as_bytes() {
|
||||
input.push_overwrite(*byte);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
self.pollee.notify(IoEvents::IN);
|
||||
@ -240,8 +248,8 @@ impl Drop for PtyMaster {
|
||||
}
|
||||
|
||||
pub struct PtySlave {
|
||||
ldisc: Arc<LineDiscipline>,
|
||||
job_control: Arc<JobControl>,
|
||||
ldisc: LineDiscipline,
|
||||
job_control: JobControl,
|
||||
master: Weak<PtyMaster>,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
@ -2,10 +2,7 @@
|
||||
|
||||
use alloc::format;
|
||||
|
||||
use ostd::{
|
||||
sync::LocalIrqDisabled,
|
||||
trap::{disable_local, in_interrupt_context},
|
||||
};
|
||||
use ostd::{sync::LocalIrqDisabled, trap::disable_local};
|
||||
|
||||
use super::termio::{KernelTermios, WinSize, CC_C_CHAR};
|
||||
use crate::{
|
||||
@ -13,10 +10,9 @@ use crate::{
|
||||
prelude::*,
|
||||
process::signal::{
|
||||
constants::{SIGINT, SIGQUIT},
|
||||
signals::kernel::KernelSignal,
|
||||
sig_num::SigNum,
|
||||
PollHandle, Pollable, Pollee,
|
||||
},
|
||||
thread::work_queue::{submit_work_item, work_item::WorkItem, WorkPriority},
|
||||
util::ring_buffer::RingBuffer,
|
||||
};
|
||||
|
||||
@ -25,8 +21,6 @@ use crate::{
|
||||
|
||||
const BUFFER_CAPACITY: usize = 4096;
|
||||
|
||||
pub type LdiscSignalSender = Arc<dyn Fn(KernelSignal) + Send + Sync + 'static>;
|
||||
|
||||
// Lock ordering to prevent deadlock (circular dependencies):
|
||||
// 1. `termios`
|
||||
// 2. `current_line`
|
||||
@ -43,12 +37,6 @@ pub struct LineDiscipline {
|
||||
winsize: SpinLock<WinSize, LocalIrqDisabled>,
|
||||
/// Pollee
|
||||
pollee: Pollee,
|
||||
/// Used to send signal for foreground processes, when some char comes.
|
||||
send_signal: LdiscSignalSender,
|
||||
/// Work item
|
||||
work_item: Arc<WorkItem>,
|
||||
/// Parameters used by a work item.
|
||||
work_item_para: Arc<SpinLock<LineDisciplineWorkPara, LocalIrqDisabled>>,
|
||||
}
|
||||
|
||||
pub struct CurrentLine {
|
||||
@ -99,29 +87,23 @@ impl Pollable for LineDiscipline {
|
||||
|
||||
impl LineDiscipline {
|
||||
/// Creates a new line discipline
|
||||
pub fn new(send_signal: LdiscSignalSender) -> Arc<Self> {
|
||||
Arc::new_cyclic(move |line_ref: &Weak<LineDiscipline>| {
|
||||
let line_discipline = line_ref.clone();
|
||||
let work_item = WorkItem::new(Box::new(move || {
|
||||
if let Some(line_discipline) = line_discipline.upgrade() {
|
||||
line_discipline.send_signal_after();
|
||||
}
|
||||
}));
|
||||
Self {
|
||||
current_line: SpinLock::new(CurrentLine::default()),
|
||||
read_buffer: SpinLock::new(RingBuffer::new(BUFFER_CAPACITY)),
|
||||
termios: SpinLock::new(KernelTermios::default()),
|
||||
winsize: SpinLock::new(WinSize::default()),
|
||||
pollee: Pollee::new(),
|
||||
send_signal,
|
||||
work_item,
|
||||
work_item_para: Arc::new(SpinLock::new(LineDisciplineWorkPara::new())),
|
||||
}
|
||||
})
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_line: SpinLock::new(CurrentLine::default()),
|
||||
read_buffer: SpinLock::new(RingBuffer::new(BUFFER_CAPACITY)),
|
||||
termios: SpinLock::new(KernelTermios::default()),
|
||||
winsize: SpinLock::new(WinSize::default()),
|
||||
pollee: Pollee::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Pushes a char to the line discipline
|
||||
pub fn push_char<F2: FnMut(&str)>(&self, ch: u8, echo_callback: F2) {
|
||||
pub fn push_char<F1: FnMut(SigNum), F2: FnMut(&str)>(
|
||||
&self,
|
||||
ch: u8,
|
||||
mut signal_callback: F1,
|
||||
echo_callback: F2,
|
||||
) {
|
||||
let termios = self.termios.lock();
|
||||
|
||||
let ch = if termios.contains_icrnl() && ch == b'\r' {
|
||||
@ -130,8 +112,8 @@ impl LineDiscipline {
|
||||
ch
|
||||
};
|
||||
|
||||
if self.may_send_signal(&termios, ch) {
|
||||
submit_work_item(self.work_item.clone(), WorkPriority::High);
|
||||
if let Some(signum) = char_to_signal(ch, &termios) {
|
||||
signal_callback(signum);
|
||||
// CBREAK mode may require the character to be outputted, so just go ahead.
|
||||
}
|
||||
|
||||
@ -180,27 +162,6 @@ impl LineDiscipline {
|
||||
}
|
||||
}
|
||||
|
||||
fn may_send_signal(&self, termios: &KernelTermios, ch: u8) -> bool {
|
||||
if !termios.is_canonical_mode() || !termios.contains_isig() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let signal = match ch {
|
||||
ch if ch == *termios.get_special_char(CC_C_CHAR::VINTR) => KernelSignal::new(SIGINT),
|
||||
ch if ch == *termios.get_special_char(CC_C_CHAR::VQUIT) => KernelSignal::new(SIGQUIT),
|
||||
_ => return false,
|
||||
};
|
||||
|
||||
if in_interrupt_context() {
|
||||
// `kernel_signal()` may cause sleep, so only construct parameters here.
|
||||
self.work_item_para.lock().kernel_signal = Some(signal);
|
||||
} else {
|
||||
(self.send_signal)(signal);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn check_io_events(&self) -> IoEvents {
|
||||
let buffer = self.read_buffer.lock();
|
||||
|
||||
@ -211,13 +172,6 @@ impl LineDiscipline {
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends a signal later. The signal will be handled by a work queue.
|
||||
fn send_signal_after(&self) {
|
||||
if let Some(signal) = self.work_item_para.lock().kernel_signal.take() {
|
||||
(self.send_signal)(signal);
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: respect output flags
|
||||
fn output_char<F: FnMut(&str)>(&self, ch: u8, termios: &KernelTermios, mut echo_callback: F) {
|
||||
match ch {
|
||||
@ -230,7 +184,7 @@ impl LineDiscipline {
|
||||
}
|
||||
ch if is_printable_char(ch) => print!("{}", char::from(ch)),
|
||||
ch if is_ctrl_char(ch) && termios.contains_echo_ctl() => {
|
||||
let ctrl_char = format!("^{}", get_printable_char(ch));
|
||||
let ctrl_char = format!("^{}", ctrl_char_to_printable(ch));
|
||||
echo_callback(&ctrl_char);
|
||||
}
|
||||
_ => {}
|
||||
@ -393,19 +347,19 @@ fn is_ctrl_char(ch: u8) -> bool {
|
||||
(0..0x20).contains(&ch)
|
||||
}
|
||||
|
||||
fn get_printable_char(ctrl_char: u8) -> char {
|
||||
debug_assert!(is_ctrl_char(ctrl_char));
|
||||
char::from_u32((ctrl_char + b'A' - 1) as u32).unwrap()
|
||||
}
|
||||
fn char_to_signal(ch: u8, termios: &KernelTermios) -> Option<SigNum> {
|
||||
if !termios.is_canonical_mode() || !termios.contains_isig() {
|
||||
return None;
|
||||
}
|
||||
|
||||
struct LineDisciplineWorkPara {
|
||||
kernel_signal: Option<KernelSignal>,
|
||||
}
|
||||
|
||||
impl LineDisciplineWorkPara {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
kernel_signal: None,
|
||||
}
|
||||
match ch {
|
||||
ch if ch == *termios.get_special_char(CC_C_CHAR::VINTR) => Some(SIGINT),
|
||||
ch if ch == *termios.get_special_char(CC_C_CHAR::VQUIT) => Some(SIGQUIT),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn ctrl_char_to_printable(ch: u8) -> char {
|
||||
debug_assert!(is_ctrl_char(ch));
|
||||
char::from_u32((ch + b'A' - 1) as u32).unwrap()
|
||||
}
|
||||
|
@ -14,7 +14,8 @@ use crate::{
|
||||
},
|
||||
prelude::*,
|
||||
process::{
|
||||
signal::{signals::kernel::KernelSignal, PollHandle, Pollable},
|
||||
broadcast_signal_async,
|
||||
signal::{PollHandle, Pollable},
|
||||
JobControl, Terminal,
|
||||
},
|
||||
};
|
||||
@ -40,8 +41,8 @@ pub struct Tty {
|
||||
#[expect(unused)]
|
||||
name: CString,
|
||||
/// line discipline
|
||||
ldisc: Arc<LineDiscipline>,
|
||||
job_control: Arc<JobControl>,
|
||||
ldisc: LineDiscipline,
|
||||
job_control: JobControl,
|
||||
/// driver
|
||||
driver: SpinLock<Weak<TtyDriver>>,
|
||||
weak_self: Weak<Self>,
|
||||
@ -49,11 +50,10 @@ pub struct Tty {
|
||||
|
||||
impl Tty {
|
||||
pub fn new(name: CString) -> Arc<Self> {
|
||||
let (job_control, ldisc) = new_job_control_and_ldisc();
|
||||
Arc::new_cyclic(move |weak_ref| Tty {
|
||||
name,
|
||||
ldisc,
|
||||
job_control,
|
||||
ldisc: LineDiscipline::new(),
|
||||
job_control: JobControl::new(),
|
||||
driver: SpinLock::new(Weak::new()),
|
||||
weak_self: weak_ref.clone(),
|
||||
})
|
||||
@ -66,8 +66,15 @@ impl Tty {
|
||||
pub fn push_char(&self, ch: u8) {
|
||||
// FIXME: Use `early_print` to avoid calling virtio-console.
|
||||
// This is only a workaround
|
||||
self.ldisc
|
||||
.push_char(ch, |content| early_print!("{}", content))
|
||||
self.ldisc.push_char(
|
||||
ch,
|
||||
|signum| {
|
||||
if let Some(foreground) = self.job_control.foreground() {
|
||||
broadcast_signal_async(Arc::downgrade(&foreground), signum);
|
||||
}
|
||||
},
|
||||
|content| early_print!("{}", content),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,25 +163,6 @@ impl Device for Tty {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_job_control_and_ldisc() -> (Arc<JobControl>, Arc<LineDiscipline>) {
|
||||
let job_control = Arc::new(JobControl::new());
|
||||
|
||||
let send_signal = {
|
||||
let cloned_job_control = job_control.clone();
|
||||
move |signal: KernelSignal| {
|
||||
let Some(foreground) = cloned_job_control.foreground() else {
|
||||
return;
|
||||
};
|
||||
|
||||
foreground.broadcast_signal(signal);
|
||||
}
|
||||
};
|
||||
|
||||
let ldisc = LineDiscipline::new(Arc::new(send_signal));
|
||||
|
||||
(job_control, ldisc)
|
||||
}
|
||||
|
||||
pub fn get_n_tty() -> &'static Arc<Tty> {
|
||||
N_TTY.get().unwrap()
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ pub use clone::{clone_child, CloneArgs, CloneFlags};
|
||||
pub use credentials::{Credentials, Gid, Uid};
|
||||
pub use kill::{kill, kill_all, kill_group, tgkill};
|
||||
pub use process::{
|
||||
enqueue_signal_async, spawn_init_process, ExitCode, JobControl, Pgid, Pid, Process,
|
||||
ProcessGroup, Session, Sid, Terminal,
|
||||
broadcast_signal_async, enqueue_signal_async, spawn_init_process, ExitCode, JobControl, Pgid,
|
||||
Pid, Process, ProcessGroup, Session, Sid, Terminal,
|
||||
};
|
||||
pub use process_filter::ProcessFilter;
|
||||
pub use process_vm::{
|
||||
|
@ -725,3 +725,23 @@ pub fn enqueue_signal_async(process: Weak<Process>, signum: SigNum) {
|
||||
work_queue::WorkPriority::High,
|
||||
);
|
||||
}
|
||||
|
||||
/// Broadcasts a process-directed kernel signal asynchronously.
|
||||
///
|
||||
/// This is the asynchronous version of [`ProcessGroup::broadcast_signal`]. By asynchronous, this
|
||||
/// method submits a work item and returns, so this method doesn't sleep and can be used in atomic
|
||||
/// mode.
|
||||
pub fn broadcast_signal_async(process_group: Weak<ProcessGroup>, signal: SigNum) {
|
||||
use super::signal::signals::kernel::KernelSignal;
|
||||
use crate::thread::work_queue;
|
||||
|
||||
let signal = KernelSignal::new(signal);
|
||||
work_queue::submit_work_func(
|
||||
move || {
|
||||
if let Some(process_group) = process_group.upgrade() {
|
||||
process_group.broadcast_signal(signal);
|
||||
}
|
||||
},
|
||||
work_queue::WorkPriority::High,
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user