Skip to content

Commit 56d5b48

Browse files
brendandahlinolen
andauthored
[test] Convert select and poll tests to using clock_gettime (#26401)
Prefer `clock_gettime` with `CLOCK_MONOTONIC` instead of `gettimeofday` whose implementation isn't guaranteed to be monotonic. Add some margin for error in the timeout asserts. --------- Co-authored-by: Anthony Pesch <inolen@gmail.com>
1 parent a699999 commit 56d5b48

File tree

4 files changed

+98
-70
lines changed

4 files changed

+98
-70
lines changed

test/core/test_poll_blocking.c

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,48 +17,57 @@
1717
#include <sys/time.h>
1818

1919
#define TIMEOUT_MS 300
20+
// It is possible for the node timers (such as setTimeout or Atomics.wait) to wake up
21+
// slightly earlier than requested. Because we measure times accurately using
22+
// clock_gettime, we give tests a 5 milliseconds error margin to avoid flaky timeouts.
23+
#define TIMEOUT_MARGIN_MS 5
2024

2125
void sleep_ms(int ms) {
2226
usleep(ms * 1000);
2327
}
2428

25-
int64_t timeval_delta_ms(struct timeval* begin, struct timeval* end) {
26-
int64_t delta_s = end->tv_sec - begin->tv_sec;
27-
int64_t delta_us = end->tv_usec - begin->tv_usec;
28-
assert(delta_s >= 0);
29-
return (delta_s * 1000) + (delta_us / 1000);
29+
int64_t timespec_delta_ms(struct timespec* begin, struct timespec* end) {
30+
int64_t delta_sec = end->tv_sec - begin->tv_sec;
31+
int64_t delta_nsec = end->tv_nsec - begin->tv_nsec;
32+
33+
assert(delta_sec >= 0);
34+
assert(delta_nsec > -1000000000 && delta_nsec < 1000000000);
35+
36+
int64_t delta_ms = (delta_sec * 1000) + (delta_nsec / 1000000);
37+
assert(delta_ms >= 0);
38+
return delta_ms;
3039
}
3140

3241
// Check if timeout works without fds
3342
void test_timeout_without_fds() {
3443
printf("test_timeout_without_fds\n");
35-
struct timeval begin, end;
44+
struct timespec begin, end;
3645

37-
gettimeofday(&begin, NULL);
46+
clock_gettime(CLOCK_MONOTONIC, &begin);
3847
assert(poll(NULL, 0, TIMEOUT_MS) == 0);
39-
gettimeofday(&end, NULL);
48+
clock_gettime(CLOCK_MONOTONIC, &end);
4049

41-
int64_t duration = timeval_delta_ms(&begin, &end);
50+
int64_t duration = timespec_delta_ms(&begin, &end);
4251
printf(" -> duration: %lld ms\n", duration);
43-
assert(duration >= TIMEOUT_MS);
52+
assert(duration >= TIMEOUT_MS - TIMEOUT_MARGIN_MS);
4453
}
4554

4655
// Check if timeout works with fds without events
4756
void test_timeout_with_fds_without_events() {
4857
printf("test_timeout_with_fds_without_events\n");
49-
struct timeval begin, end;
58+
struct timespec begin, end;
5059
int pipe_a[2];
5160

5261
assert(pipe(pipe_a) == 0);
5362

54-
gettimeofday(&begin, NULL);
63+
clock_gettime(CLOCK_MONOTONIC, &begin);
5564
struct pollfd fds = {pipe_a[0], 0, 0};
5665
assert(poll(&fds, 1, TIMEOUT_MS) == 0);
57-
gettimeofday(&end, NULL);
66+
clock_gettime(CLOCK_MONOTONIC, &end);
5867

59-
int64_t duration = timeval_delta_ms(&begin, &end);
68+
int64_t duration = timespec_delta_ms(&begin, &end);
6069
printf(" -> duration: %lld ms\n", duration);
61-
assert(duration >= TIMEOUT_MS);
70+
assert(duration >= TIMEOUT_MS - TIMEOUT_MARGIN_MS);
6271

6372
close(pipe_a[0]); close(pipe_a[1]);
6473
}
@@ -77,7 +86,7 @@ void *write_after_sleep(void * arg) {
7786
// Check if poll can unblock on an event
7887
void test_unblock_poll() {
7988
printf("test_unblock_poll\n");
80-
struct timeval begin, end;
89+
struct timespec begin, end;
8190
pthread_t tid;
8291
int pipe_a[2];
8392

@@ -88,15 +97,15 @@ void test_unblock_poll() {
8897
{pipe_a[0], POLLIN, 0},
8998
{pipe_shared[0], POLLIN, 0},
9099
};
91-
gettimeofday(&begin, NULL);
100+
clock_gettime(CLOCK_MONOTONIC, &begin);
92101
assert(pthread_create(&tid, NULL, write_after_sleep, NULL) == 0);
93102
assert(poll(fds, 2, -1) == 1);
94-
gettimeofday(&end, NULL);
103+
clock_gettime(CLOCK_MONOTONIC, &end);
95104
assert(fds[1].revents & POLLIN);
96105

97-
int64_t duration = timeval_delta_ms(&begin, &end);
106+
int64_t duration = timespec_delta_ms(&begin, &end);
98107
printf(" -> duration: %lld ms\n", duration);
99-
assert(duration >= TIMEOUT_MS);
108+
assert(duration >= TIMEOUT_MS - TIMEOUT_MARGIN_MS);
100109

101110
pthread_join(tid, NULL);
102111

@@ -109,21 +118,21 @@ pthread_mutex_t running_lock = PTHREAD_MUTEX_INITIALIZER;
109118
pthread_cond_t running_cv = PTHREAD_COND_INITIALIZER;
110119

111120
void *do_poll_in_thread(void * arg) {
112-
struct timeval begin, end;
121+
struct timespec begin, end;
113122

114-
gettimeofday(&begin, NULL);
123+
clock_gettime(CLOCK_MONOTONIC, &begin);
115124
struct pollfd fds = {pipe_shared[0], POLLIN, 0};
116125
pthread_mutex_lock(&running_lock);
117126
threads_running++;
118127
pthread_cond_signal(&running_cv);
119128
pthread_mutex_unlock(&running_lock);
120129
assert(poll(&fds, 1, 4000) == 1);
121-
gettimeofday(&end, NULL);
130+
clock_gettime(CLOCK_MONOTONIC, &end);
122131
assert(fds.revents & POLLIN);
123132

124-
int64_t duration = timeval_delta_ms(&begin, &end);
133+
int64_t duration = timespec_delta_ms(&begin, &end);
125134
printf(" -> duration: %lld ms\n", duration);
126-
assert((duration >= TIMEOUT_MS) && (duration < 4000));
135+
assert((duration >= TIMEOUT_MS - TIMEOUT_MARGIN_MS) && (duration < 4000));
127136

128137
return NULL;
129138
}

test/core/test_poll_blocking_asyncify.c

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,38 @@
1616
#include <sys/time.h>
1717
#include <time.h>
1818
#include <unistd.h>
19-
2019
#include <emscripten/eventloop.h>
2120

22-
int64_t timeval_delta_ms(struct timeval* begin, struct timeval* end) {
23-
int64_t delta_s = end->tv_sec - begin->tv_sec;
24-
int64_t delta_us = end->tv_usec - begin->tv_usec;
25-
assert(delta_s >= 0);
26-
return (delta_s * 1000) + (delta_us / 1000);
21+
// It is possible for the node timers (such as setTimeout or Atomics.wait) to wake up
22+
// slightly earlier than requested. Because we measure times accurately using
23+
// clock_gettime, we give tests a 5 milliseconds error margin to avoid flaky timeouts.
24+
#define TIMEOUT_MARGIN_MS 5
25+
26+
int64_t timespec_delta_ms(struct timespec* begin, struct timespec* end) {
27+
int64_t delta_sec = end->tv_sec - begin->tv_sec;
28+
int64_t delta_nsec = end->tv_nsec - begin->tv_nsec;
29+
30+
assert(delta_sec >= 0);
31+
assert(delta_nsec > -1000000000 && delta_nsec < 1000000000);
32+
33+
int64_t delta_ms = (delta_sec * 1000) + (delta_nsec / 1000000);
34+
assert(delta_ms >= 0);
35+
return delta_ms;
2736
}
2837

2938
// Check if timeout works without fds
3039
void test_timeout_without_fds() {
3140
printf("test_timeout_without_fds\n");
32-
struct timeval begin, end;
41+
struct timespec begin = {0};
42+
struct timespec end = {0};
3343

34-
gettimeofday(&begin, NULL);
44+
clock_gettime(CLOCK_MONOTONIC, &begin);
3545
assert(poll(NULL, 0, 1000) == 0);
36-
gettimeofday(&end, NULL);
46+
clock_gettime(CLOCK_MONOTONIC, &end);
3747

38-
int64_t duration = timeval_delta_ms(&begin, &end);
48+
int64_t duration = timespec_delta_ms(&begin, &end);
3949
printf(" -> duration: %lld ms\n", duration);
40-
assert(duration >= 1000);
50+
assert(duration >= 1000 - TIMEOUT_MARGIN_MS);
4151
}
4252

4353
int pipe_shared[2];
@@ -51,7 +61,8 @@ void write_to_pipe(void * arg) {
5161
// Check if poll can unblock on an event
5262
void test_unblock_poll() {
5363
printf("test_unblock_poll\n");
54-
struct timeval begin, end;
64+
struct timespec begin = {0};
65+
struct timespec end = {0};
5566
int pipe_a[2];
5667

5768
assert(pipe(pipe_a) == 0);
@@ -62,14 +73,14 @@ void test_unblock_poll() {
6273
{pipe_shared[0], POLLIN, 0},
6374
};
6475
emscripten_set_timeout(write_to_pipe, 1000, NULL);
65-
gettimeofday(&begin, NULL);
76+
clock_gettime(CLOCK_MONOTONIC, &begin);
6677
assert(poll(fds, 2, -1) == 1);
67-
gettimeofday(&end, NULL);
78+
clock_gettime(CLOCK_MONOTONIC, &end);
6879
assert(fds[1].revents & POLLIN);
6980

70-
int64_t duration = timeval_delta_ms(&begin, &end);
81+
int64_t duration = timespec_delta_ms(&begin, &end);
7182
printf(" -> duration: %lld ms\n", duration);
72-
assert(duration >= 1000);
83+
assert(duration >= 1000 - TIMEOUT_MARGIN_MS);
7384

7485
close(pipe_a[0]); close(pipe_a[1]);
7586
close(pipe_shared[0]); close(pipe_shared[1]);

test/core/test_select_blocking.c

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,49 @@
1414
#include <sys/time.h>
1515

1616
#define TIMEOUT_MS 300
17+
// It is possible for the node timers (such as setTimeout or Atomics.wait) to wake up
18+
// slightly earlier than requested. Because we measure times accurately using
19+
// clock_gettime, we give tests a 5 milliseconds error margin to avoid flaky timeouts.
20+
#define TIMEOUT_MARGIN_MS 5
1721

1822
void sleep_ms(int ms) {
1923
usleep(ms * 1000);
2024
}
2125

22-
int64_t timeval_delta_ms(struct timeval* begin, struct timeval* end) {
23-
int64_t delta_s = end->tv_sec - begin->tv_sec;
24-
int64_t delta_us = end->tv_usec - begin->tv_usec;
25-
assert(delta_s >= 0);
26-
return (delta_s * 1000) + (delta_us / 1000);
26+
int64_t timespec_delta_ms(struct timespec* begin, struct timespec* end) {
27+
int64_t delta_sec = end->tv_sec - begin->tv_sec;
28+
int64_t delta_nsec = end->tv_nsec - begin->tv_nsec;
29+
30+
assert(delta_sec >= 0);
31+
assert(delta_nsec > -1000000000 && delta_nsec < 1000000000);
32+
33+
int64_t delta_ms = (delta_sec * 1000) + (delta_nsec / 1000000);
34+
assert(delta_ms >= 0);
35+
return delta_ms;
2736
}
2837

2938
// Check if timeout works without fds
3039
void test_timeout_without_fds() {
3140
printf("test_timeout_without_fds\n");
32-
struct timeval tv, begin, end;
41+
struct timespec begin, end;
42+
struct timeval tv;
3343

3444
tv.tv_sec = 0;
3545
tv.tv_usec = TIMEOUT_MS * 1000;
36-
gettimeofday(&begin, NULL);
46+
clock_gettime(CLOCK_MONOTONIC, &begin);
3747
assert(select(0, NULL, NULL, NULL, &tv) == 0);
38-
gettimeofday(&end, NULL);
48+
clock_gettime(CLOCK_MONOTONIC, &end);
3949

40-
int64_t duration = timeval_delta_ms(&begin, &end);
50+
int64_t duration = timespec_delta_ms(&begin, &end);
4151
printf(" -> duration: %lld ms\n", duration);
42-
assert(duration >= TIMEOUT_MS);
52+
assert(duration >= TIMEOUT_MS - TIMEOUT_MARGIN_MS);
4353
}
4454

4555
// Check if timeout works with fds without events
4656
void test_timeout_with_fds_without_events() {
4757
printf("test_timeout_with_fds_without_events\n");
48-
struct timeval tv, begin, end;
58+
struct timespec begin, end;
59+
struct timeval tv;
4960
fd_set readfds;
5061
int pipe_a[2];
5162

@@ -55,13 +66,13 @@ void test_timeout_with_fds_without_events() {
5566
tv.tv_usec = TIMEOUT_MS * 1000;
5667
FD_ZERO(&readfds);
5768
FD_SET(pipe_a[0], &readfds);
58-
gettimeofday(&begin, NULL);
69+
clock_gettime(CLOCK_MONOTONIC, &begin);
5970
assert(select(pipe_a[0] + 1, &readfds, NULL, NULL, &tv) == 0);
60-
gettimeofday(&end, NULL);
71+
clock_gettime(CLOCK_MONOTONIC, &end);
6172

62-
int64_t duration = timeval_delta_ms(&begin, &end);
73+
int64_t duration = timespec_delta_ms(&begin, &end);
6374
printf(" -> duration: %lld ms\n", duration);
64-
assert(duration >= TIMEOUT_MS);
75+
assert(duration >= TIMEOUT_MS - TIMEOUT_MARGIN_MS);
6576

6677
close(pipe_a[0]); close(pipe_a[1]);
6778
}
@@ -80,7 +91,7 @@ void *write_after_sleep(void * arg) {
8091
// Check if select can unblock on an event
8192
void test_unblock_select() {
8293
printf("test_unblock_select\n");
83-
struct timeval begin, end;
94+
struct timespec begin, end;
8495
fd_set readfds;
8596
pthread_t tid;
8697
int pipe_a[2];
@@ -92,15 +103,15 @@ void test_unblock_select() {
92103
FD_SET(pipe_a[0], &readfds);
93104
FD_SET(pipe_shared[0], &readfds);
94105
int maxfd = (pipe_a[0] > pipe_shared[0] ? pipe_a[0] : pipe_shared[0]);
95-
gettimeofday(&begin, NULL);
106+
clock_gettime(CLOCK_MONOTONIC, &begin);
96107
assert(pthread_create(&tid, NULL, write_after_sleep, NULL) == 0);
97108
assert(select(maxfd + 1, &readfds, NULL, NULL, NULL) == 1);
98-
gettimeofday(&end, NULL);
109+
clock_gettime(CLOCK_MONOTONIC, &end);
99110
assert(FD_ISSET(pipe_shared[0], &readfds));
100111

101-
int64_t duration = timeval_delta_ms(&begin, &end);
112+
int64_t duration = timespec_delta_ms(&begin, &end);
102113
printf(" -> duration: %lld ms\n", duration);
103-
assert(duration >= TIMEOUT_MS);
114+
assert(duration >= TIMEOUT_MS - TIMEOUT_MARGIN_MS);
104115

105116
pthread_join(tid, NULL);
106117

@@ -113,28 +124,28 @@ pthread_mutex_t running_lock = PTHREAD_MUTEX_INITIALIZER;
113124
pthread_cond_t running_cv = PTHREAD_COND_INITIALIZER;
114125

115126
void *do_select_in_thread(void * arg) {
116-
struct timeval begin, end;
117-
fd_set readfds;
127+
struct timespec begin, end;
118128
struct timeval tv;
129+
fd_set readfds;
119130
tv.tv_sec = 4;
120131
tv.tv_usec = 0;
121132

122133
FD_ZERO(&readfds);
123134
FD_SET(pipe_shared[0], &readfds);
124135
int maxfd = pipe_shared[0];
125136

126-
gettimeofday(&begin, NULL);
137+
clock_gettime(CLOCK_MONOTONIC, &begin);
127138
pthread_mutex_lock(&running_lock);
128139
threads_running++;
129140
pthread_cond_signal(&running_cv);
130141
pthread_mutex_unlock(&running_lock);
131142
assert(select(maxfd + 1, &readfds, NULL, NULL, &tv) == 1);
132-
gettimeofday(&end, NULL);
143+
clock_gettime(CLOCK_MONOTONIC, &end);
133144
assert(FD_ISSET(pipe_shared[0], &readfds));
134145

135-
int64_t duration = timeval_delta_ms(&begin, &end);
146+
int64_t duration = timespec_delta_ms(&begin, &end);
136147
printf(" -> duration: %lld ms\n", duration);
137-
assert((duration >= TIMEOUT_MS) && (duration < 4000));
148+
assert((duration >= TIMEOUT_MS - TIMEOUT_MARGIN_MS) && (duration < 4000));
138149

139150
return NULL;
140151
}

test/test_core.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9573,17 +9573,14 @@ def test_syscall_intercept(self):
95739573
self.do_core_test('test_syscall_intercept.c')
95749574

95759575
@requires_pthreads
9576-
@flaky('https://github.com/emscripten-core/emscripten/issues/26256')
95779576
def test_select_blocking(self):
95789577
self.do_runf('core/test_select_blocking.c', cflags=['-pthread', '-sPROXY_TO_PTHREAD=1', '-sEXIT_RUNTIME=1'])
95799578

95809579
@requires_pthreads
9581-
@flaky('https://github.com/emscripten-core/emscripten/issues/26256')
95829580
def test_poll_blocking(self):
95839581
self.do_runf('core/test_poll_blocking.c', cflags=['-pthread', '-sPROXY_TO_PTHREAD=1', '-sEXIT_RUNTIME=1'])
95849582

95859583
@with_asyncify_and_jspi
9586-
@flaky('https://github.com/emscripten-core/emscripten/issues/26256')
95879584
def test_poll_blocking_asyncify(self):
95889585
if self.get_setting('JSPI') and engine_is_v8(self.get_current_js_engine()):
95899586
self.skipTest('test requires setTimeout which is not supported under v8')

0 commit comments

Comments
 (0)