[test] Implement unit test for pselect
This commit is contained in:
parent
56528f67da
commit
382bc812f1
@ -38,11 +38,15 @@ pub fn do_handle_interrupt(
|
|||||||
/// Broadcast interrupts to threads by sending POSIX signals.
|
/// Broadcast interrupts to threads by sending POSIX signals.
|
||||||
pub fn broadcast_interrupts() -> Result<usize> {
|
pub fn broadcast_interrupts() -> Result<usize> {
|
||||||
let should_interrupt_thread = |thread: &&ThreadRef| -> bool {
|
let should_interrupt_thread = |thread: &&ThreadRef| -> bool {
|
||||||
// TODO: check Thread::sig_mask to reduce false positives
|
let should_interrupt = thread.process().is_forced_to_exit()
|
||||||
thread.process().is_forced_to_exit()
|
|
||||||
|| thread.is_forced_to_stop()
|
|| 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()
|
let num_signaled_threads = crate::process::table::get_all_threads()
|
||||||
|
@ -14,6 +14,13 @@ pub struct SigQueues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
pub fn new() -> Self {
|
||||||
let count = 0;
|
let count = 0;
|
||||||
let std_queues = (0..COUNT_STD_SIGS).map(|_| None).collect();
|
let std_queues = (0..COUNT_STD_SIGS).map(|_| None).collect();
|
||||||
@ -31,6 +38,41 @@ impl SigQueues {
|
|||||||
self.count == 0
|
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<dyn Signal>) {
|
pub fn enqueue(&mut self, signal: Box<dyn Signal>) {
|
||||||
let signum = signal.num();
|
let signum = signal.num();
|
||||||
if signum.is_std() {
|
if signum.is_std() {
|
||||||
@ -81,13 +123,7 @@ impl SigQueues {
|
|||||||
// POSIX leaves unspecified which to deliver first if there are multiple
|
// POSIX leaves unspecified which to deliver first if there are multiple
|
||||||
// pending standard signals. So we are free to define our own. The
|
// pending standard signals. So we are free to define our own. The
|
||||||
// principle is to give more urgent signals higher priority (like SIGKILL).
|
// principle is to give more urgent signals higher priority (like SIGKILL).
|
||||||
const ORDERED_STD_SIGS: [SigNum; COUNT_STD_SIGS] = [
|
for &signum in &Self::ORDERED_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 {
|
|
||||||
if blocked.contains(signum) {
|
if blocked.contains(signum) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -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 \
|
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 \
|
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 \
|
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
|
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
|
# Benchmarks: need to be compiled and run by bench-% target
|
||||||
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput
|
BENCHES := spawn_and_exit_latency pipe_throughput unix_socket_throughput
|
||||||
|
5
test/pselect/Makefile
Normal file
5
test/pselect/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
include ../test_common.mk
|
||||||
|
|
||||||
|
EXTRA_C_FLAGS :=
|
||||||
|
EXTRA_LINK_FLAGS := -lpthread
|
||||||
|
BIN_ARGS :=
|
83
test/pselect/main.c
Normal file
83
test/pselect/main.c
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/timerfd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdint.h> // for uint64_t
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user