131 lines
3.6 KiB
C++
131 lines
3.6 KiB
C++
#pragma once
|
|
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <cstddef>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
|
|
#include "manager/safety_manager/include/safety_types.h"
|
|
|
|
namespace cmvr::safety {
|
|
|
|
struct CommandKey {
|
|
std::string effective_principal_id;
|
|
std::string command_id;
|
|
|
|
bool operator==(const CommandKey& other) const noexcept
|
|
{
|
|
return effective_principal_id == other.effective_principal_id &&
|
|
command_id == other.command_id;
|
|
}
|
|
};
|
|
|
|
struct CommandOutcome {
|
|
CommandLifecycle lifecycle{CommandLifecycle::Failed};
|
|
SafetyReason reason{SafetyReason::InternalError};
|
|
std::string detail;
|
|
std::string serialized_response;
|
|
std::uint64_t safety_epoch{0};
|
|
std::uint64_t device_generation{0};
|
|
bool hardware_submission_possible{false};
|
|
};
|
|
|
|
enum class CommandReservationStatus {
|
|
AcceptedNew,
|
|
JoinedInFlight,
|
|
CachedResult,
|
|
CommandIdConflict,
|
|
ResultEvicted,
|
|
LedgerExhausted,
|
|
Invalid,
|
|
};
|
|
|
|
class CommandLedger final {
|
|
private:
|
|
struct State;
|
|
|
|
public:
|
|
struct Config {
|
|
std::size_t result_capacity{4096};
|
|
std::size_t total_id_capacity{256U * 1024U};
|
|
};
|
|
|
|
class Ticket final {
|
|
public:
|
|
Ticket() = default;
|
|
bool valid() const noexcept { return state_ != nullptr; }
|
|
|
|
private:
|
|
friend class CommandLedger;
|
|
explicit Ticket(std::shared_ptr<State> state)
|
|
: state_(std::move(state))
|
|
{
|
|
}
|
|
|
|
std::shared_ptr<State> state_;
|
|
};
|
|
|
|
struct Reservation {
|
|
CommandReservationStatus status{CommandReservationStatus::Invalid};
|
|
Ticket ticket;
|
|
std::optional<CommandOutcome> cached_outcome;
|
|
};
|
|
|
|
CommandLedger();
|
|
explicit CommandLedger(Config config);
|
|
|
|
Reservation reserve(CommandKey key, std::string payload_hash);
|
|
bool setLifecycle(const Ticket& ticket,
|
|
CommandLifecycle lifecycle,
|
|
std::uint64_t safety_epoch = 0,
|
|
std::uint64_t device_generation = 0,
|
|
bool hardware_submission_possible = false);
|
|
bool complete(const Ticket& ticket, CommandOutcome outcome);
|
|
std::optional<CommandOutcome> wait(
|
|
const Ticket& ticket,
|
|
SafetyClock::time_point deadline = SafetyClock::time_point::max()) const;
|
|
std::optional<CommandOutcome> lookup(
|
|
const CommandKey& key,
|
|
const std::string& payload_hash) const;
|
|
|
|
std::size_t acceptedIdCount() const;
|
|
std::size_t liveRecordCount() const;
|
|
std::size_t retiredIdCount() const;
|
|
|
|
private:
|
|
struct KeyHash {
|
|
std::size_t operator()(const CommandKey& key) const noexcept;
|
|
};
|
|
|
|
struct State {
|
|
CommandKey key;
|
|
std::string payload_hash;
|
|
mutable std::mutex mutex;
|
|
mutable std::condition_variable condition;
|
|
CommandLifecycle lifecycle{CommandLifecycle::Reserved};
|
|
std::optional<CommandOutcome> outcome;
|
|
std::uint64_t safety_epoch{0};
|
|
std::uint64_t device_generation{0};
|
|
bool hardware_submission_possible{false};
|
|
bool terminal{false};
|
|
};
|
|
|
|
static bool validKey_(const CommandKey& key) noexcept;
|
|
void trimTerminalResultsLocked_();
|
|
|
|
const Config config_;
|
|
mutable std::mutex mutex_;
|
|
std::unordered_map<CommandKey, std::shared_ptr<State>, KeyHash> records_;
|
|
std::unordered_map<CommandKey, std::string, KeyHash> retired_ids_;
|
|
std::vector<CommandKey> terminal_order_;
|
|
std::size_t terminal_result_count_{0};
|
|
};
|
|
|
|
const char* toString(CommandReservationStatus status) noexcept;
|
|
|
|
} // namespace cmvr::safety
|