blob: 0ec9d5e670dc2f382b642033557eff8009bde995 [file] [log] [blame]
// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
#pragma once
#include "pw_chrono/system_clock.h"
#include "pw_chrono_backend/system_timer_native.h"
#include "pw_function/function.h"
namespace pw::chrono {
/// The `SystemTimer` allows an `ExpiryCallback` be executed at a set time in
/// the future.
///
/// The base `SystemTimer` only supports a one-shot style timer with a callback.
/// A periodic timer can be implemented by rescheduling the timer in the
/// callback through `InvokeAt(kDesiredPeriod + expired_deadline)`.
///
/// When implementing a periodic layer on top, the user should be mindful of
/// handling missed periodic callbacks. They could opt to invoke the callback
/// multiple times with the expected expired_deadline values or instead saturate
/// and invoke the callback only once with the latest expired_deadline.
///
/// The entire API is thread safe, however it is NOT always IRQ safe.
class SystemTimer {
public:
using native_handle_type = backend::NativeSystemTimerHandle;
/// The `ExpiryCallback` is either invoked from a high priority thread or an
/// interrupt.
///
/// For a given timer instance, its `ExpiryCallback` will not preempt itself.
/// This makes it appear like there is a single executor of a timer instance's
/// `ExpiryCallback`.
///
/// Ergo ExpiryCallbacks should be treated as if they are executed by an
/// interrupt, meaning:
/// - Processing inside of the callback should be kept to a minimum.
/// - Callbacks should never attempt to block.
/// - APIs which are not interrupt safe such as pw::sync::Mutex should not be
/// used!
using ExpiryCallback =
Function<void(SystemClock::time_point expired_deadline)>;
/// Constructs the SystemTimer based on the user provided
/// `pw::Function<void(SystemClock::time_point expired_deadline)>`. Note that
/// The `ExpiryCallback` is either invoked from a high priority thread or an
/// interrupt.
SystemTimer(ExpiryCallback&& callback);
/// Cancels the timer and blocks if necssary if the callback is already being
/// processed.
///
/// @b Postcondition: The expiry callback is not in progress and will not be
/// called in the future.
~SystemTimer();
SystemTimer(const SystemTimer&) = delete;
SystemTimer(SystemTimer&&) = delete;
SystemTimer& operator=(const SystemTimer&) = delete;
SystemTimer& operator=(SystemTimer&&) = delete;
/// Invokes the expiry callback as soon as possible after at least the
/// specified duration.
///
/// Scheduling a callback cancels the existing callback (if pending).
/// If the callback is already being executed while you reschedule it, it will
/// finish callback execution to completion. You are responsible for any
/// critical section locks which may be needed for timer coordination.
///
/// This is thread safe, it may not be IRQ safe.
void InvokeAfter(SystemClock::duration delay);
/// Invokes the expiry callback as soon as possible starting at the specified
/// time_point.
///
/// Scheduling a callback cancels the existing callback (if pending).
/// If the callback is already being executed while you reschedule it, it will
/// finish callback execution to completion. You are responsible for any
/// critical section locks which may be needed for timer coordination.
///
/// This is thread safe, it may not be IRQ safe.
void InvokeAt(SystemClock::time_point timestamp);
/// Cancels the software timer expiry callback if pending.
///
/// Canceling a timer which isn't scheduled does nothing.
///
/// If the callback is already being executed while you cancel it, it will
/// finish callback execution to completion. You are responsible for any
/// synchronization which is needed for thread safety.
///
/// This is thread safe, it may not be IRQ safe.
void Cancel();
native_handle_type native_handle();
private:
/// This may be a wrapper around a native type with additional members.
backend::NativeSystemTimer native_type_;
};
} // namespace pw::chrono
#include "pw_chrono_backend/system_timer_inline.h"