Nix 2.26.3
Nix, the purely functional package manager; unstable internal interfaces
 
Loading...
Searching...
No Matches
pool.hh
Go to the documentation of this file.
1#pragma once
3
4#include <functional>
5#include <limits>
6#include <list>
7#include <memory>
8#include <cassert>
9
10#include "sync.hh"
11#include "ref.hh"
12
13namespace nix {
14
32template <class R>
33class Pool
34{
35public:
36
40 typedef std::function<ref<R>()> Factory;
41
46 typedef std::function<bool(const ref<R> &)> Validator;
47
48private:
49
50 Factory factory;
51 Validator validator;
52
53 struct State
54 {
55 size_t inUse = 0;
56 size_t max;
57 std::vector<ref<R>> idle;
58 };
59
60 Sync<State> state;
61
62 std::condition_variable wakeup;
63
64public:
65
66 Pool(size_t max = std::numeric_limits<size_t>::max(),
67 const Factory & factory = []() { return make_ref<R>(); },
68 const Validator & validator = [](ref<R> r) { return true; })
69 : factory(factory)
70 , validator(validator)
71 {
72 auto state_(state.lock());
73 state_->max = max;
74 }
75
76 void incCapacity()
77 {
78 auto state_(state.lock());
79 state_->max++;
80 /* we could wakeup here, but this is only used when we're
81 * about to nest Pool usages, and we want to save the slot for
82 * the nested use if we can
83 */
84 }
85
86 void decCapacity()
87 {
88 auto state_(state.lock());
89 state_->max--;
90 }
91
92 ~Pool()
93 {
94 auto state_(state.lock());
95 assert(!state_->inUse);
96 state_->max = 0;
97 state_->idle.clear();
98 }
99
100 class Handle
101 {
102 private:
103 Pool & pool;
104 std::shared_ptr<R> r;
105 bool bad = false;
106
107 friend Pool;
108
109 Handle(Pool & pool, std::shared_ptr<R> r) : pool(pool), r(r) { }
110
111 public:
112 // NOTE: Copying std::shared_ptr and calling a .reset() on it is always noexcept.
113 Handle(Handle && h) noexcept
114 : pool(h.pool)
115 , r(h.r)
116 {
117 static_assert(noexcept(h.r.reset()));
118 static_assert(noexcept(std::shared_ptr(h.r)));
119 h.r.reset();
120 }
121
122 Handle(const Handle & l) = delete;
123
124 ~Handle()
125 {
126 if (!r) return;
127 {
128 auto state_(pool.state.lock());
129 if (!bad)
130 state_->idle.push_back(ref<R>(r));
131 assert(state_->inUse);
132 state_->inUse--;
133 }
134 pool.wakeup.notify_one();
135 }
136
137 R * operator -> () { return &*r; }
138 R & operator * () { return *r; }
139
140 void markBad() { bad = true; }
141 };
142
143 Handle get()
144 {
145 {
146 auto state_(state.lock());
147
148 /* If we're over the maximum number of instance, we need
149 to wait until a slot becomes available. */
150 while (state_->idle.empty() && state_->inUse >= state_->max)
151 state_.wait(wakeup);
152
153 while (!state_->idle.empty()) {
154 auto p = state_->idle.back();
155 state_->idle.pop_back();
156 if (validator(p)) {
157 state_->inUse++;
158 return Handle(*this, p);
159 }
160 }
161
162 state_->inUse++;
163 }
164
165 /* We need to create a new instance. Because that might take a
166 while, we don't hold the lock in the meantime. */
167 try {
168 Handle h(*this, factory());
169 return h;
170 } catch (...) {
171 auto state_(state.lock());
172 state_->inUse--;
173 wakeup.notify_one();
174 throw;
175 }
176 }
177
178 size_t count()
179 {
180 auto state_(state.lock());
181 return state_->idle.size() + state_->inUse;
182 }
183
184 size_t capacity()
185 {
186 return state.lock()->max;
187 }
188
189 void flushBad()
190 {
191 auto state_(state.lock());
192 std::vector<ref<R>> left;
193 for (auto & p : state_->idle)
194 if (validator(p))
195 left.push_back(p);
196 std::swap(state_->idle, left);
197 }
198};
199
200}
Definition pool.hh:101
Definition pool.hh:34
std::function< ref< R >()> Factory
Definition pool.hh:40
std::function< bool(const ref< R > &)> Validator
Definition pool.hh:46
Definition ref.hh:15
std::shared_ptr< T > p
Definition lexer.l:1269