• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 struct thread_state {
19 	pthread_t thread;
20 	pid_t tid;
21 };
22 
23 static char *str_threads;
24 static int n_threads = 10;
25 static struct thread_state *threads;
26 
27 static pthread_t sigusr1_thread;
28 
sigusr1_handler(int signum)29 static void sigusr1_handler(int signum __attribute__((unused)))
30 {
31 	sigusr1_thread = pthread_self();
32 }
33 
thread_func(void * arg)34 static 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 
start_thread(struct thread_state * thread)52 static 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 
stop_threads(void)59 static 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 
run(void)77 static 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 
setup(void)102 static 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 
123 static struct tst_option options[] = {
124 	{"t:", &str_threads, "-t       Number of threads (default 10)"},
125 	{NULL, NULL, NULL},
126 };
127 
128 static struct tst_test test = {
129 	.options = options,
130 	.needs_checkpoints = 1,
131 	.setup = setup,
132 	.test_all = run,
133 	.cleanup = stop_threads,
134 };
135