aboutsummaryrefslogtreecommitdiff
path: root/tests/tcg/multiarch/signals.c
blob: 998c8fdefd6f1720a4393a8886c7fd2406b88711 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
 * linux-user signal handling tests.
 *
 * Copyright (c) 2021 Linaro Ltd
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>

static void error1(const char *filename, int line, const char *fmt, ...)
{
    va_list ap;
    va_start(ap, fmt);
    fprintf(stderr, "%s:%d: ", filename, line);
    vfprintf(stderr, fmt, ap);
    fprintf(stderr, "\n");
    va_end(ap);
    exit(1);
}

static int __chk_error(const char *filename, int line, int ret)
{
    if (ret < 0) {
        error1(filename, line, "%m (ret=%d, errno=%d/%s)",
               ret, errno, strerror(errno));
    }
    return ret;
}

#define error(fmt, ...) error1(__FILE__, __LINE__, fmt, ## __VA_ARGS__)

#define chk_error(ret) __chk_error(__FILE__, __LINE__, (ret))

/*
 * Thread handling
 */
typedef struct ThreadJob ThreadJob;

struct ThreadJob {
    int number;
    int sleep;
    int count;
};

static pthread_t *threads;
static int max_threads = 10;
__thread int signal_count;
int total_signal_count;

static void *background_thread_func(void *arg)
{
    ThreadJob *job = (ThreadJob *) arg;

    printf("thread%d: started\n", job->number);
    while (total_signal_count < job->count) {
        usleep(job->sleep);
    }
    printf("thread%d: saw %d alarms from %d\n", job->number,
           signal_count, total_signal_count);
    return NULL;
}

static void spawn_threads(void)
{
    int i;
    threads = calloc(sizeof(pthread_t), max_threads);

    for (i = 0; i < max_threads; i++) {
        ThreadJob *job = calloc(sizeof(ThreadJob), 1);
        job->number = i;
        job->sleep = i * 1000;
        job->count = i * 100;
        pthread_create(threads + i, NULL, background_thread_func, job);
    }
}

static void close_threads(void)
{
    int i;
    for (i = 0; i < max_threads; i++) {
        pthread_join(threads[i], NULL);
    }
    free(threads);
    threads = NULL;
}

static void sig_alarm(int sig, siginfo_t *info, void *puc)
{
    if (sig != SIGRTMIN) {
        error("unexpected signal");
    }
    signal_count++;
    __atomic_fetch_add(&total_signal_count, 1, __ATOMIC_SEQ_CST);
}

static void test_signals(void)
{
    struct sigaction act;
    struct itimerspec it;
    timer_t tid;
    struct sigevent sev;

    /* Set up SIG handler */
    act.sa_sigaction = sig_alarm;
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;
    chk_error(sigaction(SIGRTMIN, &act, NULL));

    /* Create POSIX timer */
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo = SIGRTMIN;
    sev.sigev_value.sival_ptr = &tid;
    chk_error(timer_create(CLOCK_REALTIME, &sev, &tid));

    it.it_interval.tv_sec = 0;
    it.it_interval.tv_nsec = 1000000;
    it.it_value.tv_sec = 0;
    it.it_value.tv_nsec = 1000000;
    chk_error(timer_settime(tid, 0, &it, NULL));

    spawn_threads();

    do {
        usleep(1000);
    } while (total_signal_count < 2000);

    printf("shutting down after: %d signals\n", total_signal_count);

    close_threads();

    chk_error(timer_delete(tid));
}

int main(int argc, char **argv)
{
    test_signals();
    return 0;
}