-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtest_resource_pool.cpp
211 lines (192 loc) · 6.32 KB
/
test_resource_pool.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include <iostream>
#include <future>
#define private public
#include "resource_pool.hpp"
#include "../mutils/extras"
using namespace mutils;
bool unique(){ return true;}
template<typename Fst, typename... Rst>
bool unique(const Fst &fst, const Rst&... rst){
auto neq = [&](auto &rst){return fst != rst;};
return forall(neq(rst)...) && unique(rst...);
}
struct AssignOnce{
int val{0};
auto operator=(int i){
assert(val == 0);
assert(i != 0);
return val = i;
}
template<typename T>
bool operator==(const T &t) const {
return val == t;
}
operator int() const {return val;}
};
void single_threaded_test(){
using LockedResource = typename ResourcePool<AssignOnce>::LockedResource;
using WeakResource = typename ResourcePool<AssignOnce>::WeakResource;
ResourcePool<AssignOnce> pool{3,2,[](){return new AssignOnce();}};
auto prefer_1 = [&](){
auto my_int = pool.acquire();
assert(*my_int == 0);
*my_int = 1;
WeakResource ret{my_int};
assert(*ret.lock() == 1);
return ret;
}();
auto prefer_2 = [&](){
auto my_int_2 = pool.acquire();
*my_int_2 = 2;
WeakResource ret{my_int_2};
assert(*ret.lock() == 2);
return ret;
}();
auto prefer_3 = [&](){
auto my_int_3 = pool.acquire();
*my_int_3 = 3;
WeakResource ret{my_int_3};
assert(*ret.lock() == 3);
return ret;
}();
assert(pool.preferred_full());
assert(*prefer_2.lock() != 0);
assert(*prefer_1.lock() != 0);
assert(*prefer_3.lock() == 3);
assert(*prefer_2.lock() == 2);
prefer_2.lock();
assert(*prefer_1.lock() == 1);
const int index_1 = prefer_1.index_preference->indx;
const int index_2 = prefer_2.index_preference->indx;
const int index_3 = prefer_3.index_preference->indx;
assert(*prefer_3.lock() == 3);
assert(*prefer_2.lock() == 2);
assert(*prefer_1.lock() == 1);
{
auto hold_shared = [&](const auto&, const auto&, const auto&){
return std::make_pair(pool.acquire(),pool.acquire());
}(prefer_1.lock(), prefer_2.lock(),prefer_3.lock());
assert(pool.preferred_full());
WeakResource orphan{[&](const auto &, const auto &, const auto&){
assert(*pool.acquire() == 0);
return pool.acquire();
}(prefer_1.lock(), prefer_2.lock(),prefer_3.lock())};
auto val = *orphan.lock();
assert(val == 1 || val == 2 || val == 3);
assert(*prefer_2.lock() == 2);
assert(*prefer_1.lock() == 1);
auto locked_orphan = orphan.lock();
assert(*locked_orphan == 1 || *locked_orphan == 2 || *locked_orphan == 3);
if (*locked_orphan == 3){
assert(*prefer_2.lock() == 2);
assert(*prefer_1.lock() == 1);
auto tmp = *prefer_3.lock();
assert(tmp == 1 || tmp == 2);
}
if (*locked_orphan == 2){
assert(*prefer_1.lock() == 1);
assert(*prefer_2.lock() == 1 || *prefer_2.lock() == 3);
assert(*prefer_3.lock() == 3);
}
if (*locked_orphan == 1){
assert(*prefer_1.lock() == 2 || *prefer_2.lock() == 3);
assert(*prefer_2.lock() == 2);
assert(*prefer_3.lock() == 3);
}
assert(*pool.acquire() != 0);
}
{
auto state = pool.dbg_leak_state();
assert(state->free_resources.size_approx() == 5);
auto hold_first_shared = [&](const auto&, const auto&, const auto&){
return pool.acquire();
}(prefer_1.lock(), prefer_2.lock(),prefer_3.lock());
*hold_first_shared = 5;
assert(state->free_resources.size_approx() == 4);
assert(ResourcePool<AssignOnce>::rented_spare::_resource_type() == hold_first_shared.which__resource_type());
WeakResource spare{[&](const auto &, const auto &, const auto&){
auto l = pool.acquire();
assert(ResourcePool<AssignOnce>::rented_spare::_resource_type() == l.which__resource_type());
assert(*l == 0);
*l = 4;
return l;
}(prefer_1.lock(), prefer_2.lock(),prefer_3.lock())};
auto val = *spare.lock();
assert(state->free_resources.size_approx() == 4);
assert(val > 0 && val <= 4);
auto locked_spare = spare.lock();
assert(ResourcePool<AssignOnce>::rented_spare::_resource_type() == locked_spare.which__resource_type()
|| ResourcePool<AssignOnce>::rented_preferred::_resource_type() == locked_spare.which__resource_type()
);
assert(*locked_spare > 0 || *locked_spare <= 4);
assert(!prefer_3.is_locked());
assert(!prefer_2.is_locked());
assert(!prefer_1.is_locked());
assert(unique(prefer_3.index_preference->indx,
prefer_2.index_preference->indx,
prefer_1.index_preference->indx
));
assert(prefer_3.index_preference->indx == index_3);
assert(prefer_2.index_preference->indx == index_2);
assert(prefer_1.index_preference->indx == index_1);
assert(*prefer_3.lock() == 3 || (*locked_spare == 3 && *prefer_3.lock() == 4));
prefer_2.lock();
assert(*prefer_2.lock() == 2 || (*locked_spare == 2 && *prefer_2.lock() == 4));
assert(*prefer_1.lock() == 1 || (*locked_spare == 1 && *prefer_1.lock() == 4));
assert(*pool.acquire() != 0);
}
WeakResource spare{[&](const auto &, const auto &, const auto&){
assert(*pool.acquire() > 3);
return pool.acquire();
}(prefer_1.lock(), prefer_2.lock(),prefer_3.lock())};
assert(*spare.lock() == 4 || *spare.lock() == 5);
}
void multi_threaded_test(){
struct Incrementor{
int i;
std::mutex m;
Incrementor(int i):i(i){}
auto incr() {
assert(m.try_lock());
std::unique_lock<std::mutex> lock{m, std::adopt_lock};
return ++i;
}
bool held{false};
void onRelease() {
assert(held);
held = false;
}
void onAcquire(int) {
assert(!held);
held = true;
}
};
using LockedResource = typename ResourcePool<Incrementor, int>::LockedResource;
using WeakResource = typename ResourcePool<Incrementor, int>::WeakResource;
ResourcePool<Incrementor, int> pool{30,10,[](int i){return new Incrementor{i};}};
std::vector<std::future<void> > futs;
for (int i = 0; i < 80; ++i){
auto acquired = pool.acquire(std::move(i));
assert(acquired->held == true);
futs.emplace_back(
std::async(std::launch::async,
[i, weak_resource = WeakResource{std::move(acquired)}]() mutable {
auto locked_resource = weak_resource.lock(std::move(i));
assert(locked_resource->held == true);
while (locked_resource->incr() < 500000);
}));
}
for (auto& fut : futs){
fut.get();
}
auto state = pool.dbg_leak_state();
std::cout << "state check: " << std::endl;
for (const auto &res : state->preferred_resources){
auto i = res.resource->i;
if (i < 500000) std::cout << i << std::endl;
}
}
int main(){
single_threaded_test();
multi_threaded_test();
}