[test] Implement unit test for pselect
This commit is contained in:
		
							parent
							
								
									d27f0d5cd0
								
							
						
					
					
						commit
						d6741c0096
					
				| @ -38,11 +38,15 @@ pub fn do_handle_interrupt( | ||||
| /// Broadcast interrupts to threads by sending POSIX signals.
 | ||||
| pub fn broadcast_interrupts() -> Result<usize> { | ||||
|     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() | ||||
|  | ||||
| @ -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<dyn Signal>) { | ||||
|         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; | ||||
|             } | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
							
								
								
									
										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