1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2018 Google, Inc. 4 * 5 * tgkill() delivers a signal to a specific thread. Test this by installing 6 * a SIGUSR1 handler which records the current pthread ID. Start a number 7 * of threads in parallel, then one-by-one call tgkill(..., tid, SIGUSR1) 8 * and check that the expected pthread ID was recorded. 9 */ 10 11#include <pthread.h> 12#include <stdlib.h> 13 14#include "tst_safe_pthread.h" 15#include "tst_test.h" 16#include "tgkill.h" 17 18struct thread_state { 19 pthread_t thread; 20 pid_t tid; 21}; 22 23static char *str_threads; 24static int n_threads = 10; 25static struct thread_state *threads; 26 27static pthread_t sigusr1_thread; 28 29static void sigusr1_handler(int signum __attribute__((unused))) 30{ 31 sigusr1_thread = pthread_self(); 32} 33 34static void *thread_func(void *arg) 35{ 36 struct thread_state *thread = arg; 37 38 /** 39 * There is no standard way to map pthread -> tid, so we will have the 40 * child stash its own tid then notify the parent that the stashed tid 41 * is available. 42 */ 43 thread->tid = sys_gettid(); 44 45 TST_CHECKPOINT_WAKE(0); 46 47 TST_CHECKPOINT_WAIT(1); 48 49 return arg; 50} 51 52static void start_thread(struct thread_state *thread) 53{ 54 SAFE_PTHREAD_CREATE(&thread->thread, NULL, thread_func, thread); 55 56 TST_CHECKPOINT_WAIT(0); 57} 58 59static void stop_threads(void) 60{ 61 int i; 62 63 TST_CHECKPOINT_WAKE2(1, n_threads); 64 65 for (i = 0; i < n_threads; i++) { 66 if (threads[i].tid == -1) 67 continue; 68 69 SAFE_PTHREAD_JOIN(threads[i].thread, NULL); 70 threads[i].tid = -1; 71 } 72 73 if (threads) 74 free(threads); 75} 76 77static void run(void) 78{ 79 int i; 80 81 for (i = 0; i < n_threads; i++) { 82 sigusr1_thread = pthread_self(); 83 84 TEST(sys_tgkill(getpid(), threads[i].tid, SIGUSR1)); 85 if (TST_RET) { 86 tst_res(TFAIL | TTERRNO, "tgkill() failed"); 87 return; 88 } 89 90 while (pthread_equal(sigusr1_thread, pthread_self())) 91 usleep(1000); 92 93 if (!pthread_equal(sigusr1_thread, threads[i].thread)) { 94 tst_res(TFAIL, "SIGUSR1 delivered to wrong thread"); 95 return; 96 } 97 } 98 99 tst_res(TPASS, "SIGUSR1 delivered to correct threads"); 100} 101 102static void setup(void) 103{ 104 int i; 105 106 if (tst_parse_int(str_threads, &n_threads, 1, INT_MAX)) 107 tst_brk(TBROK, "Invalid number of threads '%s'", str_threads); 108 109 threads = SAFE_MALLOC(sizeof(*threads) * n_threads); 110 111 struct sigaction sigusr1 = { 112 .sa_handler = sigusr1_handler, 113 }; 114 SAFE_SIGACTION(SIGUSR1, &sigusr1, NULL); 115 116 for (i = 0; i < n_threads; i++) 117 threads[i].tid = -1; 118 119 for (i = 0; i < n_threads; i++) 120 start_thread(&threads[i]); 121} 122 123static struct tst_test test = { 124 .options = (struct tst_option[]) { 125 {"t:", &str_threads, "Number of threads (default 10)"}, 126 {} 127 }, 128 .needs_checkpoints = 1, 129 .setup = setup, 130 .test_all = run, 131 .cleanup = stop_threads, 132}; 133