blob: b94423dcf490cb99996ce9b263435d15be9c80a4 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors
// Copyright (c) 2008-2009 Travis Geiselbrecht
//
// 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
#pragma once
#include <kernel/spinlock.h>
#include <list.h>
#include <zircon/compiler.h>
#include <sys/types.h>
__BEGIN_CDECLS
void timer_queue_init(void);
struct timer;
typedef enum handler_return (*timer_callback)(struct timer*, lk_time_t now, void* arg);
#define TIMER_MAGIC (0x74696D72) //'timr'
enum slack_mode {
TIMER_SLACK_CENTER, // slack is centered arround dealine
TIMER_SLACK_LATE, // slack interval is [deadline, dealine + slack)
TIMER_SLACK_EARLY, // slack interval is (deadline - slack, dealine]
};
typedef struct timer {
int magic;
struct list_node node;
lk_time_t scheduled_time;
int64_t slack; // Stores the applied slack adjustment from
// the ideal scheduled_time.
timer_callback callback;
void* arg;
volatile int active_cpu; // <0 if inactive
volatile bool cancel; // true if cancel is pending
} timer_t;
#define TIMER_INITIAL_VALUE(t) \
{ \
.magic = TIMER_MAGIC, \
.node = LIST_INITIAL_CLEARED_VALUE, \
.scheduled_time = 0, \
.slack = 0, \
.callback = NULL, \
.arg = NULL, \
.active_cpu = -1, \
.cancel = false, \
}
/* Rules for Timers:
* - Timer callbacks occur from interrupt context
* - Timers may be programmed or canceled from interrupt or thread context
* - Timers may be canceled or reprogrammed from within their callback
* - Setting and canceling timers is not thread safe and cannot be done concurrently
* - timer_cancel() may spin waiting for a pending timer to complete on another cpu
*/
/**
* Initialize a timer object
*/
void timer_init(timer_t*);
/**
* Set up a timer that executes once
*
* This function specifies a callback function to be run after a specified
* deadline passes. The function will be called one time.
*
* timer: the timer to use
* deadline: absolute time, in ns, after which the timer is executed
* mode: type of slack to apply, either symmetrical or one-sided to early or late
* slack: delta time in nanoseconds from |deadline| after or before is
* acceptable to execute the timer.
* callback: the function to call when the timer expires
* arg: the argument to pass to the callback
*
* The timer function is declared as:
* enum handler_return callback(struct timer *, lk_time_t now, void *arg) { ... }
*
* The |slack| parameter defines an interval depending on the |mode| in which
* is acceptable to fire the timer:
*
* - TIMER_SLACK_CENTER: |deadline - slack| to |deadline + slack|
* - TIMER_SLACK_LATE: |dealine| to |deadline + slack|
* - TIMER_SLACK_EARLY: |deadline - slack| to |deadline|
*
*/
void timer_set(timer_t* timer, lk_time_t deadline,
enum slack_mode mode, uint64_t slack, timer_callback callback, void* arg);
/**
* Cancel a pending timer
*
* Returns true if the timer was canceled before it was
* scheduled in a cpu and false otherwise or if the timer
* was not scheduled at all.
*
*/
bool timer_cancel(timer_t*);
/* Equivalent to timer_set with a slack of 0 */
static inline void timer_set_oneshot(
timer_t* timer, lk_time_t deadline, timer_callback callback, void* arg) {
return timer_set(timer, deadline, TIMER_SLACK_CENTER, 0ull, callback, arg);
}
/* Similar to timer_set_oneshot, with additional constraints:
* - Will reset a currently active timer
* - Must be called with interrupts disabled
* - Must be running on the cpu that the timer is set to fire on (if currently set)
* - Cannot be called from the timer itself
*/
/* NOTE: internal api that is needed probably only by the scheduler */
void timer_reset_oneshot_local(timer_t* timer, lk_time_t deadline, timer_callback callback, void* arg);
/* Internal routines used when bringing cpus online/offline */
/* Moves timers from |old_cpu| to the current cpu
*/
void timer_transition_off_cpu(uint old_cpu);
/* This function is to be invoked after resume on each CPU that may have
* had timers still on it, in order to restart hardware timers.
*/
void timer_thaw_percpu(void);
/* Special helper routine to simultaneously try to acquire a spinlock and check for
* timer cancel, which is needed in a few special cases.
* returns ZX_OK if spinlock was acquired, ZX_ERR_TIMED_OUT if timer was canceled.
*/
status_t timer_trylock_or_cancel(timer_t* t, spin_lock_t* lock);
__END_CDECLS