]> git.ipfire.org Git - thirdparty/glibc.git/blame - test-skeleton.c
malloc: Manual part of conversion to __libc_lock
[thirdparty/glibc.git] / test-skeleton.c
CommitLineData
80a18298 1/* Skeleton for test programs.
f7a9f785 2 Copyright (C) 1998-2016 Free Software Foundation, Inc.
41bdb6e2 3 This file is part of the GNU C Library.
80a18298
UD
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1998.
5
6 The GNU C Library is free software; you can redistribute it and/or
41bdb6e2
AJ
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
80a18298
UD
10
11 The GNU C Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41bdb6e2 14 Lesser General Public License for more details.
80a18298 15
41bdb6e2 16 You should have received a copy of the GNU Lesser General Public
59ba27a6
PE
17 License along with the GNU C Library; if not, see
18 <http://www.gnu.org/licenses/>. */
80a18298 19
d7a05d07 20#include <assert.h>
e1d8e1b7 21#include <errno.h>
c5bb8e23 22#include <fcntl.h>
80a18298 23#include <getopt.h>
854278df 24#include <malloc.h>
c5bb8e23 25#include <paths.h>
b9337b6a 26#include <search.h>
80a18298
UD
27#include <signal.h>
28#include <stdio.h>
29#include <stdlib.h>
4380ef5e 30#include <string.h>
80a18298
UD
31#include <unistd.h>
32#include <sys/resource.h>
33#include <sys/wait.h>
63b11dd1 34#include <sys/param.h>
fa4a36fd 35#include <time.h>
85f7554c 36#include <stdarg.h>
80a18298
UD
37
38/* The test function is normally called `do_test' and it is called
39 with argc and argv as the arguments. We nevertheless provide the
40 possibility to overwrite this name. */
41#ifndef TEST_FUNCTION
42# define TEST_FUNCTION do_test (argc, argv)
43#endif
44
63b11dd1
RM
45#ifndef TEST_DATA_LIMIT
46# define TEST_DATA_LIMIT (64 << 20) /* Data limit (bytes) to run with. */
47#endif
80a18298 48
b0b88abc 49#ifndef TIMEOUT
a28605b2
MF
50 /* Default timeout is twenty seconds. Tests should normally complete faster
51 than this, but if they don't, that's abnormal (a bug) anyways. */
52# define TIMEOUT 20
b0b88abc
RM
53#endif
54
80a18298
UD
55#define OPT_DIRECT 1000
56#define OPT_TESTDIR 1001
57
58static struct option options[] =
59{
60#ifdef CMDLINE_OPTIONS
61 CMDLINE_OPTIONS
62#endif
63 { "direct", no_argument, NULL, OPT_DIRECT },
64 { "test-dir", required_argument, NULL, OPT_TESTDIR },
65 { NULL, 0, NULL, 0 }
66};
67
68/* PID of the test itself. */
608c5afd 69static pid_t pid;
80a18298
UD
70
71/* Directory to place temporary files in. */
72static const char *test_dir;
73
530bb2bf
PP
74static void
75oom_error (const char *fn, size_t size)
76{
77 printf ("%s: unable to allocate %zu bytes: %m\n", fn, size);
78 exit (1);
79}
80
81/* Allocate N bytes of memory dynamically, with error checking. */
9d52cb01 82__attribute__ ((unused))
530bb2bf 83static void *
530bb2bf
PP
84xmalloc (size_t n)
85{
86 void *p;
87
88 p = malloc (n);
89 if (p == NULL)
90 oom_error ("malloc", n);
91 return p;
92}
93
94/* Allocate memory for N elements of S bytes, with error checking. */
9d52cb01 95__attribute__ ((unused))
530bb2bf 96static void *
530bb2bf
PP
97xcalloc (size_t n, size_t s)
98{
99 void *p;
100
101 p = calloc (n, s);
102 if (p == NULL)
103 oom_error ("calloc", n * s);
104 return p;
105}
106
107/* Change the size of an allocated block of memory P to N bytes,
108 with error checking. */
9d52cb01 109__attribute__ ((unused))
530bb2bf 110static void *
530bb2bf
PP
111xrealloc (void *p, size_t n)
112{
64ba1731
FW
113 void *result = realloc (p, n);
114 if (result == NULL && (n > 0 || p == NULL))
530bb2bf 115 oom_error ("realloc", n);
64ba1731 116 return result;
530bb2bf
PP
117}
118
85f7554c
FW
119/* Call asprintf with error checking. */
120__attribute__ ((always_inline, format (printf, 1, 2)))
121static __inline__ char *
122xasprintf (const char *format, ...)
123{
124 char *result;
125 if (asprintf (&result, format, __builtin_va_arg_pack ()) < 0)
126 {
127 printf ("error: asprintf: %m\n");
128 exit (1);
129 }
130 return result;
131}
132
14699b6e
FW
133/* Write a message to standard output. Can be used in signal
134 handlers. */
135static void
136__attribute__ ((unused))
137write_message (const char *message)
138{
139 ssize_t unused __attribute__ ((unused));
140 unused = write (STDOUT_FILENO, message, strlen (message));
141}
142
b9337b6a 143/* List of temporary files. */
51eecc4a 144struct temp_name_list
b9337b6a
UD
145{
146 struct qelem q;
cc8dcf96 147 char *name;
51eecc4a 148} *temp_name_list;
b9337b6a
UD
149
150/* Add temporary files in list. */
9c0592ab 151static void
5cd6f8f7 152__attribute__ ((unused))
b9337b6a
UD
153add_temp_file (const char *name)
154{
51eecc4a 155 struct temp_name_list *newp
530bb2bf 156 = (struct temp_name_list *) xcalloc (sizeof (*newp), 1);
cc8dcf96 157 char *newname = strdup (name);
530bb2bf 158 if (newname != NULL)
b9337b6a 159 {
cc8dcf96 160 newp->name = newname;
51eecc4a
AJ
161 if (temp_name_list == NULL)
162 temp_name_list = (struct temp_name_list *) &newp->q;
b9337b6a 163 else
51eecc4a 164 insque (newp, temp_name_list);
b9337b6a 165 }
cc8dcf96
FW
166 else
167 free (newp);
b9337b6a
UD
168}
169
170/* Delete all temporary files. */
9c0592ab 171static void
b9337b6a
UD
172delete_temp_files (void)
173{
51eecc4a 174 while (temp_name_list != NULL)
b9337b6a 175 {
51eecc4a 176 remove (temp_name_list->name);
cc8dcf96
FW
177 free (temp_name_list->name);
178
179 struct temp_name_list *next
180 = (struct temp_name_list *) temp_name_list->q.q_forw;
181 free (temp_name_list);
182 temp_name_list = next;
b9337b6a
UD
183 }
184}
185
cc8dcf96
FW
186/* Create a temporary file. Return the opened file descriptor on
187 success, or -1 on failure. Write the file name to *FILENAME if
188 FILENAME is not NULL. In this case, the caller is expected to free
189 *FILENAME. */
10e62564
UD
190static int
191__attribute__ ((unused))
192create_temp_file (const char *base, char **filename)
193{
194 char *fname;
195 int fd;
196
530bb2bf
PP
197 fname = (char *) xmalloc (strlen (test_dir) + 1 + strlen (base)
198 + sizeof ("XXXXXX"));
10e62564
UD
199 strcpy (stpcpy (stpcpy (stpcpy (fname, test_dir), "/"), base), "XXXXXX");
200
201 fd = mkstemp (fname);
202 if (fd == -1)
203 {
204 printf ("cannot open temporary file '%s': %m\n", fname);
205 free (fname);
206 return -1;
207 }
208
209 add_temp_file (fname);
210 if (filename != NULL)
211 *filename = fname;
cc8dcf96
FW
212 else
213 free (fname);
10e62564
UD
214
215 return fd;
216}
217
80a18298 218/* Timeout handler. We kill the child and exit with an error. */
9c0592ab 219static void
8c0b7170 220__attribute__ ((noreturn))
85fda49b 221signal_handler (int sig __attribute__ ((unused)))
80a18298
UD
222{
223 int killed;
b79e3737 224 int status;
80a18298 225
d7a05d07
MR
226 assert (pid > 1);
227 /* Kill the whole process group. */
228 kill (-pid, SIGKILL);
229 /* In case setpgid failed in the child, kill it individually too. */
80a18298
UD
230 kill (pid, SIGKILL);
231
232 /* Wait for it to terminate. */
6d0e6e84
UD
233 int i;
234 for (i = 0; i < 5; ++i)
235 {
236 killed = waitpid (pid, &status, WNOHANG|WUNTRACED);
237 if (killed != 0)
238 break;
239
240 /* Delay, give the system time to process the kill. If the
241 nanosleep() call return prematurely, all the better. We
242 won't restart it since this probably means the child process
243 finally died. */
33192609
UD
244 struct timespec ts;
245 ts.tv_sec = 0;
246 ts.tv_nsec = 100000000;
6d0e6e84
UD
247 nanosleep (&ts, NULL);
248 }
80a18298
UD
249 if (killed != 0 && killed != pid)
250 {
c5c13355 251 printf ("Failed to kill test process: %m\n");
80a18298
UD
252 exit (1);
253 }
254
255#ifdef CLEANUP_HANDLER
256 CLEANUP_HANDLER;
257#endif
258
85fda49b
UD
259 if (sig == SIGINT)
260 {
261 signal (sig, SIG_DFL);
262 raise (sig);
263 }
264
4614167a
UD
265 /* If we expected this signal: good! */
266#ifdef EXPECTED_SIGNAL
267 if (EXPECTED_SIGNAL == SIGALRM)
268 exit (0);
269#endif
270
6d0e6e84 271 if (killed == 0 || (WIFSIGNALED (status) && WTERMSIG (status) == SIGKILL))
c5c13355 272 puts ("Timed out: killed the child process");
b79e3737 273 else if (WIFSTOPPED (status))
c5c13355
WN
274 printf ("Timed out: the child process was %s\n",
275 strsignal (WSTOPSIG (status)));
b79e3737 276 else if (WIFSIGNALED (status))
c5c13355
WN
277 printf ("Timed out: the child process got signal %s\n",
278 strsignal (WTERMSIG (status)));
b79e3737 279 else
c5c13355
WN
280 printf ("Timed out: killed the child process but it exited %d\n",
281 WEXITSTATUS (status));
80a18298
UD
282
283 /* Exit with an error. */
284 exit (1);
285}
286
02242448
TMQMF
287/* Avoid all the buffer overflow messages on stderr. */
288static void
289__attribute__ ((unused))
290ignore_stderr (void)
291{
292 int fd = open (_PATH_DEVNULL, O_WRONLY);
293 if (fd == -1)
294 close (STDERR_FILENO);
295 else
296 {
297 dup2 (fd, STDERR_FILENO);
298 close (fd);
299 }
300 setenv ("LIBC_FATAL_STDERR_", "1", 1);
301}
302
c5bb8e23
MF
303/* Set fortification error handler. Used when tests want to verify that bad
304 code is caught by the library. */
305static void
306__attribute__ ((unused))
307set_fortify_handler (void (*handler) (int sig))
308{
309 struct sigaction sa;
310
311 sa.sa_handler = handler;
312 sa.sa_flags = 0;
313 sigemptyset (&sa.sa_mask);
314
315 sigaction (SIGABRT, &sa, NULL);
02242448 316 ignore_stderr ();
c5bb8e23
MF
317}
318
496405af
MF
319/* Show people how to run the program. */
320static void
321usage (void)
322{
323 size_t i;
324
325 printf ("Usage: %s [options]\n"
326 "\n"
327 "Environment Variables:\n"
328 " TIMEOUTFACTOR An integer used to scale the timeout\n"
329 " TMPDIR Where to place temporary files\n"
330 "\n",
331 program_invocation_short_name);
332 printf ("Options:\n");
333 for (i = 0; options[i].name; ++i)
334 {
335 int indent;
336
337 indent = printf (" --%s", options[i].name);
338 if (options[i].has_arg == required_argument)
339 indent += printf (" <arg>");
340 printf ("%*s", 25 - indent, "");
341 switch (options[i].val)
342 {
343 case OPT_DIRECT:
344 printf ("Run the test directly (instead of forking & monitoring)");
345 break;
346 case OPT_TESTDIR:
347 printf ("Override the TMPDIR env var");
348 break;
349 }
350 printf ("\n");
351 }
352}
353
80a18298
UD
354/* We provide the entry point here. */
355int
356main (int argc, char *argv[])
357{
358 int direct = 0; /* Directly call the test function? */
359 int status;
360 int opt;
dc391246 361 unsigned int timeoutfactor = 1;
608c5afd 362 pid_t termpid;
80a18298 363
f690b569 364#ifndef TEST_NO_MALLOPT
854278df
UD
365 /* Make uses of freed and uninitialized memory known. */
366 mallopt (M_PERTURB, 42);
f690b569 367#endif
854278df 368
e7c036b3
UD
369#ifdef STDOUT_UNBUFFERED
370 setbuf (stdout, NULL);
371#endif
372
6f1acb30 373 while ((opt = getopt_long (argc, argv, "+", options, NULL)) != -1)
80a18298
UD
374 switch (opt)
375 {
376 case '?':
496405af 377 usage ();
80a18298
UD
378 exit (1);
379 case OPT_DIRECT:
380 direct = 1;
381 break;
382 case OPT_TESTDIR:
383 test_dir = optarg;
384 break;
385#ifdef CMDLINE_PROCESS
386 CMDLINE_PROCESS
387#endif
388 }
389
dc391246
UD
390 /* If set, read the test TIMEOUTFACTOR value from the environment.
391 This value is used to scale the default test timeout values. */
392 char *envstr_timeoutfactor = getenv ("TIMEOUTFACTOR");
393 if (envstr_timeoutfactor != NULL)
394 {
395 char *envstr_conv = envstr_timeoutfactor;
396 unsigned long int env_fact;
397
398 env_fact = strtoul (envstr_timeoutfactor, &envstr_conv, 0);
399 if (*envstr_conv == '\0' && envstr_conv != envstr_timeoutfactor)
400 timeoutfactor = MAX (env_fact, 1);
401 }
402
80a18298
UD
403 /* Set TMPDIR to specified test directory. */
404 if (test_dir != NULL)
405 {
406 setenv ("TMPDIR", test_dir, 1);
407
408 if (chdir (test_dir) < 0)
409 {
c5c13355 410 printf ("chdir: %m\n");
80a18298
UD
411 exit (1);
412 }
413 }
b9337b6a
UD
414 else
415 {
416 test_dir = getenv ("TMPDIR");
417 if (test_dir == NULL || test_dir[0] == '\0')
418 test_dir = "/tmp";
419 }
80a18298 420
6b9c2e67
UD
421 /* Make sure we see all message, even those on stdout. */
422 setvbuf (stdout, NULL, _IONBF, 0);
423
b0b88abc 424 /* Make sure temporary files are deleted. */
310b3460
UD
425 atexit (delete_temp_files);
426
726b7b0f 427 /* Correct for the possible parameters. */
55033a44 428 argv[optind - 1] = argv[0];
726b7b0f
UD
429 argv += optind - 1;
430 argc -= optind - 1;
431
310b3460
UD
432 /* Call the initializing function, if one is available. */
433#ifdef PREPARE
434 PREPARE (argc, argv);
435#endif
436
b0b88abc
RM
437 const char *envstr_direct = getenv ("TEST_DIRECT");
438 if (envstr_direct != NULL)
439 {
440 FILE *f = fopen (envstr_direct, "w");
441 if (f == NULL)
442 {
443 printf ("cannot open TEST_DIRECT output file '%s': %m\n",
444 envstr_direct);
445 exit (1);
446 }
447
448 fprintf (f, "timeout=%u\ntimeoutfactor=%u\n", TIMEOUT, timeoutfactor);
449#ifdef EXPECTED_STATUS
450 fprintf (f, "exit=%u\n", EXPECTED_STATUS);
451#endif
452#ifdef EXPECTED_SIGNAL
453 switch (EXPECTED_SIGNAL)
454 {
455 default: abort ();
456# define init_sig(signo, name, text) \
457 case signo: fprintf (f, "signal=%s\n", name); break;
458# include <siglist.h>
459# undef init_sig
460 }
461#endif
462
463 if (temp_name_list != NULL)
464 {
330fadfc 465 struct temp_name_list *n;
b0b88abc 466 fprintf (f, "temp_files=(\n");
330fadfc 467 for (n = temp_name_list;
b0b88abc
RM
468 n != NULL;
469 n = (struct temp_name_list *) n->q.q_forw)
470 fprintf (f, " '%s'\n", n->name);
471 fprintf (f, ")\n");
472 }
473
474 fclose (f);
475 direct = 1;
476 }
477
80a18298
UD
478 /* If we are not expected to fork run the function immediately. */
479 if (direct)
480 return TEST_FUNCTION;
481
482 /* Set up the test environment:
483 - prevent core dumps
484 - set up the timer
485 - fork and execute the function. */
486
487 pid = fork ();
488 if (pid == 0)
489 {
490 /* This is the child. */
491#ifdef RLIMIT_CORE
492 /* Try to avoid dumping core. */
493 struct rlimit core_limit;
494 core_limit.rlim_cur = 0;
495 core_limit.rlim_max = 0;
496 setrlimit (RLIMIT_CORE, &core_limit);
497#endif
63b11dd1 498
b79e3737
RM
499 /* We put the test process in its own pgrp so that if it bogusly
500 generates any job control signals, they won't hit the whole build. */
d7a05d07
MR
501 if (setpgid (0, 0) != 0)
502 printf ("Failed to set the process group ID: %m\n");
b79e3737 503
80a18298
UD
504 /* Execute the test function and exit with the return value. */
505 exit (TEST_FUNCTION);
506 }
507 else if (pid < 0)
508 {
c5c13355 509 printf ("Cannot fork test program: %m\n");
80a18298
UD
510 exit (1);
511 }
512
513 /* Set timeout. */
85fda49b 514 signal (SIGALRM, signal_handler);
dc391246 515 alarm (TIMEOUT * timeoutfactor);
80a18298 516
85fda49b
UD
517 /* Make sure we clean up if the wrapper gets interrupted. */
518 signal (SIGINT, signal_handler);
519
80a18298 520 /* Wait for the regular termination. */
53854476 521 termpid = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
608c5afd 522 if (termpid == -1)
80a18298 523 {
608c5afd
UD
524 printf ("Waiting for test program failed: %m\n");
525 exit (1);
526 }
527 if (termpid != pid)
528 {
529 printf ("Oops, wrong test program terminated: expected %ld, got %ld\n",
530 (long int) pid, (long int) termpid);
80a18298
UD
531 exit (1);
532 }
533
fffa1cf8
LH
534 /* Process terminated normaly without timeout etc. */
535 if (WIFEXITED (status))
80a18298 536 {
ede0f73a 537#ifndef EXPECTED_STATUS
fffa1cf8
LH
538# ifndef EXPECTED_SIGNAL
539 /* Simply exit with the return value of the test. */
540 return WEXITSTATUS (status);
541# else
542 printf ("Expected signal '%s' from child, got none\n",
543 strsignal (EXPECTED_SIGNAL));
544 exit (1);
545# endif
ede0f73a 546#else
fffa1cf8
LH
547 if (WEXITSTATUS (status) != EXPECTED_STATUS)
548 {
549 printf ("Expected status %d, got %d\n",
550 EXPECTED_STATUS, WEXITSTATUS (status));
551 exit (1);
552 }
553
554 return 0;
555#endif
556 }
557 /* Process was killed by timer or other signal. */
558 else
ede0f73a 559 {
fffa1cf8
LH
560#ifndef EXPECTED_SIGNAL
561 printf ("Didn't expect signal from child: got `%s'\n",
562 strsignal (WTERMSIG (status)));
ede0f73a 563 exit (1);
fffa1cf8
LH
564#else
565 if (WTERMSIG (status) != EXPECTED_SIGNAL)
566 {
567 printf ("Incorrect signal from child: got `%s', need `%s'\n",
568 strsignal (WTERMSIG (status)),
569 strsignal (EXPECTED_SIGNAL));
570 exit (1);
571 }
ede0f73a 572
fffa1cf8 573 return 0;
ede0f73a 574#endif
fffa1cf8 575 }
80a18298 576}
7e625f7e
FW
577
578/* The following functionality is only available if <pthread.h> was
579 included before this file. */
580#ifdef _PTHREAD_H
581
582/* Call pthread_sigmask with error checking. */
583static void
584xpthread_sigmask (int how, const sigset_t *set, sigset_t *oldset)
585{
586 if (pthread_sigmask (how, set, oldset) != 0)
587 {
588 write_message ("error: pthread_setmask failed\n");
589 _exit (1);
590 }
591}
592
593/* Call pthread_mutex_lock with error checking. */
594__attribute__ ((unused))
595static void
596xpthread_mutex_lock (pthread_mutex_t *mutex)
597{
598 int ret = pthread_mutex_lock (mutex);
599 if (ret != 0)
600 {
601 errno = ret;
602 printf ("error: pthread_mutex_lock: %m\n");
603 exit (1);
604 }
605}
606
607/* Call pthread_spin_lock with error checking. */
608__attribute__ ((unused))
609static void
610xpthread_spin_lock (pthread_spinlock_t *lock)
611{
612 int ret = pthread_spin_lock (lock);
613 if (ret != 0)
614 {
615 errno = ret;
616 printf ("error: pthread_spin_lock: %m\n");
617 exit (1);
618 }
619}
620
621/* Call pthread_cond_wait with error checking. */
622__attribute__ ((unused))
623static void
624xpthread_cond_wait (pthread_cond_t * cond,
625 pthread_mutex_t * mutex)
626{
627 int ret = pthread_cond_wait (cond, mutex);
628 if (ret != 0)
629 {
630 errno = ret;
631 printf ("error: pthread_cond_wait: %m\n");
632 exit (1);
633 }
634}
635
636/* Call pthread_barrier_wait with error checking. */
637__attribute__ ((unused))
638static int
639xpthread_barrier_wait (pthread_barrier_t *barrier)
640{
641 int ret = pthread_barrier_wait (barrier);
642 if (ret != 0 && ret != PTHREAD_BARRIER_SERIAL_THREAD)
643 {
644 errno = ret;
645 printf ("error: pthread_barrier_wait: %m\n");
646 exit (1);
647 }
648 return ret;
649}
650
651/* Call pthread_create with error checking. */
652static pthread_t
653xpthread_create (pthread_attr_t *attr,
654 void *(*thread_func) (void *), void *closure)
655{
656 pthread_t thr;
657 int ret = pthread_create (&thr, attr, thread_func, closure);
658 if (ret != 0)
659 {
660 errno = ret;
661 printf ("error: pthread_create: %m\n");
662 exit (1);
663 }
664 return thr;
665}
666
667/* Call pthread_detach with error checking. */
668static void
669xpthread_detach (pthread_t thr)
670{
671 int ret = pthread_detach (thr);
672 if (ret != 0)
673 {
674 errno = ret;
675 printf ("error: pthread_detach: %m\n");
676 exit (1);
677 }
678}
679
680/* Call pthread_join with error checking. */
681__attribute__ ((unused))
682static void *
683xpthread_join (pthread_t thr)
684{
685 void *result;
686 int ret = pthread_join (thr, &result);
687 if (ret != 0)
688 {
689 errno = ret;
690 printf ("error: pthread_join: %m\n");
691 exit (1);
692 }
693 return result;
694}
695
696/* Used to implement the delayed_exit function defined below. */
697static void *
698delayed_exit_thread (void *seconds_as_ptr)
699{
700 int seconds = (uintptr_t) seconds_as_ptr;
701 struct timespec delay = { seconds, 0 };
21e79af4 702 struct timespec remaining = { 0 };
7e625f7e
FW
703 if (nanosleep (&delay, &remaining) != 0)
704 {
705 printf ("error: nanosleep: %m\n");
706 _exit (1);
707 }
708 /* Exit the process sucessfully. */
709 exit (0);
710 return NULL;
711}
712
713/* Exit (with status 0) after SECONDS have elapsed, from a helper
714 thread. The process is terminated with the exit function, so
715 atexit handlers are executed. */
716__attribute__ ((unused))
717static void
718delayed_exit (int seconds)
719{
720 /* Create the new thread with all signals blocked. */
721 sigset_t all_blocked;
722 sigfillset (&all_blocked);
723 sigset_t old_set;
724 xpthread_sigmask (SIG_SETMASK, &all_blocked, &old_set);
725 /* Create a detached thread. */
726 pthread_t thr = xpthread_create
727 (NULL, delayed_exit_thread, (void *) (uintptr_t) seconds);
728 xpthread_detach (thr);
729 /* Restore the original signal mask. */
730 xpthread_sigmask (SIG_SETMASK, &old_set, NULL);
731}
732
733#endif /* _PTHREAD_H */