blob: 2d1a2620ab58ef6a3b414403546bd2b1cdf9b71f [file] [log] [blame]
David Reissea2cba82009-03-30 21:35:00 +00001/*
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 */
Mark Slee9f0c6512007-02-28 23:58:26 +000019
Roger Meier49ff8b12012-04-13 09:12:31 +000020#include <thrift/concurrency/TimerManager.h>
cyyca8af9b2019-01-11 22:13:12 +080021#include <thrift/concurrency/ThreadFactory.h>
Roger Meier49ff8b12012-04-13 09:12:31 +000022#include <thrift/concurrency/Monitor.h>
Marc Slemko8a40a762006-07-19 17:46:50 +000023
24#include <assert.h>
James E. King III71afec02019-02-02 11:22:26 -050025#include <chrono>
26#include <thread>
Marc Slemko8a40a762006-07-19 17:46:50 +000027#include <iostream>
28
Konrad Grochowski16a23a62014-11-13 15:33:38 +010029namespace apache {
30namespace thrift {
31namespace concurrency {
32namespace test {
Marc Slemko8a40a762006-07-19 17:46:50 +000033
T Jake Lucianib5e62212009-01-31 22:36:20 +000034using namespace apache::thrift::concurrency;
Marc Slemko8a40a762006-07-19 17:46:50 +000035
Marc Slemko8a40a762006-07-19 17:46:50 +000036class TimerManagerTests {
37
Konrad Grochowski16a23a62014-11-13 15:33:38 +010038public:
39 class Task : public Runnable {
40 public:
cyyf7a4ead2019-01-16 13:40:46 +080041 Task(Monitor& monitor, uint64_t timeout)
Konrad Grochowski16a23a62014-11-13 15:33:38 +010042 : _timeout(timeout),
cyybfdbd032019-01-12 14:38:28 +080043 _startTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()),
James E. King, III36200902016-10-05 14:47:18 -040044 _endTime(0),
Konrad Grochowski16a23a62014-11-13 15:33:38 +010045 _monitor(monitor),
46 _success(false),
47 _done(false) {}
Marc Slemko8a40a762006-07-19 17:46:50 +000048
Sebastian Zenker042580f2019-01-29 15:48:12 +010049 ~Task() override { std::cerr << this << std::endl; }
Marc Slemko6f038a72006-08-03 18:58:09 +000050
Sebastian Zenker042580f2019-01-29 15:48:12 +010051 void run() override {
Marc Slemko8a40a762006-07-19 17:46:50 +000052
cyybfdbd032019-01-12 14:38:28 +080053 _endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count();
James E. King, IIIdf899132016-11-12 15:16:30 -050054 _success = (_endTime - _startTime) >= _timeout;
Marc Slemko6f038a72006-08-03 18:58:09 +000055
Konrad Grochowski16a23a62014-11-13 15:33:38 +010056 {
57 Synchronized s(_monitor);
James E. King, IIIdf899132016-11-12 15:16:30 -050058 _done = true;
David Reiss96d23882007-07-26 21:10:32 +000059 _monitor.notifyAll();
Marc Slemko8a40a762006-07-19 17:46:50 +000060 }
David Reiss0c90f6f2008-02-06 22:18:40 +000061 }
Marc Slemko9f27a4e2006-07-19 20:02:22 +000062
Mark Slee9b82d272007-05-23 05:16:07 +000063 int64_t _timeout;
64 int64_t _startTime;
65 int64_t _endTime;
Marc Slemko8a40a762006-07-19 17:46:50 +000066 Monitor& _monitor;
Marc Slemko9f27a4e2006-07-19 20:02:22 +000067 bool _success;
Marc Slemko8a40a762006-07-19 17:46:50 +000068 bool _done;
69 };
70
Mark Sleef5f2be42006-09-05 21:05:31 +000071 /**
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 */
cyyf7a4ead2019-01-16 13:40:46 +080077 bool test00(uint64_t timeout = 1000LL) {
Marc Slemko8a40a762006-07-19 17:46:50 +000078
Konrad Grochowski16a23a62014-11-13 15:33:38 +010079 shared_ptr<TimerManagerTests::Task> orphanTask
80 = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, 10 * timeout));
Marc Slemko8a40a762006-07-19 17:46:50 +000081
Marc Slemko9f27a4e2006-07-19 20:02:22 +000082 {
Marc Slemko9f27a4e2006-07-19 20:02:22 +000083 TimerManager timerManager;
cyyca8af9b2019-01-11 22:13:12 +080084 timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
Marc Slemko9f27a4e2006-07-19 20:02:22 +000085 timerManager.start();
James E. King III9bea32f2018-03-16 16:07:42 -040086 if (timerManager.state() != TimerManager::STARTED) {
87 std::cerr << "timerManager is not in the STARTED state, but should be" << std::endl;
88 return false;
89 }
Marc Slemko8a40a762006-07-19 17:46:50 +000090
David Reiss52687eb2009-06-04 00:32:57 +000091 // 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;
Marc Slemko8a40a762006-07-19 17:46:50 +000094
Mark Sleef5f2be42006-09-05 21:05:31 +000095 {
96 Synchronized s(_monitor);
David Reiss96d23882007-07-26 21:10:32 +000097 timerManager.add(orphanTask, 10 * timeout);
Marc Slemko8a40a762006-07-19 17:46:50 +000098
cyyf7a4ead2019-01-16 13:40:46 +080099 std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
David Reiss52687eb2009-06-04 00:32:57 +0000100
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100101 task.reset(new TimerManagerTests::Task(_monitor, timeout));
David Reiss96d23882007-07-26 21:10:32 +0000102 timerManager.add(task, timeout);
David Reiss96d23882007-07-26 21:10:32 +0000103 _monitor.wait();
Marc Slemko9f27a4e2006-07-19 20:02:22 +0000104 }
105
James E. King III9bea32f2018-03-16 16:07:42 -0400106 if (!task->_done) {
107 std::cerr << "task is not done, but it should have executed" << std::endl;
108 return false;
109 }
Marc Slemko9f27a4e2006-07-19 20:02:22 +0000110
Marc Slemko9f27a4e2006-07-19 20:02:22 +0000111 std::cout << "\t\t\t" << (task->_success ? "Success" : "Failure") << "!" << std::endl;
Marc Slemko8a40a762006-07-19 17:46:50 +0000112 }
113
James E. King III9bea32f2018-03-16 16:07:42 -0400114 if (orphanTask->_done) {
115 std::cerr << "orphan task is done, but it should not have executed" << std::endl;
116 return false;
117 }
Marc Slemko8a40a762006-07-19 17:46:50 +0000118
Marc Slemko8a40a762006-07-19 17:46:50 +0000119 return true;
120 }
121
Francois Ferrandcc2d5582017-08-25 09:01:26 +0200122 /**
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 */
cyyf7a4ead2019-01-16 13:40:46 +0800127 bool test01(uint64_t timeout = 1000LL) {
Francois Ferrandcc2d5582017-08-25 09:01:26 +0200128 TimerManager timerManager;
cyyca8af9b2019-01-11 22:13:12 +0800129 timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
Francois Ferrandcc2d5582017-08-25 09:01:26 +0200130 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 */
cyyf7a4ead2019-01-16 13:40:46 +0800160 bool test02(uint64_t timeout = 1000LL) {
Francois Ferrandcc2d5582017-08-25 09:01:26 +0200161 TimerManager timerManager;
cyyca8af9b2019-01-11 22:13:12 +0800162 timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
Francois Ferrandcc2d5582017-08-25 09:01:26 +0200163 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
Francois Ferrand69603702017-09-11 12:09:40 +0200188 /**
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 */
cyyf7a4ead2019-01-16 13:40:46 +0800193 bool test03(uint64_t timeout = 1000LL) {
Francois Ferrand69603702017-09-11 12:09:40 +0200194 TimerManager timerManager;
cyyca8af9b2019-01-11 22:13:12 +0800195 timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
Francois Ferrand69603702017-09-11 12:09:40 +0200196 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);
Sebastian Zenker042580f2019-01-29 15:48:12 +0100220 assert(nullptr == "ERROR: This remove should send a NoSuchTaskException exception.");
Francois Ferrand69603702017-09-11 12:09:40 +0200221 } catch (NoSuchTaskException&) {
222 }
223
224 return true;
225 }
226
227 /**
James E. King III71afec02019-02-02 11:22:26 -0500228 * This test creates one task, and tries to remove it after it has expired.
Francois Ferrand69603702017-09-11 12:09:40 +0200229 */
cyyf7a4ead2019-01-16 13:40:46 +0800230 bool test04(uint64_t timeout = 1000LL) {
Francois Ferrand69603702017-09-11 12:09:40 +0200231 TimerManager timerManager;
cyyca8af9b2019-01-11 22:13:12 +0800232 timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory()));
Francois Ferrand69603702017-09-11 12:09:40 +0200233 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);
James E. King III71afec02019-02-02 11:22:26 -0500242 task.reset();
Francois Ferrand69603702017-09-11 12:09:40 +0200243
244 // Wait until the task has completed
245 _monitor.wait(timeout);
246
247 // Verify behavior when removing the expired task
James E. King III71afec02019-02-02 11:22:26 -0500248 // 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 }
Francois Ferrand69603702017-09-11 12:09:40 +0200260 }
261
262 return true;
263 }
264
Marc Slemko8a40a762006-07-19 17:46:50 +0000265 friend class TestTask;
266
267 Monitor _monitor;
268};
Marc Slemko8a40a762006-07-19 17:46:50 +0000269
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100270}
271}
272}
273} // apache::thrift::concurrency