Everett
Loading...
Searching...
No Matches
runtime_registry.h
Go to the documentation of this file.
1
12#pragma once
13
14#include <everett/sections.h>
15
16#include <memory>
17#include <type_traits>
18#include <unordered_map>
19#include <utility>
20#include <vector>
21
24 void operator()(bool, bool) const noexcept {}
25 };
26
27 // Counts registry roots and immediate edges, separately from shared_ptr's
28 // ownership. A live owner has acquired its children exactly once. No owner
29 // points back to this registry; retaining it costs only its current closure.
30 template <class Node, class Visit = ignore_registry_visits> struct runtime_registry {
31 using pair_type = typename Node::pair_type;
32 using native_pointer = typename Node::native_pointer;
33 using native_root = std::pair<object_id, native_pointer>;
34 explicit runtime_registry(Visit visit = {}) : visit_(std::move(visit)) {
35 static_assert(noexcept(std::declval<Visit &>()(false, false)));
36 static_assert(std::is_nothrow_move_constructible_v<Visit> && std::is_nothrow_move_assignable_v<Visit>);
37 }
40 runtime_registry(runtime_registry && other) noexcept : visit_(std::move(other.visit_)) { swap_roots(other); }
42 if (this != &other) { clear(); visit_ = std::move(other.visit_); swap_roots(other); }
43 return *this;
44 }
46
47 pair_type pair(blob_identity const & id) const {
48 auto found = pairs_.find(id.index.hex());
49 if (found == pairs_.end()) return {};
50 if (found->second.value->mapped()->identity() != id)
51 throw std::invalid_argument("checkpoint index has another native identity");
52 return found->second.value;
53 }
54 native_pointer native(object_id const & id) const {
55 auto found = natives_.find(id.hex());
56 return found == natives_.end() ? native_pointer{} : found->second.value;
57 }
58 std::size_t pair_count() const noexcept { return pairs_.size(); }
59 std::size_t native_count() const noexcept { return natives_.size(); }
60
61 // Failed acquisition rolls back every increment and leaves the previous
62 // authorized closure unchanged. Only after all roots are coherent do we
63 // retire the old roots. Callers decode against exactly this new closure.
64 bool replace(std::vector<pair_type> pairs, std::vector<native_root> natives) {
65 std::size_t p = 0, n = 0;
66 auto rollback = [&]() noexcept {
67 while (n) release_native(natives[--n].first);
68 while (p) release_pair(pairs[--p]->mapped()->identity());
69 };
70 try {
71 for (; p != pairs.size(); ++p) if (!acquire_pair(pairs[p])) { rollback(); return false; }
72 for (; n != natives.size(); ++n) if (!acquire_native(natives[n].first, natives[n].second)) { rollback(); return false; }
73 } catch (...) { rollback(); throw; }
74 for (auto const & old : native_roots_) release_native(old.first);
75 for (auto const & old : pair_roots_) release_pair(old->mapped()->identity());
76 pair_roots_ = std::move(pairs); native_roots_ = std::move(natives);
78 return true;
79 }
80 void clear() noexcept {
81 for (auto const & old : native_roots_) release_native(old.first);
82 for (auto const & old : pair_roots_) release_pair(old->mapped()->identity());
83 native_roots_.clear(); pair_roots_.clear();
84 decltype(pairs_){}.swap(pairs_); decltype(natives_){}.swap(natives_);
85 decltype(pair_roots_){}.swap(pair_roots_); decltype(native_roots_){}.swap(native_roots_);
86 }
87
88 private:
89 template <class T> struct entry { T value; std::size_t references = 1; };
90 std::unordered_map<std::string, entry<pair_type>> pairs_;
91 std::unordered_map<std::string, entry<native_pointer>> natives_;
92 std::vector<pair_type> pair_roots_;
93 std::vector<native_root> native_roots_;
94 [[no_unique_address]] Visit visit_;
95 template <class Map> static void trim(Map & map) noexcept {
96 // Geometric shrinkage pays for rebuilding buckets with the retired
97 // entries. Allocation failure may retain spare buckets, never owners.
98 if (map.bucket_count() > 64 && map.size() < map.bucket_count() / 4) {
99 try { map.rehash(map.size() * 2 + 1); }
100 catch (std::bad_alloc const &) {}
101 }
102 }
103 void swap_roots(runtime_registry & other) noexcept {
104 pairs_.swap(other.pairs_); natives_.swap(other.natives_);
105 pair_roots_.swap(other.pair_roots_); native_roots_.swap(other.native_roots_);
106 }
107 bool acquire_native(object_id const & id, native_pointer const & value) {
108 auto [found, inserted] = natives_.try_emplace(id.hex(), entry<native_pointer>{value});
109 if (!inserted) {
110 if (found->second.value != value) return false;
111 ++found->second.references;
112 } else visit_(false, true);
113 return true;
114 }
115 bool acquire_pair(pair_type const & value) {
116 auto const & id = value->mapped()->identity();
117 auto [found, inserted] = pairs_.try_emplace(id.index.hex(), entry<pair_type>{value});
118 if (!inserted) {
119 if (found->second.value != value || found->second.value->mapped()->identity() != id) return false;
120 ++found->second.references; return true;
121 }
122 // Recursive insertion can rehash the maps; retain identities and owners,
123 // never iterators into those maps, across the child acquisitions.
124 auto main = value->main_target(); auto secondary = value->secondary_target();
125 bool native_live = false, main_live = false, secondary_live = false;
126 auto rollback = [&]() noexcept {
127 pairs_.erase(id.index.hex());
128 if (secondary_live) release_native(*value->mapped()->index_object()->secondary_id());
129 if (main_live) release_pair(main->mapped()->identity());
130 if (native_live) release_native(id.native);
131 };
132 try {
133 native_live = acquire_native(id.native, value->native_owner());
134 if (!native_live) { rollback(); return false; }
135 if (main && !(main_live = acquire_pair(main))) { rollback(); return false; }
136 if (secondary && !(secondary_live = acquire_native(*value->mapped()->index_object()->secondary_id(), secondary))) {
137 rollback(); return false;
138 }
139 } catch (...) { rollback(); throw; }
140 visit_(true, true); return true;
141 }
142 void release_native(object_id const & id) noexcept {
143 auto found = natives_.find(id.hex());
144 if (!--found->second.references) { natives_.erase(found); visit_(false, false); }
145 }
146 void release_pair(blob_identity const & id) noexcept {
147 auto found = pairs_.find(id.index.hex());
148 if (--found->second.references) return;
149 auto value = std::move(found->second.value);
150 pairs_.erase(found); visit_(true, false);
151 if (auto secondary = value->secondary_target())
152 release_native(*value->mapped()->index_object()->secondary_id());
153 if (auto main = value->main_target()) release_pair(main->mapped()->identity());
154 release_native(value->mapped()->identity().native);
155 }
156 };
157}
Definition runtime_checkpoint.h:23
Encodes portable blob sections and retains mmap-backed profile directories.
Definition sections.h:34
Definition object_path.h:38
void operator()(bool, bool) const noexcept
Definition runtime_registry.h:24
std::size_t references
Definition runtime_registry.h:89
Definition runtime_registry.h:30
void clear() noexcept
Definition runtime_registry.h:80
runtime_registry(runtime_registry &&other) noexcept
Definition runtime_registry.h:40
std::vector< pair_type > pair_roots_
Definition runtime_registry.h:92
void release_pair(blob_identity const &id) noexcept
Definition runtime_registry.h:146
runtime_registry & operator=(runtime_registry &&other) noexcept
Definition runtime_registry.h:41
typename Node::pair_type pair_type
Definition runtime_registry.h:31
std::size_t pair_count() const noexcept
Definition runtime_registry.h:58
native_pointer native(object_id const &id) const
Definition runtime_registry.h:54
pair_type pair(blob_identity const &id) const
Definition runtime_registry.h:47
runtime_registry(runtime_registry const &)=delete
typename Node::native_pointer native_pointer
Definition runtime_registry.h:32
static void trim(Map &map) noexcept
Definition runtime_registry.h:95
std::pair< object_id, native_pointer > native_root
Definition runtime_registry.h:33
void release_native(object_id const &id) noexcept
Definition runtime_registry.h:142
std::unordered_map< std::string, entry< pair_type > > pairs_
Definition runtime_registry.h:90
Visit visit_
Definition runtime_registry.h:94
std::unordered_map< std::string, entry< native_pointer > > natives_
Definition runtime_registry.h:91
bool acquire_native(object_id const &id, native_pointer const &value)
Definition runtime_registry.h:107
~runtime_registry()
Definition runtime_registry.h:45
runtime_registry & operator=(runtime_registry const &)=delete
bool acquire_pair(pair_type const &value)
Definition runtime_registry.h:115
std::size_t native_count() const noexcept
Definition runtime_registry.h:59
std::vector< native_root > native_roots_
Definition runtime_registry.h:93
runtime_registry(Visit visit={})
Definition runtime_registry.h:34
void swap_roots(runtime_registry &other) noexcept
Definition runtime_registry.h:103
bool replace(std::vector< pair_type > pairs, std::vector< native_root > natives)
Definition runtime_registry.h:64