blob: 9a2718e88efb8170514aac1a79cc625c36d15912 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors
//
// 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_LOOP_LIMITER_H_
#define ZIRCON_KERNEL_INCLUDE_KERNEL_LOOP_LIMITER_H_
#include <lib/affine/ratio.h>
#include <zircon/types.h>
#include <cstdint>
#include <ktl/move.h>
#include <platform/timer.h>
// LoopLimiter is used to detect when a thread is looping for "too long".
//
// Example usage:
//
// // Make sure we spend no more than 30,000 nanoseconds in the loop.
// auto limiter = LoopLimiter<>::WithDuration(30000);
// while (!limiter.Exceeded()) {
// ...
// }
//
// Because getting the current ticks may be expensive in some virtualized
// environments, the template parameter |ItersPerGetTicks| controls how often
// |current_ticks| is called. For example:
//
// // Make sure we spend no more than 30,000 nanoseconds in the loop, but
// // don't call current_ticks() more than once every 1,000 loop iterations.
// auto limiter = LoopLimiter<1000>::WithDuration(30000);
// while (!limiter.Exceeded()) {
// ...
// }
//
// An |ItersPerGetTicks| value of 1 means call |current_ticks| for each
// invocation of |Exceeded|.
template <uint64_t ItersPerGetTicks>
class LoopLimiter {
public:
static_assert(ItersPerGetTicks > 0);
// Construct a limiter with a relative timeout.
//
// If |duration| is <= 0 |Exceeded| will always return false.
static LoopLimiter<ItersPerGetTicks> WithDuration(zx_duration_t duration) {
const zx_ticks_t relative_ticks = platform_get_ticks_to_time_ratio().Inverse().Scale(duration);
return LoopLimiter<ItersPerGetTicks>(relative_ticks);
}
// Returns true if the timeout has been exceeded.
//
// Call once per loop iteration.
bool Exceeded() {
if constexpr (ItersPerGetTicks <= 1) {
const zx_ticks_t now = current_ticks();
return now >= deadline_ticks_;
}
++iter_since_;
if (iter_since_ >= ItersPerGetTicks) {
iter_since_ = 0;
const zx_ticks_t now = current_ticks();
return now >= deadline_ticks_;
}
return false;
}
private:
explicit LoopLimiter(zx_ticks_t relative_ticks) {
if (relative_ticks > 0) {
const zx_ticks_t now = current_ticks();
deadline_ticks_ = zx_ticks_add_ticks(now, relative_ticks);
}
}
// Absolute deadline measured in monotonic clock ticks.
zx_ticks_t deadline_ticks_{};
// Number of iterations since the last call to |current_ticks|.
uint64_t iter_since_{};
};
#endif // ZIRCON_KERNEL_INCLUDE_KERNEL_LOOP_LIMITER_H_