text: // 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.
  
  #ifndef SRC_COBALT_BIN_APP_TIMER_MANAGER_H_
  #define SRC_COBALT_BIN_APP_TIMER_MANAGER_H_
  
  #include <fuchsia/cobalt/cpp/fidl.h>
  #include <lib/async/cpp/task.h>
  #include <lib/zx/time.h>
  #include <stdlib.h>
  
  #include <chrono>
  #include <memory>
  #include <mutex>
  #include <queue>
  #include <string>
  #include <unordered_map>
  
  #include "src/lib/fxl/logging.h"
  
  using fuchsia::cobalt::Status;
  
  namespace cobalt {
  
  // Used to store all necessary values for a Timer to be able to log an Event.
  struct TimerVal {
    // The metric_id of the event we will create.
    uint32_t metric_id;
    // The encoding_id used in the event we will log.
    uint32_t encoding_id;
    // The event_code for the event.
    uint32_t event_code;
    // The component for the event.
    std::string component;
    // When the timer starts.
    int64_t start_timestamp;
    // When the timer ends.
    int64_t end_timestamp;
    // The time at which the timer is expired.
    zx::time expiry_time;
    // Task which will delete the timer once it is expired.
    async::TaskClosure expiry_task;
    // The name of the timer field/part if it is a multipart obervation.
    std::string part_name;
  
    // Stores the start-related arguments in the given TimerVal.
    void AddStart(uint32_t metric_id, uint32_t event_code, const std::string& component,
                  uint32_t encoding_id, int64_t timestamp);
  
    // Stores the end-related arguments in the given TimerVal.
    void AddEnd(int64_t timestamp, const std::string& part_name);
  };
  
  // Stores partial timer values as they are encountered. Once both the start and
  // end value of the timer have been encountered the timer's values are returned
  // as a TimerVal.
  class TimerManager {
   public:
    // Constructs a TimerManager Object. Uses the given dispatcher to
    // schedule tasks which delete timer data once it has expired.
    TimerManager(async_dispatcher_t* dispatcher);
  
    // In some situations (e.g. fuzzing) the currently active dispatcher may change over time. Rather
    // than constructing a new TimerManager each time, we just update the dispatcher.
    void UpdateDispatcher(async_dispatcher_t* dispatcher) { dispatcher_ = dispatcher; };
  
    ~TimerManager();
  
    // Checks if the given TimerVal contains all the information it needs to log
    // an event. That means it was populated by both StartTimer and EndTimer
    // calls.
    static bool isReady(const std::unique_ptr<TimerVal>& timer_val_ptr);
  
    // Checks that the arguments are valid timer arguments.
    static bool isValidTimerArguments(fidl::StringPtr timer_id, int64_t timestamp,
                                      uint32_t timeout_s);
  
    // Populates the TimerVal parameter with the timer's values if there is a
    // valid timer with the timer_id. If no valid timer exists it creates a new
    // timer with the start data and resets the TimerVal ptr. If a timer with the
    // same timer_id and different start timestamp exists it returns
    // FAILED_PRECONDITION. If timer_ID or timeout_s is invalid, returns
    // INVALID_ARGUMENTS.
    Status 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);
  
    // Populates the TimerVal parameter with the timer's values if there is a
    // valid timer with the timer_id. If no valid timer exists it creates a new
    // timer with the end data and resets the TimerVal ptr. If a timer with the
    // same timer_id and different end timestamp exists it returns an error.
    Status GetTimerValWithEnd(const std::string& timer_id, int64_t timestamp, uint32_t timeout_s,
                              std::unique_ptr<TimerVal>* timer_val_ptr);
  
    // Populates the TimerVal parameter with the timer's values if there is a
    // valid timer with the timer_id. If no valid timer exists it creates a new
    // timer with the end data and resets the TimerVal ptr. If a timer with the
    // same timer_id and different end timestamp exists it returns an error.
    Status 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);
  
   private:
    // Schedules a task which will delete the timer entries associated with
    // timer_id when it expires.
    // timeout_s : the timer timer_id will be deleted after timeout_s seconds.
    // timer_val_ptr : the task will be stored in the given object until it is
    //                 executed or cancelled. Deleting the TimerVal will cancel
    //                 the task.
    void ScheduleExpiryTask(const std::string& timer_id, uint32_t timeout_s,
                            std::unique_ptr<TimerVal>* timer_val_ptr);
  
    // Copies the data found in the iterator entry to the TimerVal pointer
    // provided. It then deletes the data associated with it from the map, which
    // includes cancelling the pending expiry task.
    void MoveTimerToTimerVal(
        std::unordered_map<std::string, std::unique_ptr<TimerVal>>::iterator* timer_val_iter,
        std::unique_ptr<TimerVal>* timer_val_ptr);
  
    // Map from timer_id to the TimerVal values associated with it.
    std::unordered_map<std::string, std::unique_ptr<TimerVal>> timer_values_;
    // Async dispatcher used for deleting expired timer entries.
    async_dispatcher_t* dispatcher_;  // not owned.
  };
  
  }  // namespace cobalt
  
  #endif  // SRC_COBALT_BIN_APP_TIMER_MANAGER_H_
  