1e66f31c5Sopenharmony_ci/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
2e66f31c5Sopenharmony_ci *
3e66f31c5Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
4e66f31c5Sopenharmony_ci * of this software and associated documentation files (the "Software"), to
5e66f31c5Sopenharmony_ci * deal in the Software without restriction, including without limitation the
6e66f31c5Sopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7e66f31c5Sopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is
8e66f31c5Sopenharmony_ci * furnished to do so, subject to the following conditions:
9e66f31c5Sopenharmony_ci *
10e66f31c5Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
11e66f31c5Sopenharmony_ci * all copies or substantial portions of the Software.
12e66f31c5Sopenharmony_ci *
13e66f31c5Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14e66f31c5Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15e66f31c5Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16e66f31c5Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17e66f31c5Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18e66f31c5Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19e66f31c5Sopenharmony_ci * IN THE SOFTWARE.
20e66f31c5Sopenharmony_ci */
21e66f31c5Sopenharmony_ci
22e66f31c5Sopenharmony_ci
23e66f31c5Sopenharmony_ci/* This test does not pretend to be cross-platform. */
24e66f31c5Sopenharmony_ci#ifndef _WIN32
25e66f31c5Sopenharmony_ci
26e66f31c5Sopenharmony_ci#include "uv.h"
27e66f31c5Sopenharmony_ci#include "task.h"
28e66f31c5Sopenharmony_ci
29e66f31c5Sopenharmony_ci#include <errno.h>
30e66f31c5Sopenharmony_ci#include <signal.h>
31e66f31c5Sopenharmony_ci#include <stdarg.h>
32e66f31c5Sopenharmony_ci#include <stdio.h>
33e66f31c5Sopenharmony_ci#include <stdlib.h>
34e66f31c5Sopenharmony_ci#include <string.h>
35e66f31c5Sopenharmony_ci#include <unistd.h>
36e66f31c5Sopenharmony_ci
37e66f31c5Sopenharmony_ci/* The value of NUM_SIGNAL_HANDLING_THREADS is not arbitrary; it needs to be a
38e66f31c5Sopenharmony_ci * multiple of three for reasons that will become clear when you scroll down.
39e66f31c5Sopenharmony_ci * We're basically creating three different thread groups.  The total needs
40e66f31c5Sopenharmony_ci * to be divisible by three in order for the numbers in the final check to
41e66f31c5Sopenharmony_ci * match up.
42e66f31c5Sopenharmony_ci */
43e66f31c5Sopenharmony_ci#define NUM_SIGNAL_HANDLING_THREADS 24
44e66f31c5Sopenharmony_ci#define NUM_LOOP_CREATING_THREADS 10
45e66f31c5Sopenharmony_ci
46e66f31c5Sopenharmony_cienum signal_action {
47e66f31c5Sopenharmony_ci  ONLY_SIGUSR1,
48e66f31c5Sopenharmony_ci  ONLY_SIGUSR2,
49e66f31c5Sopenharmony_ci  SIGUSR1_AND_SIGUSR2
50e66f31c5Sopenharmony_ci};
51e66f31c5Sopenharmony_ci
52e66f31c5Sopenharmony_cistatic uv_sem_t sem;
53e66f31c5Sopenharmony_cistatic uv_mutex_t lock;
54e66f31c5Sopenharmony_cistatic int stop = 0;
55e66f31c5Sopenharmony_ci
56e66f31c5Sopenharmony_cistatic int signal1_cb_counter = 0;
57e66f31c5Sopenharmony_cistatic int signal2_cb_counter = 0;
58e66f31c5Sopenharmony_cistatic int loop_creation_counter = 0;
59e66f31c5Sopenharmony_ci
60e66f31c5Sopenharmony_ci
61e66f31c5Sopenharmony_cistatic void increment_counter(int* counter) {
62e66f31c5Sopenharmony_ci  uv_mutex_lock(&lock);
63e66f31c5Sopenharmony_ci  ++(*counter);
64e66f31c5Sopenharmony_ci  uv_mutex_unlock(&lock);
65e66f31c5Sopenharmony_ci}
66e66f31c5Sopenharmony_ci
67e66f31c5Sopenharmony_ci
68e66f31c5Sopenharmony_cistatic void signal1_cb(uv_signal_t* handle, int signum) {
69e66f31c5Sopenharmony_ci  ASSERT_EQ(signum, SIGUSR1);
70e66f31c5Sopenharmony_ci  increment_counter(&signal1_cb_counter);
71e66f31c5Sopenharmony_ci  uv_signal_stop(handle);
72e66f31c5Sopenharmony_ci}
73e66f31c5Sopenharmony_ci
74e66f31c5Sopenharmony_ci
75e66f31c5Sopenharmony_cistatic void signal2_cb(uv_signal_t* handle, int signum) {
76e66f31c5Sopenharmony_ci  ASSERT_EQ(signum, SIGUSR2);
77e66f31c5Sopenharmony_ci  increment_counter(&signal2_cb_counter);
78e66f31c5Sopenharmony_ci  uv_signal_stop(handle);
79e66f31c5Sopenharmony_ci}
80e66f31c5Sopenharmony_ci
81e66f31c5Sopenharmony_ci
82e66f31c5Sopenharmony_cistatic void signal_handling_worker(void* context) {
83e66f31c5Sopenharmony_ci  enum signal_action action;
84e66f31c5Sopenharmony_ci  uv_signal_t signal1a;
85e66f31c5Sopenharmony_ci  uv_signal_t signal1b;
86e66f31c5Sopenharmony_ci  uv_signal_t signal2;
87e66f31c5Sopenharmony_ci  uv_loop_t loop;
88e66f31c5Sopenharmony_ci  int r;
89e66f31c5Sopenharmony_ci
90e66f31c5Sopenharmony_ci  action = (enum signal_action) (uintptr_t) context;
91e66f31c5Sopenharmony_ci
92e66f31c5Sopenharmony_ci  ASSERT_OK(uv_loop_init(&loop));
93e66f31c5Sopenharmony_ci
94e66f31c5Sopenharmony_ci  /* Setup the signal watchers and start them. */
95e66f31c5Sopenharmony_ci  if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) {
96e66f31c5Sopenharmony_ci    r = uv_signal_init(&loop, &signal1a);
97e66f31c5Sopenharmony_ci    ASSERT_OK(r);
98e66f31c5Sopenharmony_ci    r = uv_signal_start(&signal1a, signal1_cb, SIGUSR1);
99e66f31c5Sopenharmony_ci    ASSERT_OK(r);
100e66f31c5Sopenharmony_ci    r = uv_signal_init(&loop, &signal1b);
101e66f31c5Sopenharmony_ci    ASSERT_OK(r);
102e66f31c5Sopenharmony_ci    r = uv_signal_start(&signal1b, signal1_cb, SIGUSR1);
103e66f31c5Sopenharmony_ci    ASSERT_OK(r);
104e66f31c5Sopenharmony_ci  }
105e66f31c5Sopenharmony_ci
106e66f31c5Sopenharmony_ci  if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) {
107e66f31c5Sopenharmony_ci    r = uv_signal_init(&loop, &signal2);
108e66f31c5Sopenharmony_ci    ASSERT_OK(r);
109e66f31c5Sopenharmony_ci    r = uv_signal_start(&signal2, signal2_cb, SIGUSR2);
110e66f31c5Sopenharmony_ci    ASSERT_OK(r);
111e66f31c5Sopenharmony_ci  }
112e66f31c5Sopenharmony_ci
113e66f31c5Sopenharmony_ci  /* Signal watchers are now set up. */
114e66f31c5Sopenharmony_ci  uv_sem_post(&sem);
115e66f31c5Sopenharmony_ci
116e66f31c5Sopenharmony_ci  /* Wait for all signals. The signal callbacks stop the watcher, so uv_run
117e66f31c5Sopenharmony_ci   * will return when all signal watchers caught a signal.
118e66f31c5Sopenharmony_ci   */
119e66f31c5Sopenharmony_ci  r = uv_run(&loop, UV_RUN_DEFAULT);
120e66f31c5Sopenharmony_ci  ASSERT_OK(r);
121e66f31c5Sopenharmony_ci
122e66f31c5Sopenharmony_ci  /* Restart the signal watchers. */
123e66f31c5Sopenharmony_ci  if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) {
124e66f31c5Sopenharmony_ci    r = uv_signal_start(&signal1a, signal1_cb, SIGUSR1);
125e66f31c5Sopenharmony_ci    ASSERT_OK(r);
126e66f31c5Sopenharmony_ci    r = uv_signal_start(&signal1b, signal1_cb, SIGUSR1);
127e66f31c5Sopenharmony_ci    ASSERT_OK(r);
128e66f31c5Sopenharmony_ci  }
129e66f31c5Sopenharmony_ci
130e66f31c5Sopenharmony_ci  if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) {
131e66f31c5Sopenharmony_ci    r = uv_signal_start(&signal2, signal2_cb, SIGUSR2);
132e66f31c5Sopenharmony_ci    ASSERT_OK(r);
133e66f31c5Sopenharmony_ci  }
134e66f31c5Sopenharmony_ci
135e66f31c5Sopenharmony_ci  /* Wait for signals once more. */
136e66f31c5Sopenharmony_ci  uv_sem_post(&sem);
137e66f31c5Sopenharmony_ci
138e66f31c5Sopenharmony_ci  r = uv_run(&loop, UV_RUN_DEFAULT);
139e66f31c5Sopenharmony_ci  ASSERT_OK(r);
140e66f31c5Sopenharmony_ci
141e66f31c5Sopenharmony_ci  /* Close the watchers. */
142e66f31c5Sopenharmony_ci  if (action == ONLY_SIGUSR1 || action == SIGUSR1_AND_SIGUSR2) {
143e66f31c5Sopenharmony_ci    uv_close((uv_handle_t*) &signal1a, NULL);
144e66f31c5Sopenharmony_ci    uv_close((uv_handle_t*) &signal1b, NULL);
145e66f31c5Sopenharmony_ci  }
146e66f31c5Sopenharmony_ci
147e66f31c5Sopenharmony_ci  if (action == ONLY_SIGUSR2 || action == SIGUSR1_AND_SIGUSR2) {
148e66f31c5Sopenharmony_ci    uv_close((uv_handle_t*) &signal2, NULL);
149e66f31c5Sopenharmony_ci  }
150e66f31c5Sopenharmony_ci
151e66f31c5Sopenharmony_ci  /* Wait for the signal watchers to close. */
152e66f31c5Sopenharmony_ci  r = uv_run(&loop, UV_RUN_DEFAULT);
153e66f31c5Sopenharmony_ci  ASSERT_OK(r);
154e66f31c5Sopenharmony_ci
155e66f31c5Sopenharmony_ci  uv_loop_close(&loop);
156e66f31c5Sopenharmony_ci}
157e66f31c5Sopenharmony_ci
158e66f31c5Sopenharmony_ci
159e66f31c5Sopenharmony_cistatic void signal_unexpected_cb(uv_signal_t* handle, int signum) {
160e66f31c5Sopenharmony_ci  ASSERT(0 && "signal_unexpected_cb should never be called");
161e66f31c5Sopenharmony_ci}
162e66f31c5Sopenharmony_ci
163e66f31c5Sopenharmony_ci
164e66f31c5Sopenharmony_cistatic void loop_creating_worker(void* context) {
165e66f31c5Sopenharmony_ci  int done;
166e66f31c5Sopenharmony_ci
167e66f31c5Sopenharmony_ci  (void) context;
168e66f31c5Sopenharmony_ci
169e66f31c5Sopenharmony_ci  do {
170e66f31c5Sopenharmony_ci    uv_loop_t *loop;
171e66f31c5Sopenharmony_ci    uv_signal_t signal;
172e66f31c5Sopenharmony_ci    int r;
173e66f31c5Sopenharmony_ci
174e66f31c5Sopenharmony_ci    loop = malloc(sizeof(*loop));
175e66f31c5Sopenharmony_ci    ASSERT_NOT_NULL(loop);
176e66f31c5Sopenharmony_ci    ASSERT_OK(uv_loop_init(loop));
177e66f31c5Sopenharmony_ci
178e66f31c5Sopenharmony_ci    r = uv_signal_init(loop, &signal);
179e66f31c5Sopenharmony_ci    ASSERT_OK(r);
180e66f31c5Sopenharmony_ci
181e66f31c5Sopenharmony_ci    r = uv_signal_start(&signal, signal_unexpected_cb, SIGTERM);
182e66f31c5Sopenharmony_ci    ASSERT_OK(r);
183e66f31c5Sopenharmony_ci
184e66f31c5Sopenharmony_ci    uv_close((uv_handle_t*) &signal, NULL);
185e66f31c5Sopenharmony_ci
186e66f31c5Sopenharmony_ci    r = uv_run(loop, UV_RUN_DEFAULT);
187e66f31c5Sopenharmony_ci    ASSERT_OK(r);
188e66f31c5Sopenharmony_ci
189e66f31c5Sopenharmony_ci    uv_loop_close(loop);
190e66f31c5Sopenharmony_ci    free(loop);
191e66f31c5Sopenharmony_ci
192e66f31c5Sopenharmony_ci    increment_counter(&loop_creation_counter);
193e66f31c5Sopenharmony_ci
194e66f31c5Sopenharmony_ci    uv_mutex_lock(&lock);
195e66f31c5Sopenharmony_ci    done = stop;
196e66f31c5Sopenharmony_ci    uv_mutex_unlock(&lock);
197e66f31c5Sopenharmony_ci  } while (!done);
198e66f31c5Sopenharmony_ci}
199e66f31c5Sopenharmony_ci
200e66f31c5Sopenharmony_ci
201e66f31c5Sopenharmony_ciTEST_IMPL(signal_multiple_loops) {
202e66f31c5Sopenharmony_ci#if defined(__CYGWIN__) || defined(__MSYS__)
203e66f31c5Sopenharmony_ci  /* FIXME: This test needs more investigation.  Somehow the `read` in
204e66f31c5Sopenharmony_ci     uv__signal_lock fails spuriously with EACCES or even EAGAIN even
205e66f31c5Sopenharmony_ci     though it is supposed to be blocking.  Also the test hangs during
206e66f31c5Sopenharmony_ci     thread setup occasionally.  */
207e66f31c5Sopenharmony_ci  RETURN_SKIP("FIXME: This test needs more investigation on Cygwin");
208e66f31c5Sopenharmony_ci#endif
209e66f31c5Sopenharmony_ci/* TODO(gengjiawen): Fix test on QEMU. */
210e66f31c5Sopenharmony_ci#if defined(__QEMU__)
211e66f31c5Sopenharmony_ci  /* See https://github.com/libuv/libuv/issues/2859 */
212e66f31c5Sopenharmony_ci  RETURN_SKIP("QEMU's signal emulation code is notoriously tricky");
213e66f31c5Sopenharmony_ci#endif
214e66f31c5Sopenharmony_ci#if defined(__ASAN__) || defined(__MSAN__)
215e66f31c5Sopenharmony_ci  /* See https://github.com/libuv/libuv/issues/3956 */
216e66f31c5Sopenharmony_ci  RETURN_SKIP("Test is too slow to run under ASan or MSan");
217e66f31c5Sopenharmony_ci#endif
218e66f31c5Sopenharmony_ci#if defined(__TSAN__)
219e66f31c5Sopenharmony_ci  /* ThreadSanitizer complains - likely legitimately - about data races
220e66f31c5Sopenharmony_ci   * in uv__signal_compare() in src/unix/signal.c but that's pre-existing.
221e66f31c5Sopenharmony_ci   */
222e66f31c5Sopenharmony_ci  RETURN_SKIP("Fix test under ThreadSanitizer");
223e66f31c5Sopenharmony_ci#endif
224e66f31c5Sopenharmony_ci  uv_thread_t loop_creating_threads[NUM_LOOP_CREATING_THREADS];
225e66f31c5Sopenharmony_ci  uv_thread_t signal_handling_threads[NUM_SIGNAL_HANDLING_THREADS];
226e66f31c5Sopenharmony_ci  enum signal_action action;
227e66f31c5Sopenharmony_ci  sigset_t sigset;
228e66f31c5Sopenharmony_ci  int i;
229e66f31c5Sopenharmony_ci  int r;
230e66f31c5Sopenharmony_ci
231e66f31c5Sopenharmony_ci  r = uv_sem_init(&sem, 0);
232e66f31c5Sopenharmony_ci  ASSERT_OK(r);
233e66f31c5Sopenharmony_ci
234e66f31c5Sopenharmony_ci  r = uv_mutex_init(&lock);
235e66f31c5Sopenharmony_ci  ASSERT_OK(r);
236e66f31c5Sopenharmony_ci
237e66f31c5Sopenharmony_ci  /* Create a couple of threads that create a destroy loops continuously. */
238e66f31c5Sopenharmony_ci  for (i = 0; i < NUM_LOOP_CREATING_THREADS; i++) {
239e66f31c5Sopenharmony_ci    r = uv_thread_create(&loop_creating_threads[i],
240e66f31c5Sopenharmony_ci                         loop_creating_worker,
241e66f31c5Sopenharmony_ci                         NULL);
242e66f31c5Sopenharmony_ci    ASSERT_OK(r);
243e66f31c5Sopenharmony_ci  }
244e66f31c5Sopenharmony_ci
245e66f31c5Sopenharmony_ci  /* Create a couple of threads that actually handle signals. */
246e66f31c5Sopenharmony_ci  for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) {
247e66f31c5Sopenharmony_ci    switch (i % 3) {
248e66f31c5Sopenharmony_ci      case 0: action = ONLY_SIGUSR1; break;
249e66f31c5Sopenharmony_ci      case 1: action = ONLY_SIGUSR2; break;
250e66f31c5Sopenharmony_ci      case 2: action = SIGUSR1_AND_SIGUSR2; break;
251e66f31c5Sopenharmony_ci    }
252e66f31c5Sopenharmony_ci
253e66f31c5Sopenharmony_ci    r = uv_thread_create(&signal_handling_threads[i],
254e66f31c5Sopenharmony_ci                         signal_handling_worker,
255e66f31c5Sopenharmony_ci                         (void*) (uintptr_t) action);
256e66f31c5Sopenharmony_ci    ASSERT_OK(r);
257e66f31c5Sopenharmony_ci  }
258e66f31c5Sopenharmony_ci
259e66f31c5Sopenharmony_ci  /* Wait until all threads have started and set up their signal watchers. */
260e66f31c5Sopenharmony_ci  for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++)
261e66f31c5Sopenharmony_ci    uv_sem_wait(&sem);
262e66f31c5Sopenharmony_ci
263e66f31c5Sopenharmony_ci  r = kill(getpid(), SIGUSR1);
264e66f31c5Sopenharmony_ci  ASSERT_OK(r);
265e66f31c5Sopenharmony_ci  r = kill(getpid(), SIGUSR2);
266e66f31c5Sopenharmony_ci  ASSERT_OK(r);
267e66f31c5Sopenharmony_ci
268e66f31c5Sopenharmony_ci  /* Wait for all threads to handle these signals. */
269e66f31c5Sopenharmony_ci  for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++)
270e66f31c5Sopenharmony_ci    uv_sem_wait(&sem);
271e66f31c5Sopenharmony_ci
272e66f31c5Sopenharmony_ci  /* Block all signals to this thread, so we are sure that from here the signal
273e66f31c5Sopenharmony_ci   * handler runs in another thread. This is more likely to catch thread and
274e66f31c5Sopenharmony_ci   * signal safety issues if there are any.
275e66f31c5Sopenharmony_ci   */
276e66f31c5Sopenharmony_ci  sigfillset(&sigset);
277e66f31c5Sopenharmony_ci  pthread_sigmask(SIG_SETMASK, &sigset, NULL);
278e66f31c5Sopenharmony_ci
279e66f31c5Sopenharmony_ci  r = kill(getpid(), SIGUSR1);
280e66f31c5Sopenharmony_ci  ASSERT_OK(r);
281e66f31c5Sopenharmony_ci  r = kill(getpid(), SIGUSR2);
282e66f31c5Sopenharmony_ci  ASSERT_OK(r);
283e66f31c5Sopenharmony_ci
284e66f31c5Sopenharmony_ci  /* Wait for all signal handling threads to exit. */
285e66f31c5Sopenharmony_ci  for (i = 0; i < NUM_SIGNAL_HANDLING_THREADS; i++) {
286e66f31c5Sopenharmony_ci    r = uv_thread_join(&signal_handling_threads[i]);
287e66f31c5Sopenharmony_ci    ASSERT_OK(r);
288e66f31c5Sopenharmony_ci  }
289e66f31c5Sopenharmony_ci
290e66f31c5Sopenharmony_ci  /* Tell all loop creating threads to stop. */
291e66f31c5Sopenharmony_ci  uv_mutex_lock(&lock);
292e66f31c5Sopenharmony_ci  stop = 1;
293e66f31c5Sopenharmony_ci  uv_mutex_unlock(&lock);
294e66f31c5Sopenharmony_ci
295e66f31c5Sopenharmony_ci  /* Wait for all loop creating threads to exit. */
296e66f31c5Sopenharmony_ci  for (i = 0; i < NUM_LOOP_CREATING_THREADS; i++) {
297e66f31c5Sopenharmony_ci    r = uv_thread_join(&loop_creating_threads[i]);
298e66f31c5Sopenharmony_ci    ASSERT_OK(r);
299e66f31c5Sopenharmony_ci  }
300e66f31c5Sopenharmony_ci
301e66f31c5Sopenharmony_ci  uv_sem_destroy(&sem);
302e66f31c5Sopenharmony_ci  printf("signal1_cb calls: %d\n", signal1_cb_counter);
303e66f31c5Sopenharmony_ci  printf("signal2_cb calls: %d\n", signal2_cb_counter);
304e66f31c5Sopenharmony_ci  printf("loops created and destroyed: %d\n", loop_creation_counter);
305e66f31c5Sopenharmony_ci
306e66f31c5Sopenharmony_ci  /* The division by three reflects the fact that we spawn three different
307e66f31c5Sopenharmony_ci   * thread groups of (NUM_SIGNAL_HANDLING_THREADS / 3) threads each.
308e66f31c5Sopenharmony_ci   */
309e66f31c5Sopenharmony_ci  ASSERT_EQ(signal1_cb_counter, 8 * (NUM_SIGNAL_HANDLING_THREADS / 3));
310e66f31c5Sopenharmony_ci  ASSERT_EQ(signal2_cb_counter, 4 * (NUM_SIGNAL_HANDLING_THREADS / 3));
311e66f31c5Sopenharmony_ci
312e66f31c5Sopenharmony_ci  /* We don't know exactly how much loops will be created and destroyed, but at
313e66f31c5Sopenharmony_ci   * least there should be 1 for every loop creating thread.
314e66f31c5Sopenharmony_ci   */
315e66f31c5Sopenharmony_ci  ASSERT_GE(loop_creation_counter, NUM_LOOP_CREATING_THREADS);
316e66f31c5Sopenharmony_ci
317e66f31c5Sopenharmony_ci  MAKE_VALGRIND_HAPPY(uv_default_loop());
318e66f31c5Sopenharmony_ci  return 0;
319e66f31c5Sopenharmony_ci}
320e66f31c5Sopenharmony_ci
321e66f31c5Sopenharmony_ci#else
322e66f31c5Sopenharmony_ci
323e66f31c5Sopenharmony_citypedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */
324e66f31c5Sopenharmony_ci
325e66f31c5Sopenharmony_ci#endif /* !_WIN32 */
326