]> git.proxmox.com Git - ceph.git/blame - ceph/src/jaegertracing/thrift/lib/cpp/test/concurrency/TimerManagerTests.h
buildsys: switch source download to quincy
[ceph.git] / ceph / src / jaegertracing / thrift / lib / cpp / test / concurrency / TimerManagerTests.h
CommitLineData
f67539c2
TL
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#include <thrift/concurrency/TimerManager.h>
21#include <thrift/concurrency/ThreadFactory.h>
22#include <thrift/concurrency/Monitor.h>
23
24#include <assert.h>
25#include <chrono>
26#include <thread>
27#include <iostream>
28
29namespace apache {
30namespace thrift {
31namespace concurrency {
32namespace test {
33
34using namespace apache::thrift::concurrency;
35
36class TimerManagerTests {
37
38public:
39 class Task : public Runnable {
40 public:
41 Task(Monitor& monitor, uint64_t timeout)
42 : _timeout(timeout),
43 _startTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()),
44 _endTime(0),
45 _monitor(monitor),
46 _success(false),
47 _done(false) {}
48
49 ~Task() override { std::cerr << this << std::endl; }
50
51 void run() override {
52
53 _endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
54 _success = (_endTime - _startTime) >= _timeout;
55
56 {
57 Synchronized s(_monitor);
58 _done = true;
59 _monitor.notifyAll();
60 }
61 }
62
63 int64_t _timeout;
64 int64_t _startTime;
65 int64_t _endTime;
66 Monitor& _monitor;
67 bool _success;
68 bool _done;
69 };
70
71 /**
72 * This test creates two tasks and waits for the first to expire within 10%
73 * of the expected expiration time. It then verifies that the timer manager
74 * properly clean up itself and the remaining orphaned timeout task when the
75 * manager goes out of scope and its destructor is called.
76 */
77 bool test00(uint64_t timeout = 1000LL) {
78
79 shared_ptr<TimerManagerTests::Task> orphanTask
80 = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, 10 * timeout));
81
82 {
83 TimerManager timerManager;
84 timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
85 timerManager.start();
86 if (timerManager.state() != TimerManager::STARTED) {
87 std::cerr << "timerManager is not in the STARTED state, but should be" << std::endl;
88 return false;
89 }
90
91 // Don't create task yet, because its constructor sets the expected completion time, and we
92 // need to delay between inserting the two tasks into the run queue.
93 shared_ptr<TimerManagerTests::Task> task;
94
95 {
96 Synchronized s(_monitor);
97 timerManager.add(orphanTask, 10 * timeout);
98
99 std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
100
101 task.reset(new TimerManagerTests::Task(_monitor, timeout));
102 timerManager.add(task, timeout);
103 _monitor.wait();
104 }
105
106 if (!task->_done) {
107 std::cerr << "task is not done, but it should have executed" << std::endl;
108 return false;
109 }
110
111 std::cout << "\t\t\t" << (task->_success ? "Success" : "Failure") << "!" << std::endl;
112 }
113
114 if (orphanTask->_done) {
115 std::cerr << "orphan task is done, but it should not have executed" << std::endl;
116 return false;
117 }
118
119 return true;
120 }
121
122 /**
123 * This test creates two tasks, removes the first one then waits for the second one. It then
124 * verifies that the timer manager properly clean up itself and the remaining orphaned timeout
125 * task when the manager goes out of scope and its destructor is called.
126 */
127 bool test01(uint64_t timeout = 1000LL) {
128 TimerManager timerManager;
129 timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
130 timerManager.start();
131 assert(timerManager.state() == TimerManager::STARTED);
132
133 Synchronized s(_monitor);
134
135 // Setup the two tasks
136 shared_ptr<TimerManagerTests::Task> taskToRemove
137 = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 2));
138 timerManager.add(taskToRemove, taskToRemove->_timeout);
139
140 shared_ptr<TimerManagerTests::Task> task
141 = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout));
142 timerManager.add(task, task->_timeout);
143
144 // Remove one task and wait until the other has completed
145 timerManager.remove(taskToRemove);
146 _monitor.wait(timeout * 2);
147
148 assert(!taskToRemove->_done);
149 assert(task->_done);
150
151 return true;
152 }
153
154 /**
155 * This test creates two tasks with the same callback and another one, then removes the two
156 * duplicated then waits for the last one. It then verifies that the timer manager properly
157 * clean up itself and the remaining orphaned timeout task when the manager goes out of scope
158 * and its destructor is called.
159 */
160 bool test02(uint64_t timeout = 1000LL) {
161 TimerManager timerManager;
162 timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
163 timerManager.start();
164 assert(timerManager.state() == TimerManager::STARTED);
165
166 Synchronized s(_monitor);
167
168 // Setup the one tasks and add it twice
169 shared_ptr<TimerManagerTests::Task> taskToRemove
170 = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 3));
171 timerManager.add(taskToRemove, taskToRemove->_timeout);
172 timerManager.add(taskToRemove, taskToRemove->_timeout * 2);
173
174 shared_ptr<TimerManagerTests::Task> task
175 = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout));
176 timerManager.add(task, task->_timeout);
177
178 // Remove the first task (e.g. two timers) and wait until the other has completed
179 timerManager.remove(taskToRemove);
180 _monitor.wait(timeout * 2);
181
182 assert(!taskToRemove->_done);
183 assert(task->_done);
184
185 return true;
186 }
187
188 /**
189 * This test creates two tasks, removes the first one then waits for the second one. It then
190 * verifies that the timer manager properly clean up itself and the remaining orphaned timeout
191 * task when the manager goes out of scope and its destructor is called.
192 */
193 bool test03(uint64_t timeout = 1000LL) {
194 TimerManager timerManager;
195 timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
196 timerManager.start();
197 assert(timerManager.state() == TimerManager::STARTED);
198
199 Synchronized s(_monitor);
200
201 // Setup the two tasks
202 shared_ptr<TimerManagerTests::Task> taskToRemove
203 = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 2));
204 TimerManager::Timer timer = timerManager.add(taskToRemove, taskToRemove->_timeout);
205
206 shared_ptr<TimerManagerTests::Task> task
207 = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout));
208 timerManager.add(task, task->_timeout);
209
210 // Remove one task and wait until the other has completed
211 timerManager.remove(timer);
212 _monitor.wait(timeout * 2);
213
214 assert(!taskToRemove->_done);
215 assert(task->_done);
216
217 // Verify behavior when removing the removed task
218 try {
219 timerManager.remove(timer);
220 assert(nullptr == "ERROR: This remove should send a NoSuchTaskException exception.");
221 } catch (NoSuchTaskException&) {
222 }
223
224 return true;
225 }
226
227 /**
228 * This test creates one task, and tries to remove it after it has expired.
229 */
230 bool test04(uint64_t timeout = 1000LL) {
231 TimerManager timerManager;
232 timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
233 timerManager.start();
234 assert(timerManager.state() == TimerManager::STARTED);
235
236 Synchronized s(_monitor);
237
238 // Setup the task
239 shared_ptr<TimerManagerTests::Task> task
240 = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, timeout / 10));
241 TimerManager::Timer timer = timerManager.add(task, task->_timeout);
242 task.reset();
243
244 // Wait until the task has completed
245 _monitor.wait(timeout);
246
247 // Verify behavior when removing the expired task
248 // notify is called inside the task so the task may still
249 // be running when we get here, so we need to loop...
250 for (;;) {
251 try {
252 timerManager.remove(timer);
253 assert(nullptr == "ERROR: This remove should throw NoSuchTaskException, or UncancellableTaskException.");
254 } catch (const NoSuchTaskException&) {
255 break;
256 } catch (const UncancellableTaskException&) {
257 // the thread was still exiting; try again...
258 std::this_thread::sleep_for(std::chrono::milliseconds(1));
259 }
260 }
261
262 return true;
263 }
264
265 friend class TestTask;
266
267 Monitor _monitor;
268};
269
270}
271}
272}
273} // apache::thrift::concurrency