5 #include <mach/mach_time.h>
14 #include <sys/event.h>
16 #include <darwintest.h>
18 extern char **environ
;
20 static mach_timebase_info_data_t tb_info
;
21 static const uint64_t one_mil
= 1000LL * 1000LL;
23 #define tick_to_ns(ticks) (((ticks) * tb_info.numer) / (tb_info.denom))
24 #define tick_to_ms(ticks) (tick_to_ns(ticks)/one_mil)
26 #define ns_to_tick(ns) ((ns) * tb_info.denom / tb_info.numer)
27 #define ms_to_tick(ms) (ns_to_tick((ms) * one_mil))
32 uint64_t abs_now
= mach_absolute_time();
33 uint64_t cnt_now
= mach_continuous_time();;
34 return tick_to_ms(cnt_now
) - tick_to_ms(abs_now
);
37 static int run_sleep_tests
= 0;
40 trigger_sleep(int for_secs
)
42 if (!run_sleep_tests
) {
46 // sleep for 1 seconds each iteration
48 snprintf(buf
, 10, "%d", for_secs
);
50 T_LOG("Sleepeing for %s seconds...", buf
);
53 char *const pmset1_args
[] = {"/usr/bin/pmset", "relative", "wake", buf
, NULL
};
54 T_ASSERT_POSIX_ZERO((spawn_ret
= posix_spawn(&pid
, pmset1_args
[0], NULL
, NULL
, pmset1_args
, environ
)), NULL
);
56 T_ASSERT_EQ(waitpid(pid
, &spawn_ret
, 0), pid
, NULL
);
57 T_ASSERT_EQ(spawn_ret
, 0, NULL
);
59 char *const pmset2_args
[] = {"/usr/bin/pmset", "sleepnow", NULL
};
60 T_ASSERT_POSIX_ZERO((spawn_ret
= posix_spawn(&pid
, pmset2_args
[0], NULL
, NULL
, pmset2_args
, environ
)), NULL
);
62 T_ASSERT_EQ(waitpid(pid
, &spawn_ret
, 0), pid
, NULL
);
63 T_ASSERT_EQ(spawn_ret
, 0, NULL
);
68 // waits up to 30 seconds for system to sleep
69 // returns number of seconds it took for sleep to be entered
70 // or -1 if sleep wasn't accomplished
74 if (!run_sleep_tests
) {
78 uint64_t before_diff
= time_delta_ms();
80 for (int i
= 0; i
< 30; i
++) {
81 uint64_t after_diff
= time_delta_ms();
83 // on OSX, there's enough latency between calls to MCT and MAT
84 // when the system is going down for sleep for values to diverge a few ms
85 if (llabs((int64_t)before_diff
- (int64_t)after_diff
) > 2) {
90 T_LOG("waited %d seconds for sleep...", i
+ 1);
95 T_DECL(kevent_continuous_time_periodic_tick
, "kevent(EVFILT_TIMER with NOTE_MACH_CONTINUOUS_TIME)", T_META_LTEPHASE(LTE_POSTINIT
)){
96 mach_timebase_info(&tb_info
);
98 T_ASSERT_POSIX_SUCCESS((kq
= kqueue()), NULL
);
100 struct kevent64_s kev
= {
102 .filter
= EVFILT_TIMER
,
103 .flags
= EV_ADD
| EV_RECEIPT
,
104 .fflags
= NOTE_SECONDS
| NOTE_MACH_CONTINUOUS_TIME
,
107 T_LOG("EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, NOTE_SECONDS | NOTE_MACH_CONTINUOUS_TIME, 4, 0, 0, 0);");
109 T_ASSERT_EQ(kevent64(kq
, &kev
, 1, &kev
, 1, 0, NULL
), 1, NULL
);
110 T_ASSERT_EQ(0ll, kev
.data
, "No error returned");
112 uint64_t abs_then
= mach_absolute_time();
113 uint64_t cnt_then
= mach_continuous_time();;
116 int sleep_secs
= wait_for_sleep();
118 T_WITH_ERRNO
; T_ASSERT_EQ(kevent64(kq
, NULL
, 0, &kev
, 1, 0, NULL
), 1, "kevent() should have returned one event");
119 T_LOG("event = {.ident = %llx, .filter = %d, .flags = %d, .fflags = %d, .data = %lld, .udata = %lld}", kev
.ident
, kev
.filter
, kev
.flags
, kev
.fflags
, kev
.data
, kev
.udata
);
120 T_ASSERT_EQ(kev
.flags
& EV_ERROR
, 0, "event should not have EV_ERROR set: %s", kev
.flags
& EV_ERROR
? strerror((int)kev
.data
) : "no error");
122 uint64_t abs_now
= mach_absolute_time();
123 uint64_t cnt_now
= mach_continuous_time();;
124 uint64_t ct_ms_progressed
= tick_to_ms(cnt_now
- cnt_then
);
125 uint64_t ab_ms_progressed
= tick_to_ms(abs_now
- abs_then
);
127 T_LOG("ct progressed %llu ms, abs progressed %llu ms", ct_ms_progressed
, tick_to_ms(abs_now
- abs_then
));
129 if (run_sleep_tests
) {
130 T_ASSERT_GT(llabs((int64_t)ct_ms_progressed
- (int64_t)ab_ms_progressed
), 500LL, "should have > 500ms difference between MCT and MAT");
132 T_ASSERT_LT(llabs((int64_t)ct_ms_progressed
- (int64_t)ab_ms_progressed
), 10LL, "should have < 10ms difference between MCT and MAT");
135 if (sleep_secs
< 4) {
136 T_ASSERT_LT(llabs((int64_t)ct_ms_progressed
- 4000), 100LL, "mach_continuous_time should progress ~4 seconds (+/- 100ms) between sleeps");
141 kev
= (struct kevent64_s
){
143 .filter
= EVFILT_TIMER
,
144 .flags
= EV_DELETE
| EV_RECEIPT
,
146 T_LOG("EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, 0);");
147 T_ASSERT_EQ(kevent64(kq
, &kev
, 1, &kev
, 1, 0, NULL
), 1, NULL
);
148 T_ASSERT_EQ(0ll, kev
.data
, "No error returned");
150 T_ASSERT_POSIX_ZERO(close(kq
), NULL
);
153 T_DECL(kevent_continuous_time_absolute
, "kevent(EVFILT_TIMER with NOTE_MACH_CONTINUOUS_TIME and NOTE_ABSOLUTE)", T_META_LTEPHASE(LTE_POSTINIT
)){
154 mach_timebase_info(&tb_info
);
157 T_ASSERT_POSIX_SUCCESS((kq
= kqueue()), NULL
);
160 gettimeofday(&tv
, NULL
);
161 int64_t nowus
= (int64_t)tv
.tv_sec
* USEC_PER_SEC
+ (int64_t)tv
.tv_usec
;
162 int64_t fire_at
= (3 * USEC_PER_SEC
) + nowus
;
164 uint64_t cnt_now
= mach_continuous_time();
165 uint64_t cnt_then
= cnt_now
+ ms_to_tick(3000);
167 T_LOG("currently is %llu, firing at %llu", nowus
, fire_at
);
169 struct kevent64_s kev
= {
171 .filter
= EVFILT_TIMER
,
172 .flags
= EV_ADD
| EV_RECEIPT
,
173 .fflags
= NOTE_MACH_CONTINUOUS_TIME
| NOTE_ABSOLUTE
| NOTE_USECONDS
,
176 T_LOG("EV_SET(&kev, 2, EVFILT_TIMER, EV_ADD, NOTE_MACH_CONTINUOUS_TIME | NOTE_ABSOLUTE | NOTE_USECONDS, fire_at, 0);");
178 T_ASSERT_EQ(kevent64(kq
, &kev
, 1, &kev
, 1, 0, NULL
), 1, NULL
);
179 T_ASSERT_EQ(0ll, kev
.data
, "No error returned");
181 T_LOG("testing NOTE_MACH_CONTINUOUS_TIME | NOTE_ABSOLUTE between sleep");
185 struct timespec timeout
= {
189 struct kevent64_s event
= {0};
190 T_ASSERT_EQ(kevent64(kq
, NULL
, 0, &event
, 1, 0, &timeout
), 1, "kevent() should have returned one event");
191 T_LOG("event = {.ident = %llx, .filter = %d, .flags = %d, .fflags = %d, .data = %lld, .udata = %lld}", event
.ident
, event
.filter
, event
.flags
, event
.fflags
, event
.data
, event
.udata
);
192 T_ASSERT_EQ(event
.flags
& EV_ERROR
, 0, "event should not have EV_ERROR set: %s", event
.flags
& EV_ERROR
? strerror((int)event
.data
) : "no error");
194 uint64_t elapsed_ms
= tick_to_ms(mach_continuous_time() - cnt_now
);
195 int64_t missed_by
= tick_to_ns((int64_t)mach_continuous_time() - (int64_t)cnt_then
) / 1000000;
197 // ~1/2 second is about as good as we'll get
198 T_ASSERT_LT(llabs(missed_by
), 500LL, "timer should pop 3 sec in the future, popped after %lldms", elapsed_ms
);
200 T_ASSERT_EQ(event
.data
, 1LL, NULL
);
202 T_ASSERT_EQ(event
.ident
, 2ULL, NULL
);
204 // try getting a periodic tick out of kq
205 T_ASSERT_EQ(kevent64(kq
, NULL
, 0, &event
, 1, 0, &timeout
), 0, NULL
);
206 T_ASSERT_EQ(event
.flags
& EV_ERROR
, 0, "event should not have EV_ERROR set: %s", event
.flags
& EV_ERROR
? strerror((int)event
.data
) : "no error");
208 T_ASSERT_POSIX_ZERO(close(kq
), NULL
);
211 T_DECL(kevent_continuous_time_pops
, "kevent(EVFILT_TIMER with NOTE_MACH_CONTINUOUS_TIME with multiple pops)", T_META_LTEPHASE(LTE_POSTINIT
)){
212 // have to throttle rate at which pmset is called
215 mach_timebase_info(&tb_info
);
218 T_ASSERT_POSIX_SUCCESS((kq
= kqueue()), NULL
);
220 // test that periodic ticks accumulate while asleep
221 struct kevent64_s kev
= {
223 .filter
= EVFILT_TIMER
,
224 .flags
= EV_ADD
| EV_RECEIPT
,
225 .fflags
= NOTE_MACH_CONTINUOUS_TIME
,
228 T_LOG("EV_SET(&kev, 3, EVFILT_TIMER, EV_ADD, NOTE_MACH_CONTINUOUS_TIME, 100, 0);");
230 // wait for first pop, then sleep
231 T_ASSERT_EQ(kevent64(kq
, &kev
, 1, &kev
, 1, 0, NULL
), 1, NULL
);
232 T_ASSERT_EQ(0ll, kev
.data
, "No error returned");
234 struct kevent64_s event
= {0};
235 T_ASSERT_EQ(kevent64(kq
, NULL
, 0, &event
, 1, 0, NULL
), 1, "kevent() should have returned one event");
236 T_LOG("event = {.ident = %llx, .filter = %d, .flags = %d, .fflags = %d, .data = %lld, .udata = %llu}", event
.ident
, event
.filter
, event
.flags
, event
.fflags
, event
.data
, event
.udata
);
237 T_ASSERT_EQ(event
.flags
& EV_ERROR
, 0, "should not have EV_ERROR set: %s", event
.flags
& EV_ERROR
? strerror((int)event
.data
) : "no error");
238 T_ASSERT_EQ(event
.ident
, 3ULL, NULL
);
240 uint64_t cnt_then
= mach_continuous_time();
244 if (run_sleep_tests
) {
245 sleep_secs
= wait_for_sleep();
247 // simulate 2 seconds of system "sleep"
251 uint64_t cnt_now
= mach_continuous_time();
253 uint64_t ms_elapsed
= tick_to_ms(cnt_now
- cnt_then
);
254 if (run_sleep_tests
) {
255 T_ASSERT_LT(llabs((int64_t)ms_elapsed
- 2000LL), 500LL, "slept for %llums, expected 2000ms (astris is connected?)", ms_elapsed
);
258 T_ASSERT_EQ(kevent64(kq
, NULL
, 0, &event
, 1, 0, NULL
), 1, "kevent() should have returned one event");
259 T_LOG("event = {.ident = %llx, .filter = %d, .flags = %d, .fflags = %d, .data = %lld, .udata = %llu}", event
.ident
, event
.filter
, event
.flags
, event
.fflags
, event
.data
, event
.udata
);
260 T_ASSERT_EQ(event
.ident
, 3ULL, NULL
);
262 uint64_t expected_pops
= ms_elapsed
/ 100;
263 uint64_t got_pops
= (uint64_t)event
.data
;
265 T_ASSERT_GE(got_pops
, expected_pops
- 1, "tracking pops while asleep");
266 T_ASSERT_POSIX_ZERO(close(kq
), NULL
);