]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/common/test_mclock_priority_queue.cc
update sources to v12.1.1
[ceph.git] / ceph / src / test / common / test_mclock_priority_queue.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3 /*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2017 Red Hat Inc.
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15 #include <thread>
16 #include <chrono>
17 #include <iostream>
18 #include "gtest/gtest.h"
19 #include "common/mClockPriorityQueue.h"
20
21
22 struct Request {
23 int value;
24 Request() = default;
25 Request(const Request& o) = default;
26 Request(int value) :
27 value(value)
28 {}
29 };
30
31
32 struct Client {
33 int client_num;
34 Client() :
35 Client(-1)
36 {}
37 Client(int client_num) :
38 client_num(client_num)
39 {}
40 friend bool operator<(const Client& r1, const Client& r2) {
41 return r1.client_num < r2.client_num;
42 }
43 friend bool operator==(const Client& r1, const Client& r2) {
44 return r1.client_num == r2.client_num;
45 }
46 };
47
48
49 crimson::dmclock::ClientInfo client_info_func(const Client& c) {
50 static const crimson::dmclock::ClientInfo
51 the_info(10.0, 10.0, 10.0);
52 return the_info;
53 }
54
55
56 TEST(mClockPriorityQueue, Create)
57 {
58 ceph::mClockQueue<Request,Client> q(&client_info_func);
59 }
60
61
62 TEST(mClockPriorityQueue, Sizes)
63 {
64 ceph::mClockQueue<Request,Client> q(&client_info_func);
65
66 ASSERT_TRUE(q.empty());
67 ASSERT_EQ(0u, q.length());
68
69 Client c1(1);
70 Client c2(2);
71
72 q.enqueue_strict(c1, 1, Request(1));
73 q.enqueue_strict(c2, 2, Request(2));
74 q.enqueue_strict(c1, 2, Request(3));
75 q.enqueue(c2, 1, 0, Request(4));
76 q.enqueue(c1, 2, 0, Request(5));
77 q.enqueue_strict(c2, 1, Request(6));
78
79 ASSERT_FALSE(q.empty());
80 ASSERT_EQ(6u, q.length());
81
82
83 for (int i = 0; i < 6; ++i) {
84 (void) q.dequeue();
85 }
86
87 ASSERT_TRUE(q.empty());
88 ASSERT_EQ(0u, q.length());
89 }
90
91
92 TEST(mClockPriorityQueue, JustStrict)
93 {
94 ceph::mClockQueue<Request,Client> q(&client_info_func);
95
96 Client c1(1);
97 Client c2(2);
98
99 q.enqueue_strict(c1, 1, Request(1));
100 q.enqueue_strict(c2, 2, Request(2));
101 q.enqueue_strict(c1, 2, Request(3));
102 q.enqueue_strict(c2, 1, Request(4));
103
104 Request r;
105
106 r = q.dequeue();
107 ASSERT_EQ(2, r.value);
108 r = q.dequeue();
109 ASSERT_EQ(3, r.value);
110 r = q.dequeue();
111 ASSERT_EQ(1, r.value);
112 r = q.dequeue();
113 ASSERT_EQ(4, r.value);
114 }
115
116
117 TEST(mClockPriorityQueue, StrictPriorities)
118 {
119 ceph::mClockQueue<Request,Client> q(&client_info_func);
120
121 Client c1(1);
122 Client c2(2);
123
124 q.enqueue_strict(c1, 1, Request(1));
125 q.enqueue_strict(c2, 2, Request(2));
126 q.enqueue_strict(c1, 3, Request(3));
127 q.enqueue_strict(c2, 4, Request(4));
128
129 Request r;
130
131 r = q.dequeue();
132 ASSERT_EQ(4, r.value);
133 r = q.dequeue();
134 ASSERT_EQ(3, r.value);
135 r = q.dequeue();
136 ASSERT_EQ(2, r.value);
137 r = q.dequeue();
138 ASSERT_EQ(1, r.value);
139 }
140
141
142 TEST(mClockPriorityQueue, JustNotStrict)
143 {
144 ceph::mClockQueue<Request,Client> q(&client_info_func);
145
146 Client c1(1);
147 Client c2(2);
148
149 // non-strict queue ignores priorites, but will divide between
150 // clients evenly and maintain orders between clients
151 q.enqueue(c1, 1, 0, Request(1));
152 q.enqueue(c1, 2, 0, Request(2));
153 q.enqueue(c2, 3, 0, Request(3));
154 q.enqueue(c2, 4, 0, Request(4));
155
156 Request r1, r2;
157
158 r1 = q.dequeue();
159 ASSERT_TRUE(1 == r1.value || 3 == r1.value);
160
161 r2 = q.dequeue();
162 ASSERT_TRUE(1 == r2.value || 3 == r2.value);
163
164 ASSERT_NE(r1.value, r2.value);
165
166 r1 = q.dequeue();
167 ASSERT_TRUE(2 == r1.value || 4 == r1.value);
168
169 r2 = q.dequeue();
170 ASSERT_TRUE(2 == r2.value || 4 == r2.value);
171
172 ASSERT_NE(r1.value, r2.value);
173 }
174
175
176 TEST(mClockPriorityQueue, EnqueuFront)
177 {
178 ceph::mClockQueue<Request,Client> q(&client_info_func);
179
180 Client c1(1);
181 Client c2(2);
182
183 // non-strict queue ignores priorites, but will divide between
184 // clients evenly and maintain orders between clients
185 q.enqueue(c1, 1, 0, Request(1));
186 q.enqueue(c1, 2, 0, Request(2));
187 q.enqueue(c2, 3, 0, Request(3));
188 q.enqueue(c2, 4, 0, Request(4));
189 q.enqueue_strict(c2, 6, Request(6));
190 q.enqueue_strict(c1, 7, Request(7));
191
192 std::list<Request> reqs;
193
194 for (uint i = 0; i < 4; ++i) {
195 reqs.emplace_back(q.dequeue());
196 }
197
198 for (uint i = 0; i < 4; ++i) {
199 Request& r = reqs.front();
200 if (r.value > 5) {
201 q.enqueue_strict_front(r.value == 6 ? c2 : 1, r.value, r);
202 } else {
203 q.enqueue_front(r.value <= 2 ? c1 : c2, r.value, 0, r);
204 }
205 reqs.pop_front();
206 }
207
208 Request r;
209
210 r = q.dequeue();
211 ASSERT_EQ(7, r.value);
212
213 r = q.dequeue();
214 ASSERT_EQ(6, r.value);
215
216 r = q.dequeue();
217 ASSERT_TRUE(1 == r.value || 3 == r.value);
218
219 r = q.dequeue();
220 ASSERT_TRUE(1 == r.value || 3 == r.value);
221
222 r = q.dequeue();
223 ASSERT_TRUE(2 == r.value || 4 == r.value);
224
225 r = q.dequeue();
226 ASSERT_TRUE(2 == r.value || 4 == r.value);
227 }
228
229
230 TEST(mClockPriorityQueue, RemoveByClass)
231 {
232 ceph::mClockQueue<Request,Client> q(&client_info_func);
233
234 Client c1(1);
235 Client c2(2);
236 Client c3(3);
237
238 q.enqueue(c1, 1, 0, Request(1));
239 q.enqueue(c2, 1, 0, Request(2));
240 q.enqueue(c3, 1, 0, Request(4));
241 q.enqueue_strict(c1, 2, Request(8));
242 q.enqueue_strict(c2, 1, Request(16));
243 q.enqueue_strict(c3, 3, Request(32));
244 q.enqueue(c3, 1, 0, Request(64));
245 q.enqueue(c2, 1, 0, Request(128));
246 q.enqueue(c1, 1, 0, Request(256));
247
248 int out_mask = 2 | 16 | 128;
249 int in_mask = 1 | 8 | 256;
250
251 std::list<Request> out;
252 q.remove_by_class(c2, &out);
253
254 ASSERT_EQ(3u, out.size());
255 while (!out.empty()) {
256 ASSERT_TRUE((out.front().value & out_mask) > 0) <<
257 "had value that was not expected after first removal";
258 out.pop_front();
259 }
260
261 ASSERT_EQ(6u, q.length()) << "after removal of three from client c2";
262
263 q.remove_by_class(c3);
264
265 ASSERT_EQ(3u, q.length()) << "after removal of three from client c3";
266 while (!q.empty()) {
267 Request r = q.dequeue();
268 ASSERT_TRUE((r.value & in_mask) > 0) <<
269 "had value that was not expected after two removals";
270 }
271 }
272
273
274 TEST(mClockPriorityQueue, RemoveByFilter)
275 {
276 ceph::mClockQueue<Request,Client> q(&client_info_func);
277
278 Client c1(1);
279 Client c2(2);
280 Client c3(3);
281
282 q.enqueue(c1, 1, 0, Request(1));
283 q.enqueue(c2, 1, 0, Request(2));
284 q.enqueue(c3, 1, 0, Request(3));
285 q.enqueue_strict(c1, 2, Request(4));
286 q.enqueue_strict(c2, 1, Request(5));
287 q.enqueue_strict(c3, 3, Request(6));
288 q.enqueue(c3, 1, 0, Request(7));
289 q.enqueue(c2, 1, 0, Request(8));
290 q.enqueue(c1, 1, 0, Request(9));
291
292 std::list<Request> filtered;
293
294 q.remove_by_filter([&](const Request& r) -> bool {
295 if (r.value & 2) {
296 filtered.push_back(r);
297 return true;
298 } else {
299 return false;
300 }
301 });
302
303 ASSERT_EQ(4u, filtered.size()) <<
304 "filter should have removed four elements";
305 while (!filtered.empty()) {
306 ASSERT_TRUE((filtered.front().value & 2) > 0) <<
307 "expect this value to have been filtered out";
308 filtered.pop_front();
309 }
310
311 ASSERT_EQ(5u, q.length()) <<
312 "filter should have left five remaining elements";
313 while (!q.empty()) {
314 Request r = q.dequeue();
315 ASSERT_TRUE((r.value & 2) == 0) <<
316 "expect this value to have been left in";
317 }
318 }