diff options
author | Richard Braun <rbraun@sceen.net> | 2017-08-27 16:07:29 +0200 |
---|---|---|
committer | Richard Braun <rbraun@sceen.net> | 2017-08-27 16:08:34 +0200 |
commit | 2691b6088bddb12c4a17d08628e0af0a96b59b7f (patch) | |
tree | d17a7b9d4ccac83031f2f01ac89cc39af03eb6cd /kern/clock.h | |
parent | 094319b4a0a04ae11e24b44bb67aaf901536afb2 (diff) |
kern/clock: new module
Diffstat (limited to 'kern/clock.h')
-rw-r--r-- | kern/clock.h | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/kern/clock.h b/kern/clock.h new file mode 100644 index 00000000..30db0a82 --- /dev/null +++ b/kern/clock.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2017 Richard Braun. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * + * Timekeeping module. + */ + +#ifndef _KERN_CLOCK_H +#define _KERN_CLOCK_H + +#include <stdbool.h> +#include <stdint.h> + +#include <kern/atomic.h> +#include <kern/clock_i.h> +#include <kern/init.h> +#include <kern/macros.h> + +/* + * Clock frequency. + * + * TODO Clock frequency selection. + */ +#define CLOCK_FREQ 200 + +#if (1000 % CLOCK_FREQ) != 0 +#error "invalid clock frequency" +#endif /* (1000 % CLOCK_FREQ) != 0 */ + +/* + * Arbitrary value used to determine if a time is in the past or the future. + * + * Time is represented as 64-bits unsigned integers counting ticks. The + * global time currently starts from 0 but this isn't a strong assumption + * users should rely on. Instead, all time checks involve a time reference + * against which to compare. The result of that comparison, done by + * substraction, is either in the future, i.e. the difference is less + * than the expire threshold, or in the past, i.e. the difference is + * greater (keep in mind the result is unsigned). The threshold must be + * large enough to allow both a wide range of possible times in the future, + * but also enough time in the past for reliable timeout detection. Note + * that using signed integers would be equivalent to dividing the range + * in two (almost) equal past and future halves. + */ +#define CLOCK_EXPIRE_THRESHOLD (-(1ULL << 60)) + +static inline uint64_t +clock_get_time(void) +{ + extern union clock_global_time clock_global_time; + +#ifdef ATOMIC_HAVE_64B_OPS + + /* + * Don't enforce a stronger memory order, since : + * 1/ it's useless as long as the reader remains on the same processor + * 2/ thread migration enforces sequential consistency + */ + return atomic_load(&clock_global_time.ticks, ATOMIC_RELAXED); + +#else /* ATOMIC_HAVE_64B_OPS */ + + uint32_t high1, low, high2; + + /* + * For machines with no 64-bits atomic accessors, this implementation uses + * a variant of the two-digit monotonic-clock algorithm, described in the + * paper "Concurrent Reading and Writing of Clocks" by Leslie Lamport. + */ + + do { + high1 = atomic_load_acquire(&clock_global_time.high1); + low = atomic_load_acquire(&clock_global_time.low); + high2 = atomic_load(&clock_global_time.high2, ATOMIC_RELAXED); + } while (high1 != high2); + + return ((uint64_t)high2 << 32) | low; + +#endif /* ATOMIC_HAVE_64B_OPS */ +} + +static inline uint64_t +clock_ticks_to_ms(uint64_t ticks) +{ + return ticks * (1000 / CLOCK_FREQ); +} + +static inline uint64_t +clock_ticks_from_ms(uint64_t ms) +{ + return DIV_CEIL(ms, (1000 / CLOCK_FREQ)); +} + +static inline bool +clock_time_expired(uint64_t t, uint64_t ref) +{ + return (t - ref) > CLOCK_EXPIRE_THRESHOLD; +} + +static inline bool +clock_time_occurred(uint64_t t, uint64_t ref) +{ + return (t == ref) || clock_time_expired(t, ref); +} + +void clock_tick_intr(void); + +INIT_OP_DECLARE(clock_setup); + +#endif /* _KERN_CLOCK_H */ |