virthttp  0.0
libvirt http interface
async_store.hpp
Go to the documentation of this file.
1 #pragma once
2 #include <chrono>
3 #include <cstddef>
4 #include <future>
5 #include <limits>
6 #include <unordered_map>
7 #include <virt_wrap/utility.hpp>
8 
9 using namespace std::literals;
10 
15 struct AsyncStore {
16  using IndexType = std::uint32_t;
17  using ClockType = std::chrono::system_clock;
18 
23  enum class TaskStatus {
24  non_existent,
25  in_progress,
26  finished,
27  };
28 
33  struct Element {
34  std::chrono::time_point<ClockType> expires;
35  std::future<std::string> fut;
36  };
37 
38  std::mutex mut{};
39  std::unordered_map<IndexType, Element> elems;
40  IndexType last_id{-1u};
41  std::chrono::seconds default_expire = 120s;
42  constexpr static std::chrono::seconds init_expire = std::chrono::seconds::max();
43 
52  template <class Fcn> std::optional<IndexType> launch(Fcn&& fcn, std::optional<std::chrono::seconds> expire_opt = std::nullopt) {
53  static_assert(std::is_same_v<std::invoke_result_t<Fcn>, std::string>);
54  if (elems.size() == std::numeric_limits<IndexType>::max())
55  return std::nullopt;
56 
57  std::lock_guard guard{mut};
58  const auto id = last_id = get_free_id();
59  auto [it, sucess] = elems.emplace(id, std::move(Element{std::chrono::time_point<ClockType>{init_expire}, {}}));
60 
61  it->second.fut = std::async(std::launch::async, [&, expire_opt, id, fcn = std::forward<Fcn>(fcn)]() -> std::string {
62  std::string ret = fcn(); // Not const for NRVO
63 
64  /* Housekeeping */
65  std::lock_guard guard{mut};
66  auto& [exp, fut /*, th*/] = elems[id];
67  exp = ClockType::now() + expire_opt.value_or(default_expire);
68 
70 
71  return ret;
72  });
73 
74  return {id};
75  }
76 
83  std::pair<TaskStatus, std::string> value_if_ready(IndexType id) {
84  std::lock_guard guard{mut};
85  const auto it = elems.find(id);
86  if (it == elems.end())
87  return {TaskStatus::non_existent, {}};
88 
89  auto& [expires, fut] = it->second;
90 
91  if (future_status(fut) != std::future_status::ready)
92  return {TaskStatus::in_progress, {}};
93 
94  auto node = elems.extract(it);
95  return {TaskStatus::finished, fut.get()};
96  }
97 
102  void gc() noexcept {
103  for (auto it = elems.begin(), last = elems.end(); it != last;) {
104  if (auto& [key, val] = *it; val.expires > ClockType::now() && future_status(val.fut) == std::future_status::ready) {
105  it = elems.erase(it);
106  } else
107  ++it;
108  }
109  }
110 
111  private:
119  template <class T> static std::future_status future_status(const std::future<T>& fut) { return fut.wait_for(std::chrono::seconds{0}); }
120 
126  IndexType get_free_id() const noexcept {
127  auto id = last_id;
128  while (elems.count(++id) > 0)
129  ;
130  return id;
131  }
132 };
std::pair< TaskStatus, std::string > value_if_ready(IndexType id)
Definition: async_store.hpp:83
std::chrono::system_clock ClockType
Type used for the expiration clock.
Definition: async_store.hpp:17
Definition: async_store.hpp:33
std::chrono::time_point< ClockType > expires
point of expiry of the store entry
Definition: async_store.hpp:34
std::unordered_map< IndexType, Element > elems
actual container of entries
Definition: async_store.hpp:39
TaskStatus
Definition: async_store.hpp:23
std::uint32_t IndexType
Type used as the key to elems.
Definition: async_store.hpp:16
void gc() noexcept
Definition: async_store.hpp:102
Definition: async_store.hpp:15
std::optional< IndexType > launch(Fcn &&fcn, std::optional< std::chrono::seconds > expire_opt=std::nullopt)
Definition: async_store.hpp:52
std::future< std::string > fut
promise of the response body
Definition: async_store.hpp:35