#include <err.h>
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void __dead usage(void);
void handler(int);
void *runner(void *);
void __dead
usage(void)
{
fprintf(stderr, "sigpthread [-bSsU] [-k kill] -t threads [-u unblock] "
"[-w waiter]\n"
" -b block signal to make it pending\n"
" -k kill thread to kill, else process\n"
" -S sleep in each thread before suspend\n"
" -s sleep in main before kill\n"
" -t threads number of threads to run\n"
" -U sleep in thread before unblock\n"
" -u unblock thread to unblock, else unblock all\n"
" -w waiter use sigwait in thread\n"
);
exit(1);
}
int blocksignal = 0;
int threadmax, threadunblock = -1, threadwaiter = -1;
int sleepthread, sleepmain, sleepunblock;
sigset_t set, oset;
pthread_t *threads;
volatile sig_atomic_t *signaled;
int
main(int argc, char *argv[])
{
struct sigaction act;
int ch, ret, tnum, threadkill = -1;
long arg;
void *val;
const char *errstr;
while ((ch = getopt(argc, argv, "bk:Sst:Uu:w:")) != -1) {
switch (ch) {
case 'b':
blocksignal = 1;
break;
case 'k':
threadkill = strtonum(optarg, 0, INT_MAX, &errstr);
if (errstr != NULL)
errx(1, "thread to kill is %s: %s",
errstr, optarg);
break;
case 'S':
sleepthread = 1;
break;
case 's':
sleepmain = 1;
break;
case 't':
threadmax = strtonum(optarg, 1, INT_MAX, &errstr);
if (errstr != NULL)
errx(1, "number of threads is %s: %s",
errstr, optarg);
break;
case 'U':
sleepunblock = 1;
break;
case 'u':
threadunblock = strtonum(optarg, 0, INT_MAX, &errstr);
if (errstr != NULL)
errx(1, "thread to unblock is %s: %s",
errstr, optarg);
break;
case 'w':
threadwaiter = strtonum(optarg, 0, INT_MAX, &errstr);
if (errstr != NULL)
errx(1, "thread to wait is %s: %s",
errstr, optarg);
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 0)
errx(1, "more arguments than expected");
if (threadmax == 0)
errx(1, "number of threads required");
if (threadkill >= threadmax)
errx(1, "thread to kill greater than number of threads");
if (threadunblock >= threadmax)
errx(1, "thread to unblock greater than number of threads");
if (threadwaiter >= threadmax)
errx(1, "thread to wait greater than number of threads");
if (!blocksignal && threadunblock >= 0)
errx(1, "do not unblock thread without blocked signals");
if (!blocksignal && threadwaiter >= 0)
errx(1, "do not wait in thread without blocked signals");
if (threadunblock >= 0 && threadwaiter >= 0)
errx(1, "do not unblock and wait together");
if (sleepunblock && threadwaiter >= 0)
errx(1, "do not sleep before unblock and wait together");
alarm(10);
if (sigemptyset(&set) == -1)
err(1, "sigemptyset");
if (sigaddset(&set, SIGUSR1) == -1)
err(1, "sigaddset");
if (blocksignal) {
if (sigaddset(&set, SIGUSR2) == -1)
err(1, "sigaddset");
}
if (sigprocmask(SIG_BLOCK, &set, &oset) == -1)
err(1, "sigprocmask");
if (sigaddset(&oset, SIGUSR2) == -1)
err(1, "sigaddset");
if (sigemptyset(&set) == -1)
err(1, "sigemptyset");
if (sigaddset(&set, SIGUSR2) == -1)
err(1, "sigaddset");
memset(&act, 0, sizeof(act));
act.sa_handler = handler;
if (sigaction(SIGUSR1, &act, NULL) == -1)
err(1, "sigaction SIGUSR1");
if (sigaction(SIGUSR2, &act, NULL) == -1)
err(1, "sigaction SIGUSR2");
signaled = calloc(threadmax, sizeof(*signaled));
if (signaled == NULL)
err(1, "calloc signaled");
threads = calloc(threadmax, sizeof(*threads));
if (threads == NULL)
err(1, "calloc threads");
for (tnum = 1; tnum < threadmax; tnum++) {
arg = tnum;
errno = pthread_create(&threads[tnum], NULL, runner,
(void *)arg);
if (errno)
err(1, "pthread_create %d", tnum);
}
threads[0] = pthread_self();
if (sleepmain)
sleep(1);
if (threadkill < 0) {
if (kill(getpid(), SIGUSR2) == -1)
err(1, "kill SIGUSR2");
} else {
errno = pthread_kill(threads[threadkill], SIGUSR2);
if (errno)
err(1, "pthread_kill %d SIGUSR2", tnum);
}
for (tnum = 0; tnum < threadmax; tnum++) {
errno = pthread_kill(threads[tnum], SIGUSR1);
if (errno)
err(1, "pthread_kill %d SIGUSR1", tnum);
}
val = runner(0);
ret = (int)val;
for (tnum = 1; tnum < threadmax; tnum++) {
errno = pthread_join(threads[tnum], &val);
if (errno)
err(1, "pthread_join %d", tnum);
ret = (int)val;
if (ret)
errx(1, "pthread %d returned %d", tnum, ret);
}
free(threads);
for (tnum = 0; tnum < threadmax; tnum++) {
int i;
for (i = 0; i < signaled[tnum]; i++)
printf("signal %d\n", tnum);
}
free((void *)signaled);
return 0;
}
void
handler(int sig)
{
pthread_t tid;
int tnum;
tid = pthread_self();
for (tnum = 0; tnum < threadmax; tnum++) {
if (tid == threads[tnum])
break;
}
switch (sig) {
case SIGUSR1:
break;
case SIGUSR2:
signaled[tnum]++;
break;
default:
errx(1, "unexpected signal %d thread %d", sig, tnum);
}
}
void *
runner(void *arg)
{
int tnum = (int)arg;
if (sleepthread)
sleep(1);
if (tnum == threadwaiter) {
int sig;
if (sigwait(&set, &sig) != 0)
err(1, "sigwait thread %d", tnum);
if (sig != SIGUSR2)
errx(1, "unexpected signal %d thread %d", sig, tnum);
signaled[tnum]++;
}
if (sigsuspend(&oset) != -1 || errno != EINTR)
err(1, "sigsuspend thread %d", tnum);
if ((threadunblock < 0 || tnum == threadunblock) && threadwaiter < 0) {
if (sleepunblock)
sleep(1);
if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) == -1)
err(1, "pthread_sigmask thread %d", tnum);
}
return (void *)0;
}