blob: 6a9a8fc2dcc8a38fa96a0f9cf6b282e554f7f907 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "tests.h"
#include <err.h>
#include <inttypes.h>
#include <malloc.h>
#include <platform.h>
#include <stdio.h>
#include <kernel/event.h>
#include <kernel/thread.h>
#include <kernel/timer.h>
#include <fbl/algorithm.h>
#include <fbl/atomic.h>
#include <zircon/types.h>
static void timer_cb(timer_t* timer, zx_time_t now, void* arg) {
event_t* event = (event_t*)arg;
event_signal(event, true);
}
static int timer_do_one_thread(void* arg) {
event_t event;
timer_t timer;
event_init(&event, false, 0);
timer_init(&timer);
timer_set(&timer, current_time() + ZX_MSEC(10), TIMER_SLACK_CENTER, 0, timer_cb, &event);
event_wait(&event);
printf("got timer on cpu %u\n", arch_curr_cpu_num());
event_destroy(&event);
return 0;
}
static void timer_test_all_cpus(void) {
thread_t* timer_threads[SMP_MAX_CPUS];
uint max = arch_max_num_cpus();
uint i;
for (i = 0; i < max; i++) {
char name[16];
snprintf(name, sizeof(name), "timer %u\n", i);
timer_threads[i] = thread_create_etc(
NULL, name, timer_do_one_thread, NULL,
DEFAULT_PRIORITY, NULL, NULL, DEFAULT_STACK_SIZE, NULL);
if (timer_threads[i] == NULL) {
printf("failed to create thread for cpu %u\n", i);
return;
}
thread_set_cpu_affinity(timer_threads[i], cpu_num_to_mask(i));
thread_resume(timer_threads[i]);
}
uint joined = 0;
for (i = 0; i < max; i++) {
if (thread_join(timer_threads[i], NULL, ZX_SEC(1)) == 0) {
joined += 1;
}
}
printf("%u threads created, %u threads joined\n", max, joined);
}
static void timer_cb2(timer_t* timer, zx_time_t now, void* arg) {
auto timer_count = static_cast<fbl::atomic<size_t>*>(arg);
timer_count->fetch_add(1);
thread_preempt_set_pending();
}
static void timer_test_coalescing(enum slack_mode mode, uint64_t slack,
const zx_time_t* deadline, const int64_t* expected_adj, size_t count) {
printf("testing coalsecing mode %d\n", mode);
fbl::atomic<size_t> timer_count(0);
timer_t* timer = (timer_t*)malloc(sizeof(timer_t) * count);
printf(" orig new adjustment\n");
for (size_t ix = 0; ix != count; ++ix) {
timer_init(&timer[ix]);
zx_time_t dl = deadline[ix];
timer_set(&timer[ix], dl, mode, slack, timer_cb2, &timer_count);
printf("[%zu] %" PRIu64 " -> %" PRIu64 ", %" PRIi64 "\n",
ix, dl, timer[ix].scheduled_time, timer[ix].slack);
if (timer[ix].slack != expected_adj[ix]) {
printf("\n!! unexpected adjustment! expected %" PRIi64 "\n", expected_adj[ix]);
}
}
// Wait for the timers to fire.
while (timer_count.load() != count) {
thread_sleep(current_time() + ZX_MSEC(5));
}
free(timer);
}
static void timer_test_coalescing_center(void) {
zx_time_t when = current_time() + ZX_MSEC(1);
zx_duration_t off = ZX_USEC(10);
zx_duration_t slack = 2u * off;
const zx_time_t deadline[] = {
when + (6u * off), // non-coalesced, adjustment = 0
when, // non-coalesced, adjustment = 0
when - off, // coalesced with [1], adjustment = 10u
when - (3u * off), // non-coalesced, adjustment = 0
when + off, // coalesced with [1], adjustment = -10u
when + (3u * off), // non-coalesced, adjustment = 0
when + (5u * off), // coalesced with [0], adjustment = 10u
when - (3u * off), // non-coalesced, same as [3], adjustment = 0
};
const int64_t expected_adj[fbl::count_of(deadline)] = {
0, 0, ZX_USEC(10), 0, -(int64_t)ZX_USEC(10), 0, ZX_USEC(10), 0};
timer_test_coalescing(
TIMER_SLACK_CENTER, slack, deadline, expected_adj, fbl::count_of(deadline));
}
static void timer_test_coalescing_late(void) {
zx_time_t when = current_time() + ZX_MSEC(1);
zx_duration_t off = ZX_USEC(10);
zx_duration_t slack = 3u * off;
const zx_time_t deadline[] = {
when + off, // non-coalesced, adjustment = 0
when + (2u * off), // non-coalesced, adjustment = 0
when - off, // coalesced with [0], adjustment = 20u
when - (3u * off), // non-coalesced, adjustment = 0
when + (3u * off), // non-coalesced, adjustment = 0
when + (2u * off), // non-coalesced, same as [1]
when - (4u * off), // coalesced with [3], adjustment = 10u
};
const int64_t expected_adj[fbl::count_of(deadline)] = {
0, 0, ZX_USEC(20), 0, 0, 0, ZX_USEC(10)};
timer_test_coalescing(
TIMER_SLACK_LATE, slack, deadline, expected_adj, fbl::count_of(deadline));
}
static void timer_test_coalescing_early(void) {
zx_time_t when = current_time() + ZX_MSEC(1);
zx_duration_t off = ZX_USEC(10);
zx_duration_t slack = 3u * off;
const zx_time_t deadline[] = {
when, // non-coalesced, adjustment = 0
when + (2u * off), // coalesced with [0], adjustment = -20u
when - off, // non-coalesced, adjustment = 0
when - (3u * off), // non-coalesced, adjustment = 0
when + (4u * off), // non-coalesced, adjustment = 0
when + (5u * off), // coalesced with [4], adjustment = -10u
when - (2u * off), // coalesced with [3], adjustment = -10u
};
const int64_t expected_adj[fbl::count_of(deadline)] = {
0, -(int64_t)ZX_USEC(20), 0, 0, 0, -(int64_t)ZX_USEC(10), -(int64_t)ZX_USEC(10)};
timer_test_coalescing(
TIMER_SLACK_EARLY, slack, deadline, expected_adj, fbl::count_of(deadline));
}
static void timer_far_deadline(void) {
event_t event;
timer_t timer;
event_init(&event, false, 0);
timer_init(&timer);
timer_set(&timer, UINT64_MAX - 5, TIMER_SLACK_CENTER, 0, timer_cb, &event);
zx_status_t st = event_wait_deadline(&event, current_time() + ZX_MSEC(100), false);
if (st != ZX_ERR_TIMED_OUT) {
printf("error: unexpected timer fired!\n");
} else {
timer_cancel(&timer);
}
event_destroy(&event);
}
void timer_tests(void) {
timer_test_coalescing_center();
timer_test_coalescing_late();
timer_test_coalescing_early();
timer_test_all_cpus();
timer_far_deadline();
}