1 /* 2 * linux-user signal handling tests. 3 * 4 * Copyright (c) 2021 Linaro Ltd 5 * 6 * SPDX-License-Identifier: GPL-2.0-or-later 7 */ 8 9 #include <stdarg.h> 10 #include <stdint.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <unistd.h> 14 #include <errno.h> 15 #include <pthread.h> 16 #include <string.h> 17 #include <signal.h> 18 #include <time.h> 19 #include <sys/time.h> 20 21 static void error1(const char *filename, int line, const char *fmt, ...) 22 { 23 va_list ap; 24 va_start(ap, fmt); 25 fprintf(stderr, "%s:%d: ", filename, line); 26 vfprintf(stderr, fmt, ap); 27 fprintf(stderr, "\n"); 28 va_end(ap); 29 exit(1); 30 } 31 32 static int __chk_error(const char *filename, int line, int ret) 33 { 34 if (ret < 0) { 35 error1(filename, line, "%m (ret=%d, errno=%d/%s)", 36 ret, errno, strerror(errno)); 37 } 38 return ret; 39 } 40 41 #define error(fmt, ...) error1(__FILE__, __LINE__, fmt, ## __VA_ARGS__) 42 43 #define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret)) 44 45 /* 46 * Thread handling 47 */ 48 typedef struct ThreadJob ThreadJob; 49 50 struct ThreadJob { 51 int number; 52 int sleep; 53 int count; 54 }; 55 56 static pthread_t *threads; 57 static int max_threads = 10; 58 __thread int signal_count; 59 int total_signal_count; 60 61 static void *background_thread_func(void *arg) 62 { 63 ThreadJob *job = (ThreadJob *) arg; 64 65 printf("thread%d: started\n", job->number); 66 while (total_signal_count < job->count) { 67 usleep(job->sleep); 68 } 69 printf("thread%d: saw %d alarms from %d\n", job->number, 70 signal_count, total_signal_count); 71 return NULL; 72 } 73 74 static void spawn_threads(void) 75 { 76 int i; 77 threads = calloc(sizeof(pthread_t), max_threads); 78 79 for (i = 0; i < max_threads; i++) { 80 ThreadJob *job = calloc(sizeof(ThreadJob), 1); 81 job->number = i; 82 job->sleep = i * 1000; 83 job->count = i * 100; 84 pthread_create(threads + i, NULL, background_thread_func, job); 85 } 86 } 87 88 static void close_threads(void) 89 { 90 int i; 91 for (i = 0; i < max_threads; i++) { 92 pthread_join(threads[i], NULL); 93 } 94 free(threads); 95 threads = NULL; 96 } 97 98 static void sig_alarm(int sig, siginfo_t *info, void *puc) 99 { 100 if (sig != SIGRTMIN) { 101 error("unexpected signal"); 102 } 103 signal_count++; 104 __atomic_fetch_add(&total_signal_count, 1, __ATOMIC_SEQ_CST); 105 } 106 107 static void test_signals(void) 108 { 109 struct sigaction act; 110 struct itimerspec it; 111 timer_t tid; 112 struct sigevent sev; 113 114 /* Set up SIG handler */ 115 act.sa_sigaction = sig_alarm; 116 sigemptyset(&act.sa_mask); 117 act.sa_flags = SA_SIGINFO; 118 chk_error(sigaction(SIGRTMIN, &act, NULL)); 119 120 /* Create POSIX timer */ 121 sev.sigev_notify = SIGEV_SIGNAL; 122 sev.sigev_signo = SIGRTMIN; 123 sev.sigev_value.sival_ptr = &tid; 124 chk_error(timer_create(CLOCK_REALTIME, &sev, &tid)); 125 126 it.it_interval.tv_sec = 0; 127 it.it_interval.tv_nsec = 1000000; 128 it.it_value.tv_sec = 0; 129 it.it_value.tv_nsec = 1000000; 130 chk_error(timer_settime(tid, 0, &it, NULL)); 131 132 spawn_threads(); 133 134 do { 135 usleep(1000); 136 } while (total_signal_count < 2000); 137 138 printf("shutting down after: %d signals\n", total_signal_count); 139 140 close_threads(); 141 142 chk_error(timer_delete(tid)); 143 } 144 145 int main(int argc, char **argv) 146 { 147 test_signals(); 148 return 0; 149 } 150