blob: 3fb09accba6160fa9cca66d7658c44e26e2531fc [file] [log] [blame]
/*
* Copyright (C) 2019 - 2020 Intel Corporation
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stddef.h>
#include <stdint.h>
#include <time.h>
#include <sys/timerfd.h>
#include <usfstl/sched.h>
#include <usfstl/loop.h>
void usfstl_sched_wallclock_handle_fd(struct usfstl_loop_entry *entry)
{
struct usfstl_scheduler *sched;
uint64_t v;
sched = container_of(entry, struct usfstl_scheduler, wallclock.entry);
USFSTL_ASSERT_EQ((int)read(entry->fd, &v, sizeof(v)), (int)sizeof(v), "%d");
sched->wallclock.timer_triggered = 1;
}
static void usfstl_sched_wallclock_initialize(struct usfstl_scheduler *sched)
{
struct timespec now = {};
USFSTL_ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &now), 0, "%d");
sched->wallclock.start = (uint64_t)now.tv_sec * 1000000000 + now.tv_nsec;
sched->wallclock.initialized = 1;
}
void usfstl_sched_wallclock_request(struct usfstl_scheduler *sched, uint64_t time)
{
struct itimerspec itimer = {};
unsigned int nsec_per_tick = sched->wallclock.nsec_per_tick;
uint64_t waketime;
USFSTL_ASSERT(nsec_per_tick != 0);
if (!sched->wallclock.initialized)
usfstl_sched_wallclock_initialize(sched);
waketime = sched->wallclock.start + nsec_per_tick * time;
itimer.it_value.tv_sec = waketime / 1000000000;
itimer.it_value.tv_nsec = waketime % 1000000000;
USFSTL_ASSERT_EQ(timerfd_settime(sched->wallclock.entry.fd,
TFD_TIMER_ABSTIME, &itimer, NULL),
0, "%d");
}
void usfstl_sched_wallclock_wait(struct usfstl_scheduler *sched)
{
sched->wallclock.timer_triggered = 0;
usfstl_loop_register(&sched->wallclock.entry);
while (!sched->wallclock.timer_triggered)
usfstl_loop_wait_and_handle();
usfstl_loop_unregister(&sched->wallclock.entry);
usfstl_sched_set_time(sched, sched->prev_external_sync);
}
void usfstl_sched_wallclock_init(struct usfstl_scheduler *sched,
unsigned int ns_per_tick)
{
USFSTL_ASSERT(!sched->external_request && !sched->external_wait);
sched->external_request = usfstl_sched_wallclock_request;
sched->external_wait = usfstl_sched_wallclock_wait;
sched->wallclock.entry.fd = timerfd_create(CLOCK_MONOTONIC, 0);
USFSTL_ASSERT(sched->wallclock.entry.fd >= 0);
sched->wallclock.entry.handler = usfstl_sched_wallclock_handle_fd;
sched->wallclock.nsec_per_tick = ns_per_tick;
}
void usfstl_sched_wallclock_exit(struct usfstl_scheduler *sched)
{
USFSTL_ASSERT(sched->external_request == usfstl_sched_wallclock_request &&
sched->external_wait == usfstl_sched_wallclock_wait);
sched->external_request = NULL;
sched->external_wait = NULL;
close(sched->wallclock.entry.fd);
}
static void _usfstl_sched_wallclock_sync_real(struct usfstl_scheduler *sched)
{
struct timespec now = {};
uint64_t nowns;
USFSTL_ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, &now), 0, "%d");
nowns = (uint64_t)now.tv_sec * 1000000000 + now.tv_nsec;
nowns -= sched->wallclock.start;
usfstl_sched_set_time(sched, nowns / sched->wallclock.nsec_per_tick);
}
static void usfstl_sched_wallclock_sync_real(void *data)
{
_usfstl_sched_wallclock_sync_real(data);
}
void usfstl_sched_wallclock_wait_and_handle(struct usfstl_scheduler *sched)
{
void (*old_fn)(void *);
void *old_data;
if (usfstl_sched_next_pending(sched, NULL))
return;
if (!sched->wallclock.initialized) {
/*
* At least once at the beginning we should schedule
* and initialize from an initial request, not here.
*/
usfstl_loop_wait_and_handle();
return;
}
old_fn = g_usfstl_loop_pre_handler_fn;
old_data = g_usfstl_loop_pre_handler_fn_data;
g_usfstl_loop_pre_handler_fn = usfstl_sched_wallclock_sync_real;
g_usfstl_loop_pre_handler_fn_data = sched;
usfstl_loop_wait_and_handle();
g_usfstl_loop_pre_handler_fn = old_fn;
g_usfstl_loop_pre_handler_fn_data = old_data;
}