Make clock.c atomic
This commit is contained in:
+23
-27
@@ -1,54 +1,50 @@
|
|||||||
#define pr_fmt(fmt) "clock: " fmt
|
#define pr_fmt(fmt) "clock: " fmt
|
||||||
|
|
||||||
|
#include <zinnia/atomic.h>
|
||||||
#include <zinnia/clock.h>
|
#include <zinnia/clock.h>
|
||||||
#include <zinnia/compiler.h>
|
#include <zinnia/compiler.h>
|
||||||
#include <zinnia/print.h>
|
#include <zinnia/print.h>
|
||||||
#include <zinnia/spin.h>
|
|
||||||
#include <arch/cpu.h>
|
#include <arch/cpu.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
static struct clock* active_clock = nullptr;
|
static _Atomic(struct clock*) active_clock;
|
||||||
static uint64_t counter_base = 0;
|
static _Atomic(int64_t) realtime_offset_ns;
|
||||||
static struct spinlock lock = {};
|
|
||||||
|
|
||||||
static uint64_t clock_get_elapsed_ns_locked() {
|
static uint64_t clock_read_elapsed(struct clock* clock) {
|
||||||
if (__unlikely(!active_clock))
|
if (__unlikely(!clock))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return counter_base + active_clock->get_elapsed_ns(active_clock);
|
return clock->base + clock->get_elapsed_ns(clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool clock_available() {
|
bool clock_available() {
|
||||||
spin_lock(&lock);
|
return atomic_load(&active_clock, ATOMIC_ACQUIRE) != nullptr;
|
||||||
bool available = active_clock != nullptr;
|
|
||||||
spin_unlock(&lock);
|
|
||||||
return available;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t clock_get_elapsed_ns() {
|
uint64_t clock_get_elapsed_ns() {
|
||||||
spin_lock(&lock);
|
return clock_read_elapsed(atomic_load(&active_clock, ATOMIC_ACQUIRE));
|
||||||
uint64_t elapsed = clock_get_elapsed_ns_locked();
|
}
|
||||||
spin_unlock(&lock);
|
|
||||||
return elapsed;
|
uint64_t clock_get_realtime_ns() {
|
||||||
|
int64_t ns = (int64_t)clock_get_elapsed_ns() + atomic_load(&realtime_offset_ns, ATOMIC_RELAXED);
|
||||||
|
return ns < 0 ? 0 : ns;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clock_set_realtime_ns(uint64_t ns) {
|
||||||
|
atomic_store(&realtime_offset_ns, (int64_t)ns - (int64_t)clock_get_elapsed_ns(), ATOMIC_RELAXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool clock_switch(struct clock* clock) {
|
bool clock_switch(struct clock* clock) {
|
||||||
spin_lock(&lock);
|
struct clock* old = atomic_load(&active_clock, ATOMIC_ACQUIRE);
|
||||||
|
|
||||||
if (active_clock) {
|
do {
|
||||||
if (clock->priority <= active_clock->priority) {
|
if (old && clock->priority <= old->priority)
|
||||||
spin_unlock(&lock);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t elapsed = clock_get_elapsed_ns_locked();
|
uint64_t elapsed = clock_read_elapsed(old);
|
||||||
clock->reset(clock);
|
clock->reset(clock);
|
||||||
|
clock->base = elapsed;
|
||||||
counter_base = elapsed;
|
} while (!atomic_compare_exchange(&active_clock, &old, clock, ATOMIC_RELEASE, ATOMIC_ACQUIRE));
|
||||||
active_clock = clock;
|
|
||||||
|
|
||||||
spin_unlock(&lock);
|
|
||||||
|
|
||||||
log("Switching to clock source \"%s\"\n", clock->name);
|
log("Switching to clock source \"%s\"\n", clock->name);
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
struct clock {
|
struct clock {
|
||||||
const char* name;
|
const char* name;
|
||||||
uint8_t priority;
|
uint8_t priority;
|
||||||
|
uint64_t base;
|
||||||
|
|
||||||
void (*reset)(struct clock* c);
|
void (*reset)(struct clock* c);
|
||||||
uint64_t (*get_elapsed_ns)(struct clock* c);
|
uint64_t (*get_elapsed_ns)(struct clock* c);
|
||||||
@@ -19,5 +20,9 @@ bool clock_available();
|
|||||||
// Gets the current timestamp in nanoseconds since the last reset.
|
// Gets the current timestamp in nanoseconds since the last reset.
|
||||||
uint64_t clock_get_elapsed_ns();
|
uint64_t clock_get_elapsed_ns();
|
||||||
|
|
||||||
|
uint64_t clock_get_realtime_ns();
|
||||||
|
|
||||||
|
void clock_set_realtime_ns(uint64_t ns);
|
||||||
|
|
||||||
// Spin for `ns` nanoseconds.
|
// Spin for `ns` nanoseconds.
|
||||||
void clock_spin_ns(uint64_t ns);
|
void clock_spin_ns(uint64_t ns);
|
||||||
|
|||||||
Reference in New Issue
Block a user