blob: 0b93020e9c4d982cdc30a398c8ae3101811f27b5 [file] [log] [blame]
// Copyright 2023 Google Inc. All Rights Reserved.
//
// 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
//
// http://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.
#ifndef NINJA_ASYNC_LOOP_TIMERS_H
#define NINJA_ASYNC_LOOP_TIMERS_H
#include <algorithm>
#include <vector>
#include "async_loop.h"
class AsyncTimer::State {
public:
State(AsyncLoop& async_loop, AsyncTimer::Callback&& callback)
: async_loop_(async_loop), callback_(std::move(callback)) {
async_loop_.AttachTimer(this);
}
~State() { async_loop_.DetachTimer(this); }
AsyncLoop& async_loop() const { return async_loop_; }
// Return true if this instance is active.
bool active() const { return expiration_ms_ >= 0; }
int64_t expiration_ms() const { return expiration_ms_; }
void ResetCallback(AsyncTimer::Callback&& callback) {
callback_ = std::move(callback);
}
void SetExpirationMs(int64_t expiration_ms) {
expiration_ms_ = expiration_ms;
async_loop_.UpdateTimer(this);
}
void SetDurationMs(int64_t duration_ms) {
SetExpirationMs(async_loop_.NowMs() + duration_ms);
}
void Cancel() { SetExpirationMs(-1); }
void Invoke() {
expiration_ms_ = -1;
callback_();
}
private:
AsyncLoop& async_loop_;
int64_t expiration_ms_ = -1;
AsyncTimer::Callback callback_;
};
/// Common class used by all AsyncLoop implementation to manage timers.
class AsyncLoopTimers {
public:
void AttachTimer(AsyncTimer::State* timer) { timers_.push_back(timer); }
void DetachTimer(AsyncTimer::State* timer) {
pending_.erase(timer);
timers_.erase(timer);
}
void UpdateTimer(AsyncTimer::State* timer) { pending_.erase(timer); }
/// Compute the next expiration time in milliseconds, or return -1 if
/// not timer is active.
int64_t ComputeNextExpiration() const {
int64_t result = -1;
for (const auto* timer : timers_) {
if (!timer->active())
continue;
if (result < 0)
result = timer->expiration_ms();
else if (result > timer->expiration_ms())
result = timer->expiration_ms();
}
return result;
}
/// Compute the set of pending timers at a given time.
/// Return true if any timer expired, false otherwise.
bool ProcessExpiration(int64_t now_ms) {
// First compute the list of pending timers.
pending_.clear();
for (auto* timer : timers_) {
if (timer->active() && timer->expiration_ms() <= now_ms)
pending_.push_back(timer);
}
if (pending_.empty())
return false;
// Then process it. Each callback can end up calling ResetExpiration()
// or Destroy() which will remove or deactivate pending timers so do
// not assume that each id in the list is valid on each iteration.
do {
AsyncTimer::State* state = pending_.back();
pending_.pop_back();
state->Invoke();
} while (!pending_.empty());
return true;
}
// Return true if there are no timers in this set.
bool empty() const { return timers_.empty(); }
private:
struct TimerList : public std::vector<AsyncTimer::State*> {
void erase(AsyncTimer::State* state) {
auto it = std::find(begin(), end(), state);
if (it != end()) {
*it = back();
pop_back();
}
}
};
TimerList timers_;
TimerList pending_;
};
#endif // NINJA_ASYNC_LOOP_TIMERS_H