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
150
151
152
153
154
155
156
157
158
|
#include <nnwt/common.h>
#include <nnwt/thread.h>
#include <nnwt/timer.h>
#include <nnwt/signal.h>
#include <nnwt/time.h>
//#define TEST_WAIT_ON_SIGNAL
static s32 s_quit = 0;
static struct nn_event_loop s_loop;
#ifndef TEST_WAIT_ON_SIGNAL
static struct nn_timer s_timer;
#endif
static struct nn_signal s_signal;
static struct nn_cond s_cond;
static struct nn_mutex s_mutex;
#ifndef TEST_WAIT_ON_SIGNAL
static bool flip = false;
static bool any_fail = false;
static bool last_was_timeout = false;
#endif
static f64 last_tick;
static u64 iter = 0;
#define DELAY 0.002
#ifndef TEST_WAIT_ON_SIGNAL
static void timer_callback(void *userdata, struct nn_timer *timer)
{
(void)userdata;
(void)timer;
if (!flip) {
al_printf("*******FAIL*******\n");
any_fail = true;
} else if (!any_fail && last_was_timeout) {
// Calling timer_stop() here could avoid this but it should
// realistically not happen at nearly any above 0 delay.
al_assert(false && "Unexpected repeat timeout, signal thread deadlocked or couldn't keep up?");
}
al_printf("Timeout iter #%llu (%.5f)\n", iter, nn_get_tick() - last_tick);
last_was_timeout = true;
}
#endif
static void signal_callback(void *userdata)
{
(void)userdata;
f64 tick = nn_get_tick();
#ifndef TEST_WAIT_ON_SIGNAL
if (flip) {
if (last_was_timeout) {
last_was_timeout = false;
} else {
al_printf("Stopped iter #%llu (%.5f)\n", iter, tick - last_tick);
}
// The point of this test is to call timer_stop() right before it was set
// to fire. So we only care about the case where signal_callback() runs first.
// To do that we have to keep it close though, meaning they will trade back and forth.
nn_timer_stop(&s_timer);
// Now wait for ~DELAY seconds and if the timer still fires after being
// stopped, a fail case will trigger.
} else {
iter++;
nn_timer_again(&s_timer);
}
flip = !flip;
#else
iter++;
al_printf("Signal(#%llu) (%.5f)\n", iter, tick - last_tick);
#endif
nn_mutex_lock(&s_mutex);
nn_cond_signal(&s_cond);
last_tick = tick;
if (s_quit) {
#ifndef TEST_WAIT_ON_SIGNAL
nn_timer_stop(&s_timer);
#endif
nn_signal_stop(&s_signal);
}
nn_mutex_unlock(&s_mutex);
}
static nn_thread_result NNWT_THREADCALL signal_and_wait_thread(void *userdata)
{
(void)userdata;
for (;;) {
// Locking before signal_send() is necessary. Otherwise there would be a race
// between this send being processed and calling cond_signal() on the loop thread
// before we get to cond_wait() in this thread.
nn_mutex_lock(&s_mutex);
nn_signal_send(&s_signal);
if (s_quit) break;
nn_cond_wait(&s_cond, &s_mutex);
f64 tick = last_tick;
nn_mutex_unlock(&s_mutex);
#ifndef TEST_WAIT_ON_SIGNAL
nn_thread_sleep(MAX((tick + DELAY) - nn_get_tick(), 0.0));
#else
(void)tick;
#endif
}
nn_mutex_unlock(&s_mutex);
return 0;
}
static void sigint_handler(s32 signum)
{
(void)signum;
nn_mutex_lock(&s_mutex);
if (s_quit) {
al_printf("If the console output was frozen (deadlock), test failed.\n");
exit(128 + SIGINT);
}
s_quit = 1;
al_printf("...^C again to force exit.\n");
nn_mutex_unlock(&s_mutex);
}
s32 main(void)
{
if (!nn_common_init(NULL)) return EXIT_FAILURE;
signal(SIGINT, sigint_handler);
nn_event_loop_init(&s_loop);
nn_signal_init(&s_signal, &s_loop, signal_callback, NULL);
nn_signal_start(&s_signal);
#ifndef TEST_WAIT_ON_SIGNAL
nn_timer_init(&s_timer, &s_loop, timer_callback, NULL);
nn_timer_set_repeat(&s_timer, DELAY);
#endif
last_tick = nn_get_tick();
nn_mutex_init(&s_mutex);
nn_cond_init(&s_cond);
struct nn_thread thread;
nn_thread_create(&thread, signal_and_wait_thread, NULL);
nn_event_loop_run(&s_loop);
nn_event_loop_destroy(&s_loop);
nn_mutex_destroy(&s_mutex);
nn_cond_destroy(&s_cond);
#ifndef TEST_WAIT_ON_SIGNAL
al_printf(any_fail ? "Test failed.\n" : "Test passed.\n");
#else
al_printf("Test passed.\n");
#endif
nn_common_close();
return EXIT_SUCCESS;
}
|