diff --git a/src/libos/src/interrupt/mod.rs b/src/libos/src/interrupt/mod.rs index 47d1a393..1c45a8f4 100644 --- a/src/libos/src/interrupt/mod.rs +++ b/src/libos/src/interrupt/mod.rs @@ -38,11 +38,15 @@ pub fn do_handle_interrupt( /// Broadcast interrupts to threads by sending POSIX signals. pub fn broadcast_interrupts() -> Result { let should_interrupt_thread = |thread: &&ThreadRef| -> bool { - // TODO: check Thread::sig_mask to reduce false positives - thread.process().is_forced_to_exit() + let should_interrupt = thread.process().is_forced_to_exit() || thread.is_forced_to_stop() - || !thread.sig_queues().read().unwrap().empty() - || !thread.process().sig_queues().read().unwrap().empty() + || !thread.process().sig_queues().read().unwrap().empty(); + + // Check Thread::sig_mask to avoid wrong interrupts + let sig_queues = thread.sig_queues().read().unwrap(); + let sig_mask = thread.sig_mask().read().unwrap(); + + should_interrupt || sig_queues.has_valid_signal(&sig_mask) }; let num_signaled_threads = crate::process::table::get_all_threads() diff --git a/src/libos/src/signal/sig_queues.rs b/src/libos/src/signal/sig_queues.rs index 6dba2d9a..0dccacb7 100644 --- a/src/libos/src/signal/sig_queues.rs +++ b/src/libos/src/signal/sig_queues.rs @@ -14,6 +14,13 @@ pub struct SigQueues { } impl SigQueues { + const ORDERED_STD_SIGS: [SigNum; COUNT_STD_SIGS] = [ + SIGKILL, SIGTERM, SIGSTOP, SIGCONT, SIGSEGV, SIGILL, SIGHUP, SIGINT, SIGQUIT, SIGTRAP, + SIGABRT, SIGBUS, SIGFPE, SIGUSR1, SIGUSR2, SIGPIPE, SIGALRM, SIGSTKFLT, SIGCHLD, SIGTSTP, + SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, SIGIO, SIGPWR, + SIGSYS, + ]; + pub fn new() -> Self { let count = 0; let std_queues = (0..COUNT_STD_SIGS).map(|_| None).collect(); @@ -31,6 +38,41 @@ impl SigQueues { self.count == 0 } + pub fn has_valid_signal(&self, blocked: &SigSet) -> bool { + // Fast path for the common case of no pending signals + if self.empty() { + return false; + } + + // Check standard signals. + for &signum in &Self::ORDERED_STD_SIGS { + if blocked.contains(signum) { + continue; + } + + let queue = self.get_std_queue(signum); + if queue.is_some() { + return true; + } + } + + // If no standard signals, then check real-time signals. + for signum in MIN_RT_SIG_NUM..=MAX_RT_SIG_NUM { + let signum = unsafe { SigNum::from_u8_unchecked(signum) }; + if blocked.contains(signum) { + continue; + } + + let queue = self.get_rt_queue(signum); + if !queue.is_empty() { + return true; + } + } + + // There must be pending but blocked signals + false + } + pub fn enqueue(&mut self, signal: Box) { let signum = signal.num(); if signum.is_std() { @@ -81,13 +123,7 @@ impl SigQueues { // POSIX leaves unspecified which to deliver first if there are multiple // pending standard signals. So we are free to define our own. The // principle is to give more urgent signals higher priority (like SIGKILL). - const ORDERED_STD_SIGS: [SigNum; COUNT_STD_SIGS] = [ - SIGKILL, SIGTERM, SIGSTOP, SIGCONT, SIGSEGV, SIGILL, SIGHUP, SIGINT, SIGQUIT, SIGTRAP, - SIGABRT, SIGBUS, SIGFPE, SIGUSR1, SIGUSR2, SIGPIPE, SIGALRM, SIGSTKFLT, SIGCHLD, - SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM, SIGPROF, SIGWINCH, - SIGIO, SIGPWR, SIGSYS, - ]; - for &signum in &ORDERED_STD_SIGS { + for &signum in &Self::ORDERED_STD_SIGS { if blocked.contains(signum) { continue; } diff --git a/test/Makefile b/test/Makefile index dced6048..20a5a9b1 100644 --- a/test/Makefile +++ b/test/Makefile @@ -21,7 +21,7 @@ TEST_DEPS := client data_sink naughty_child TESTS ?= env empty hello_world malloc mmap file fs_perms getpid spawn sched pipe time timerfd \ truncate readdir mkdir open stat link symlink chmod chown tls pthread system_info rlimit \ server server_epoll unix_socket cout hostfs cpuid rdtsc device sleep exit_group posix_flock \ - ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait \ + ioctl fcntl eventfd emulate_syscall access signal sysinfo prctl rename procfs wait pselect \ spawn_attribute exec statfs random umask pgrp vfork mount flock utimes shm epoll brk posix_shm # Benchmarks: need to be compiled and run by bench-% target BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput diff --git a/test/pselect/Makefile b/test/pselect/Makefile new file mode 100644 index 00000000..5c1ee8c1 --- /dev/null +++ b/test/pselect/Makefile @@ -0,0 +1,5 @@ +include ../test_common.mk + +EXTRA_C_FLAGS := +EXTRA_LINK_FLAGS := -lpthread +BIN_ARGS := diff --git a/test/pselect/main.c b/test/pselect/main.c new file mode 100644 index 00000000..bc36cac2 --- /dev/null +++ b/test/pselect/main.c @@ -0,0 +1,83 @@ +#include +#include +#include +#include +#include +#include +#include // for uint64_t +#include + +// Signal handler for SIGUSR1 +void sigusr1_handler(int sig) { + printf("SIGUSR1 received\n"); +} + +void *send_signal(void *arg) { + pthread_t main_thread_id = *(pthread_t *)arg; + sleep(1); + pthread_kill(main_thread_id, SIGUSR1); + return NULL; +} + +int main() { + struct sigaction sa; + sa.sa_handler = sigusr1_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGUSR1, &sa, NULL); + + sigset_t sigmask; + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGUSR1); + + // Access pthread id + pthread_t main_thread_id = pthread_self(); + + // Spawn new thread for sending signal when call pselect syscall + pthread_t signal_thread; + if (pthread_create(&signal_thread, NULL, send_signal, &main_thread_id) != 0) { + perror("pthread_create"); + return 1; + } + + int timer_fd = timerfd_create(CLOCK_REALTIME, 0); + if (timer_fd == -1) { + perror("timerfd_create"); + return 1; + } + + struct itimerspec timerValue; + timerValue.it_value.tv_sec = 2; + timerValue.it_value.tv_nsec = 0; + timerValue.it_interval.tv_sec = 0; + timerValue.it_interval.tv_nsec = 0; + if (timerfd_settime(timer_fd, 0, &timerValue, NULL) == -1) { + perror("timerfd_settime"); + close(timer_fd); + return 1; + } + + fd_set readfds; + FD_ZERO(&readfds); + FD_SET(timer_fd, &readfds); + + int ready = pselect(timer_fd + 1, &readfds, NULL, NULL, NULL, &sigmask); + + if (ready == -1) { + perror("pselect6"); + } else if (ready == 0) { + // Impossible case + printf("No input - timeout reached\n"); + } else { + if (FD_ISSET(timer_fd, &readfds)) { + printf("Timer expired, pselect blocked SIGUSR1 signal successfully\n"); + uint64_t expirations; + read(timer_fd, &expirations, sizeof(expirations)); + } + } + + pthread_join(signal_thread, NULL); + close(timer_fd); + + return 0; +}