root/regress/sys/kern/signal/sig-stop2/sig-stop2.c
/*      $OpenBSD: sig-stop2.c,v 1.2 2025/09/16 09:35:39 jsg Exp $       */
/*
 *      Written by Artur Grabowski <art@openbsd.org> 2007 Public Domain.
 *      Written by Claudio Jeker <claudio@openbsd.org> 2024 Public Domain.
 */
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <err.h>
#include <signal.h>
#include <pthread.h>

#define THREAD_COUNT    4

volatile sig_atomic_t tstp_count, cont_count;
pid_t child;

static void
tstp_handler(int sig)
{
        tstp_count++;
        dprintf(STDERR_FILENO, "SIGTSTP\n");
        kill(getpid(), SIGSTOP);
}

static void
cont_handler(int sig)
{
        dprintf(STDERR_FILENO, "SIGCONT\n");
        cont_count++;
}

static void
alrm_handler(int sig)
{
        kill(child, SIGKILL);
        dprintf(STDERR_FILENO, "timeout\n");
        _exit(2);
}


static void *
thread(void *arg)
{
        struct timespec ts = { .tv_sec = 2 };

        while (nanosleep(&ts, &ts) != 0)
                ;

        return NULL;
}

static int
child_main(void)
{
        pthread_t self, pthread[THREAD_COUNT];
        sigset_t set;
        int i, r;

        signal(SIGTSTP, tstp_handler);
        signal(SIGCONT, cont_handler);

        sigemptyset(&set);
        sigaddset(&set, SIGTSTP);
        sigaddset(&set, SIGCONT);

        self = pthread_self();
        for (i = 0; i < THREAD_COUNT; i++) {
                if ((r = pthread_create(&pthread[i], NULL, thread, NULL))) {
                        warnc(r, "could not create thread");
                        pthread[i] = self;
                }
        }

        sigprocmask(SIG_BLOCK, &set, NULL);

        for (i = 0; i < THREAD_COUNT; i++) {
                if (!pthread_equal(pthread[i], self) &&
                    (r = pthread_join(pthread[i], NULL)))
                        warnc(r, "could not join thread");
        }

        printf("#tstp = %d #cont = %d\n", tstp_count, cont_count);

        return !(tstp_count == 1 && cont_count == 1);
}

int
main(int argc, char **argv)
{
        struct timespec ts = { .tv_nsec = 200 * 1000 * 1000 };
        int status;

        switch((child = fork())) {
        case -1:
                err(1, "fork");
        case 0:
                exit(child_main());
        default:
                break;
        }

        signal(SIGALRM, alrm_handler);
        alarm(5);

        nanosleep(&ts, NULL);

        if (kill(child, SIGTSTP) == -1)
                err(1, "kill");

        if (waitpid(child, &status, WCONTINUED|WUNTRACED) <= 0)
                err(1, "waitpid");

        nanosleep(&ts, NULL);

        if (kill(child, SIGCONT) == -1)
                err(1, "kill");

        if (waitpid(child, &status, WCONTINUED|WUNTRACED) <= 0)
                err(1, "waitpid");

        nanosleep(&ts, NULL);

        if (waitpid(child, &status, 0) <= 0)
                err(1, "waitpid");

        if (!WIFEXITED(status))
                err(1, "bad status: %d", status);

        return WEXITSTATUS(status);
}