proc: more signal determinism, proper process exit in mt mode
This commit is contained in:
parent
d7111e8d99
commit
cbd823e17b
@ -324,13 +324,32 @@ impl Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Raises a signal for the specified process
|
/// Raises a signal for the specified process
|
||||||
pub fn raise_signal(self: &Arc<Self>, signal: Signal) {
|
///
|
||||||
let thread = self.inner.read().threads[0].clone();
|
/// If sender is Some(id) - signal was sent by some thread in some (possibly this) process.
|
||||||
thread.raise_signal(signal);
|
/// If sender is None - signal was caused by some fault or some other non-deliberate cause.
|
||||||
|
pub fn raise_signal(self: &Arc<Self>, sender: Option<ThreadId>, signal: Signal) {
|
||||||
|
// If signal was raised by a thread within the process, consider the signal a
|
||||||
|
// "synchronous-like" and deliver it to the same thread that issued it.
|
||||||
|
|
||||||
|
let destination = {
|
||||||
|
let inner = self.inner.read();
|
||||||
|
if let Some(thread) = sender.and_then(|id| inner.threads.iter().find(|t| t.id == id)) {
|
||||||
|
// Behave like a "synchronous" signal, because a thread within a process asked to
|
||||||
|
// do something with the process itself. Make the requesting thread handle the
|
||||||
|
// signal.
|
||||||
|
thread.clone()
|
||||||
|
} else {
|
||||||
|
// Asynchronous signal not caused by any thread within the process, basically,
|
||||||
|
// any thread can handle it, but for determinism's sake, let it be the main one
|
||||||
|
inner.threads[0].clone()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
destination.raise_signal(signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raises a signal for the specified process group
|
/// Raises a signal for the specified process group
|
||||||
pub fn signal_group(group_id: ProcessGroupId, signal: Signal) {
|
pub fn signal_group(sender: Option<ThreadId>, group_id: ProcessGroupId, signal: Signal) {
|
||||||
MANAGER.for_each(|_, proc| {
|
MANAGER.for_each(|_, proc| {
|
||||||
let inner = proc.inner.read();
|
let inner = proc.inner.read();
|
||||||
if !proc.has_exited() && inner.group_id == group_id {
|
if !proc.has_exited() && inner.group_id == group_id {
|
||||||
@ -342,7 +361,7 @@ impl Process {
|
|||||||
signal
|
signal
|
||||||
);
|
);
|
||||||
drop(inner);
|
drop(inner);
|
||||||
proc.raise_signal(signal);
|
proc.raise_signal(sender, signal);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -398,6 +417,9 @@ impl Process {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Terminates all threads with the exclusion of `except`. This may be useful when a thread
|
||||||
|
/// wants the whole process to terminate: the asking thread itself must be exempt from this
|
||||||
|
/// termination, as it's the one doing the cleanup.
|
||||||
pub async fn terminate_others(&self, except: ThreadId) {
|
pub async fn terminate_others(&self, except: ThreadId) {
|
||||||
let mut inner = self.inner.write();
|
let mut inner = self.inner.write();
|
||||||
|
|
||||||
@ -407,7 +429,9 @@ impl Process {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log::info!("Terminate thread {}", thread.id);
|
log::info!("Terminate thread {}", thread.id);
|
||||||
thread.terminate().await;
|
thread.terminate();
|
||||||
|
thread.exit.wait().await;
|
||||||
|
log::debug!("{} died", thread.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
inner.retain_thread(except);
|
inner.retain_thread(except);
|
||||||
|
@ -82,6 +82,14 @@ impl CpuQueue {
|
|||||||
assert!(sched.in_queue);
|
assert!(sched.in_queue);
|
||||||
assert!(core::ptr::eq(self, sched.queue.unwrap()));
|
assert!(core::ptr::eq(self, sched.queue.unwrap()));
|
||||||
|
|
||||||
|
if thread.kill.is_signalled() {
|
||||||
|
sched.state = ThreadState::Terminated;
|
||||||
|
sched.in_queue = false;
|
||||||
|
sched.queue = None;
|
||||||
|
thread.set_terminated();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
match sched.state {
|
match sched.state {
|
||||||
ThreadState::Ready => {
|
ThreadState::Ready => {
|
||||||
sched.state = ThreadState::Running;
|
sched.state = ThreadState::Running;
|
||||||
|
@ -81,6 +81,7 @@ pub struct Thread {
|
|||||||
signal_queue: SegQueue<Signal>,
|
signal_queue: SegQueue<Signal>,
|
||||||
|
|
||||||
pub exit: Arc<BoolEvent>,
|
pub exit: Arc<BoolEvent>,
|
||||||
|
pub kill: BoolEvent,
|
||||||
|
|
||||||
/// CPU scheduling affinity mask
|
/// CPU scheduling affinity mask
|
||||||
pub affinity: ThreadAffinity,
|
pub affinity: ThreadAffinity,
|
||||||
@ -135,6 +136,7 @@ impl Thread {
|
|||||||
inner: IrqSafeSpinlock::new(ThreadInner { signal_entry: None }),
|
inner: IrqSafeSpinlock::new(ThreadInner { signal_entry: None }),
|
||||||
signal_queue: SegQueue::new(),
|
signal_queue: SegQueue::new(),
|
||||||
exit: Arc::new(BoolEvent::new()),
|
exit: Arc::new(BoolEvent::new()),
|
||||||
|
kill: BoolEvent::new(),
|
||||||
|
|
||||||
affinity: ThreadAffinity::any_cpu(),
|
affinity: ThreadAffinity::any_cpu(),
|
||||||
});
|
});
|
||||||
@ -403,11 +405,16 @@ impl Thread {
|
|||||||
self.enqueue_to(queue);
|
self.enqueue_to(queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requests thread termination and blocks until said thread finishes fully
|
/// Requests thread termination. This function only asks the thread to terminate, which will
|
||||||
pub async fn terminate(self: &Arc<Self>) {
|
/// happen the next time it's seen by the scheduler.
|
||||||
// Will not abort the execution: called from another thread
|
pub fn terminate(self: &Arc<Self>) {
|
||||||
self.dequeue(ThreadState::Terminated);
|
// Will not abort the execution: called from another thread.
|
||||||
self.exit.wait().await;
|
//
|
||||||
|
// Instead of dequeueing the thread, like done with a synchronous exit (when the thread
|
||||||
|
// exits on its own), instead, the thread needs to be **enqueued**, because we need to wake
|
||||||
|
// it up in order to make it die itself.
|
||||||
|
self.kill.signal_saturating();
|
||||||
|
self.enqueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current thread on the CPU.
|
/// Returns the current thread on the CPU.
|
||||||
@ -496,7 +503,6 @@ impl CurrentThread {
|
|||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: test multithreaded process exit
|
|
||||||
/// Terminate the parent process of the thread, including all other threads and the current
|
/// Terminate the parent process of the thread, including all other threads and the current
|
||||||
/// thread itself
|
/// thread itself
|
||||||
pub fn exit_process(&self, code: ExitCode) -> ! {
|
pub fn exit_process(&self, code: ExitCode) -> ! {
|
||||||
|
@ -156,7 +156,7 @@ impl<O: TerminalOutput> Terminal<O> {
|
|||||||
self.output.notify_readers();
|
self.output.notify_readers();
|
||||||
|
|
||||||
if let Some(group_id) = *self.input.signal_pgroup.read() {
|
if let Some(group_id) = *self.input.signal_pgroup.read() {
|
||||||
Process::signal_group(group_id, Signal::Interrupted);
|
Process::signal_group(None, group_id, Signal::Interrupted);
|
||||||
self.input.ready_ring.notify_all();
|
self.input.ready_ring.notify_all();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -205,8 +205,9 @@ pub(crate) fn set_signal_entry(ip: usize, sp: usize) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn send_signal(pid: ProcessId, signal: Signal) -> Result<(), Error> {
|
pub(crate) fn send_signal(pid: ProcessId, signal: Signal) -> Result<(), Error> {
|
||||||
|
let thread = Thread::current();
|
||||||
let target = Process::get(pid).ok_or(Error::DoesNotExist)?;
|
let target = Process::get(pid).ok_or(Error::DoesNotExist)?;
|
||||||
target.raise_signal(signal);
|
target.raise_signal(Some(thread.id), signal);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,9 +276,11 @@ pub(crate) fn wait_thread(id: u32) -> Result<(), Error> {
|
|||||||
let this_thread = Thread::current();
|
let this_thread = Thread::current();
|
||||||
let process = this_thread.process();
|
let process = this_thread.process();
|
||||||
|
|
||||||
block!(process.wait_for_thread(tid).await)??;
|
log::debug!("wait_thread({id})");
|
||||||
|
let result = block!(process.wait_for_thread(tid).await)?;
|
||||||
|
log::debug!("www -> {result:?}");
|
||||||
|
|
||||||
Ok(())
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_thread_option(option: &mut ThreadOption) -> Result<(), Error> {
|
pub(crate) fn get_thread_option(option: &mut ThreadOption) -> Result<(), Error> {
|
||||||
|
@ -166,3 +166,13 @@ impl ProgramArgumentInner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Signal {
|
||||||
|
pub fn is_synchronous(&self) -> bool {
|
||||||
|
matches!(self, Self::MemoryAccessViolation)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_handleable(&self) -> bool {
|
||||||
|
!matches!(self, Self::Killed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
19
test.c
19
test.c
@ -2,31 +2,18 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <sys/yggdrasil.h>
|
#include <sys/yggdrasil.h>
|
||||||
|
|
||||||
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
|
|
||||||
static void *function(void *arg) {
|
static void *function(void *arg) {
|
||||||
pthread_barrier_t *barrier = (pthread_barrier_t *) arg;
|
abort();
|
||||||
printf("[child] waiting for parent\n");
|
|
||||||
pthread_barrier_wait(barrier);
|
|
||||||
printf("[child] barrier signalled!!\n");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
int main(int argc, const char **argv) {
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
pthread_barrier_t barrier;
|
|
||||||
|
|
||||||
pthread_barrier_init(&barrier, NULL, 2);
|
|
||||||
|
|
||||||
printf("[main] will make the child wait on a barrier\n");
|
|
||||||
assert(pthread_create(&thread, NULL, function, (void *) &barrier) == 0);
|
|
||||||
|
|
||||||
sleep(3);
|
|
||||||
pthread_barrier_wait(&barrier);
|
|
||||||
printf("[main] barrier signalled!!\n");
|
|
||||||
|
|
||||||
|
assert(pthread_create(&thread, NULL, function, (void *) NULL) == 0);
|
||||||
pthread_join(thread, NULL);
|
pthread_join(thread, NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user