blob: c146301d6fa0a0ef284ea807b374a4a4a9d65de3 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/cobalt/bin/app/timer_manager.h"
#include <lib/async/cpp/time.h>
#include <iostream>
#include <ostream>
#include <thread>
namespace cobalt {
using fuchsia::cobalt::Status;
using std::string;
const uint32_t kMaxTimerTimeout = 300;
void TimerVal::AddStart(uint32_t metric_id, uint32_t event_code, const std::string& component,
uint32_t encoding_id, int64_t timestamp) {
this->metric_id = metric_id;
this->encoding_id = encoding_id;
this->event_code = event_code;
this->component = component;
this->start_timestamp = timestamp;
}
void TimerVal::AddEnd(int64_t timestamp, const std::string& part_name) {
this->end_timestamp = timestamp;
this->part_name = std::move(part_name);
}
TimerManager::TimerManager(async_dispatcher_t* dispatcher) : dispatcher_(dispatcher) {}
TimerManager::~TimerManager() {}
bool TimerManager::isReady(const std::unique_ptr<TimerVal>& timer_val_ptr) {
if (!timer_val_ptr) {
return false;
}
FX_DCHECK(timer_val_ptr->start_timestamp > 0 && timer_val_ptr->end_timestamp > 0)
<< "Incomplete timer was returned.";
return true;
}
bool TimerManager::isValidTimerArguments(fidl::StringPtr timer_id, int64_t timestamp,
uint32_t timeout_s) {
if (!timer_id.has_value() || timer_id.value().empty()) {
FX_DLOGS(ERROR) << "Invalid timer_id.";
return false;
}
if (timestamp <= 0) {
FX_DLOGS(ERROR) << "Invalid timestamp.";
return false;
}
if (timeout_s <= 0 || timeout_s > kMaxTimerTimeout) {
FX_DLOGS(ERROR) << "Invalid timeout_s.";
return false;
}
return true;
}
Status TimerManager::GetTimerValWithStart(uint32_t metric_id, uint32_t event_code,
const std::string& component, uint32_t encoding_id,
const std::string& timer_id, int64_t timestamp,
uint32_t timeout_s,
std::unique_ptr<TimerVal>* timer_val_ptr) {
if (!timer_val_ptr || !isValidTimerArguments(timer_id, timestamp, timeout_s)) {
return Status::INVALID_ARGUMENTS;
}
timer_val_ptr->reset();
auto timer_val_iter = timer_values_.find(timer_id);
// An expired timer with that timer_id exists.
if (timer_val_iter != timer_values_.end() &&
timer_val_iter->second->expiry_time < async::Now(dispatcher_)) {
timer_values_.erase(timer_val_iter);
timer_val_iter = timer_values_.end();
}
// No timer with the timer_id
if (timer_val_iter == timer_values_.end()) {
auto& timer = timer_values_[timer_id];
timer.reset(new TimerVal());
timer->AddStart(metric_id, event_code, component, encoding_id, timestamp);
ScheduleExpiryTask(timer_id, timeout_s, &timer);
return Status::OK;
}
// A valid start to a timer already exists with that timer_id.
if (timer_val_iter->second->start_timestamp > 0) {
timer_values_.erase(timer_id);
return Status::INVALID_ARGUMENTS;
}
// Return TimerVal with start_timestamp and end_timestamp.
timer_val_iter->second->AddStart(metric_id, event_code, component, encoding_id, timestamp);
MoveTimerToTimerVal(&timer_val_iter, timer_val_ptr);
return Status::OK;
}
Status TimerManager::GetTimerValWithEnd(const std::string& timer_id, int64_t timestamp,
uint32_t timeout_s,
std::unique_ptr<TimerVal>* timer_val_ptr) {
if (!timer_val_ptr || !isValidTimerArguments(timer_id, timestamp, timeout_s)) {
return Status::INVALID_ARGUMENTS;
}
timer_val_ptr->reset();
auto timer_val_iter = timer_values_.find(timer_id);
// An expired timer with that timer_id exists.
if (timer_val_iter != timer_values_.end() &&
timer_val_iter->second->expiry_time < async::Now(dispatcher_)) {
timer_values_.erase(timer_val_iter);
timer_val_iter = timer_values_.end();
}
// No timer with the timer_id.
if (timer_val_iter == timer_values_.end()) {
auto& timer = timer_values_[timer_id];
timer.reset(new TimerVal());
timer->end_timestamp = timestamp;
ScheduleExpiryTask(timer_id, timeout_s, &timer);
return Status::OK;
}
// A valid end to a timer already exists with that timer_id.
if (timer_val_iter->second->end_timestamp > 0) {
timer_values_.erase(timer_val_iter);
return Status::INVALID_ARGUMENTS;
}
// Return TimerVal with start_timestamp and end_timestamp.
timer_val_iter->second->end_timestamp = timestamp;
MoveTimerToTimerVal(&timer_val_iter, timer_val_ptr);
return Status::OK;
}
Status TimerManager::GetTimerValWithEnd(const std::string& timer_id, int64_t timestamp,
uint32_t timeout_s, const std::string& part_name,
std::unique_ptr<TimerVal>* timer_val_ptr) {
if (!timer_val_ptr || !isValidTimerArguments(timer_id, timestamp, timeout_s)) {
return Status::INVALID_ARGUMENTS;
}
timer_val_ptr->reset();
auto timer_val_iter = timer_values_.find(timer_id);
// An expired timer with that timer_id exists.
if (timer_val_iter != timer_values_.end() &&
timer_val_iter->second->expiry_time < async::Now(dispatcher_)) {
timer_values_.erase(timer_val_iter);
timer_val_iter = timer_values_.end();
}
// No timer with the timer_id.
if (timer_val_iter == timer_values_.end()) {
auto& timer = timer_values_[timer_id];
timer.reset(new TimerVal());
ScheduleExpiryTask(timer_id, timeout_s, &timer);
return Status::OK;
}
// A valid end to a timer already exists with that timer_id.
if (timer_val_iter->second->end_timestamp > 0) {
timer_values_.erase(timer_val_iter);
return Status::INVALID_ARGUMENTS;
}
// Return TimerVal with start_timestamp and end_timestamp.
MoveTimerToTimerVal(&timer_val_iter, timer_val_ptr);
return Status::OK;
}
void TimerManager::MoveTimerToTimerVal(
std::unordered_map<std::string, std::unique_ptr<TimerVal>>::iterator* timer_val_iter,
std::unique_ptr<TimerVal>* timer_val_ptr) {
zx_status_t status = (*timer_val_iter)->second->expiry_task.Cancel();
if (status != ZX_OK & status != ZX_ERR_BAD_STATE) {
FX_DLOGS(ERROR) << "Failed to cancel task: status = " << status;
}
*timer_val_ptr = std::move((*timer_val_iter)->second);
timer_values_.erase(*timer_val_iter);
}
void TimerManager::ScheduleExpiryTask(const std::string& timer_id, uint32_t timeout_s,
std::unique_ptr<TimerVal>* timer_val_ptr) {
(*timer_val_ptr)->expiry_time = async::Now(dispatcher_) + zx::sec(timeout_s);
(*timer_val_ptr)
->expiry_task.set_handler([this, timer_id, me = &((*timer_val_ptr)->expiry_task)] {
auto timer_val_iter = timer_values_.find(timer_id);
FX_DCHECK(&(timer_val_iter->second->expiry_task) == me);
timer_values_.erase(timer_val_iter);
});
zx_status_t status = (*timer_val_ptr)->expiry_task.PostDelayed(dispatcher_, zx::sec(timeout_s));
if (status != ZX_OK & status != ZX_ERR_BAD_STATE) {
FX_DLOGS(ERROR) << "Failed to post task: status = " << status;
}
}
} // namespace cobalt