Haio 0.2.X
Homebrew Asset IO/CDN
Loading...
Searching...
No Matches
deduplication.cpp
Go to the documentation of this file.
1#include <cache/cache.hpp>
2
3#include <boost/asio/as_tuple.hpp>
4#include <boost/asio/steady_timer.hpp>
5#include <boost/asio/use_awaitable.hpp>
6
7namespace asio = boost::asio;
8
9namespace Haio::Cdn::Detail {
10
11/**
12 * the answer lives here rather than being read back from a store, so waiters are
13 * served even when nothing is being stored.
14 */
16 std::shared_ptr<asio::steady_timer> timer;
17 std::optional<Result<CacheEntry>> answer;
18};
19
20SingleFlight::SingleFlight(asio::any_io_executor executor) : executor_(std::move(executor)) {}
21
22/**
23 * the map carries no lock because the cdn runs one io_context on one thread, so a
24 * coroutine only ever yields at a co_await and never mid update. putting the server
25 * on a thread pool means revisiting exactly this.
26 */
27asio::awaitable<Result<CacheEntry>> SingleFlight::run(const std::string& key, const Producer& produce) {
28 if (const auto waiting = inFlight_.find(key); waiting != inFlight_.end()) {
29 auto pending = waiting->second;
30 co_await pending->timer->async_wait(asio::as_tuple(asio::use_awaitable));
31 if (pending->answer) co_return *pending->answer;
32 // the leader left without an answer, so this caller does the work instead
33 }
34
35 auto pending = std::make_shared<Pending>();
36 pending->timer = std::make_shared<asio::steady_timer>(executor_, asio::steady_timer::time_point::max());
37 inFlight_.emplace(key, pending);
38
39 /**
40 * the timer has to be cancelled however this ends, including by an exception, or
41 * every waiter parks forever. the answer is written before the wake, so a waiter
42 * that runs the instant it is cancelled already has something to read.
43 */
44 struct Wake {
45 SingleFlight& owner;
46 std::string key;
47 std::shared_ptr<Pending> pending;
48 ~Wake() {
49 owner.inFlight_.erase(key);
50 pending->timer->cancel();
51 }
52 } wake{*this, key, pending};
53
54 auto made = co_await produce();
55 pending->answer = made;
56 co_return made;
57}
58
59}
SingleFlight(boost::asio::any_io_executor executor)
std::optional< Result< CacheEntry > > answer
boost::asio::awaitable< Result< CacheEntry > > run(const std::string &key, const Producer &produce)
std::shared_ptr< asio::steady_timer > timer
std::function< boost::asio::awaitable< Result< CacheEntry > >()> Producer
Definition cache.hpp:25