blob: 626c205b3dfba249d0e36676c800a2b44d161628 [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
#ifndef ZIRCON_KERNEL_INCLUDE_KERNEL_TIMER_H_
#define ZIRCON_KERNEL_INCLUDE_KERNEL_TIMER_H_
#include <sys/types.h>
#include <zircon/compiler.h>
#include <zircon/listnode.h>
#include <zircon/types.h>
#include <fbl/canary.h>
#include <kernel/deadline.h>
#include <kernel/spinlock.h>
// 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.
// For now, Timers are structs with standard layout. Eventually, the list_node inside will no longer
// force that requirement, and this can become a class with private members.
struct Timer {
using Callback = void (*)(Timer*, zx_time_t now, void* arg);
static constexpr uint32_t kMagic = fbl::magic("timr");
uint32_t magic_ = kMagic;
struct list_node node_ = LIST_INITIAL_CLEARED_VALUE;
zx_time_t scheduled_time_ = 0;
// Stores the applied slack adjustment from the ideal scheduled_time.
zx_duration_t slack_ = 0;
Callback callback_ = nullptr;
void* arg_ = nullptr;
// <0 if inactive
volatile int active_cpu_ = -1;
// true if cancel is pending
volatile bool cancel_ = false;
// Timers need a constexpr constructor, as it is valid to construct them in static storage.
constexpr Timer() = default;
// We ensure that timers are not on a list or an active cpu when destroyed.
~Timer();
// Timers are not moved or copied.
Timer(const Timer&) = delete;
Timer(Timer&&) = delete;
Timer& operator=(const Timer&) = delete;
Timer& operator=(Timer&&) = delete;
//
// 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.
//
// deadline: specifies when the timer should be executed
// callback: the function to call when the timer expires
// arg: the argument to pass to the callback
//
// The timer function is declared as:
// void callback(timer_t *, zx_time_t now, void *arg) { ... }
void Set(const Deadline& deadline, 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 Cancel();
// Equivalent to Set with no slack
void SetOneshot(zx_time_t deadline, Callback callback, void* arg) {
return Set(Deadline::no_slack(deadline), callback, arg);
}
// 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.
zx_status_t TrylockOrCancel(spin_lock_t* lock) TA_TRY_ACQ(false, lock);
};
struct TimerQueue {
// Preemption Timers
//
// Each CPU has a dedicated preemption timer that's managed using specialized functions (prefixed
// with timer_preempt_).
//
// Preemption timers are different from general timers. Preemption timers:
//
// - are reset frequently by the scheduler so performance is important
// - should not be migrated off their CPU when the CPU is shutdown
//
// Note: A preemption timer may fire even after it has been canceled.
//
//
// Set/reset the current CPU's preemption timer.
//
// When the preemption timer fires, sched_preempt_timer_tick is called.
static void PreemptReset(zx_time_t deadline);
//
// Cancel the current CPU's preemption timer.
static void PreemptCancel();
// Internal routines used when bringing cpus online/offline
// Moves |old_cpu|'s timers (except its preemption timer) to the current cpu
static void TransitionOffCpu(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.
static void ThawPercpu();
};
#endif // ZIRCON_KERNEL_INCLUDE_KERNEL_TIMER_H_