| David Reiss | ea2cba8 | 2009-03-30 21:35:00 +0000 | [diff] [blame] | 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 |  */ | 
| Mark Slee | 9f0c651 | 2007-02-28 23:58:26 +0000 | [diff] [blame] | 19 |  | 
| Roger Meier | 49ff8b1 | 2012-04-13 09:12:31 +0000 | [diff] [blame] | 20 | #include <thrift/concurrency/TimerManager.h> | 
| cyy | ca8af9b | 2019-01-11 22:13:12 +0800 | [diff] [blame] | 21 | #include <thrift/concurrency/ThreadFactory.h> | 
| Roger Meier | 49ff8b1 | 2012-04-13 09:12:31 +0000 | [diff] [blame] | 22 | #include <thrift/concurrency/Monitor.h> | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 23 |  | 
 | 24 | #include <assert.h> | 
| James E. King III | 71afec0 | 2019-02-02 11:22:26 -0500 | [diff] [blame] | 25 | #include <chrono> | 
 | 26 | #include <thread> | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 27 | #include <iostream> | 
 | 28 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 29 | namespace apache { | 
 | 30 | namespace thrift { | 
 | 31 | namespace concurrency { | 
 | 32 | namespace test { | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 33 |  | 
| T Jake Luciani | b5e6221 | 2009-01-31 22:36:20 +0000 | [diff] [blame] | 34 | using namespace apache::thrift::concurrency; | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 35 |  | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 36 | class TimerManagerTests { | 
 | 37 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 38 | public: | 
 | 39 |   class Task : public Runnable { | 
 | 40 |   public: | 
| cyy | f7a4ead | 2019-01-16 13:40:46 +0800 | [diff] [blame] | 41 |     Task(Monitor& monitor, uint64_t timeout) | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 42 |       : _timeout(timeout), | 
| cyy | bfdbd03 | 2019-01-12 14:38:28 +0800 | [diff] [blame] | 43 |         _startTime(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count()), | 
| James E. King, III | 3620090 | 2016-10-05 14:47:18 -0400 | [diff] [blame] | 44 |         _endTime(0), | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 45 |         _monitor(monitor), | 
 | 46 |         _success(false), | 
 | 47 |         _done(false) {} | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 48 |  | 
| Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 49 |     ~Task() override { std::cerr << this << std::endl; } | 
| Marc Slemko | 6f038a7 | 2006-08-03 18:58:09 +0000 | [diff] [blame] | 50 |  | 
| Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 51 |     void run() override { | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 52 |  | 
| cyy | bfdbd03 | 2019-01-12 14:38:28 +0800 | [diff] [blame] | 53 |       _endTime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now().time_since_epoch()).count(); | 
| James E. King, III | df89913 | 2016-11-12 15:16:30 -0500 | [diff] [blame] | 54 |       _success = (_endTime - _startTime) >= _timeout; | 
| Marc Slemko | 6f038a7 | 2006-08-03 18:58:09 +0000 | [diff] [blame] | 55 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 56 |       { | 
 | 57 |         Synchronized s(_monitor); | 
| James E. King, III | df89913 | 2016-11-12 15:16:30 -0500 | [diff] [blame] | 58 |         _done = true; | 
| David Reiss | 96d2388 | 2007-07-26 21:10:32 +0000 | [diff] [blame] | 59 |         _monitor.notifyAll(); | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 60 |       } | 
| David Reiss | 0c90f6f | 2008-02-06 22:18:40 +0000 | [diff] [blame] | 61 |     } | 
| Marc Slemko | 9f27a4e | 2006-07-19 20:02:22 +0000 | [diff] [blame] | 62 |  | 
| Mark Slee | 9b82d27 | 2007-05-23 05:16:07 +0000 | [diff] [blame] | 63 |     int64_t _timeout; | 
 | 64 |     int64_t _startTime; | 
 | 65 |     int64_t _endTime; | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 66 |     Monitor& _monitor; | 
| Marc Slemko | 9f27a4e | 2006-07-19 20:02:22 +0000 | [diff] [blame] | 67 |     bool _success; | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 68 |     bool _done; | 
 | 69 |   }; | 
 | 70 |  | 
| Mark Slee | f5f2be4 | 2006-09-05 21:05:31 +0000 | [diff] [blame] | 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 |    */ | 
| cyy | f7a4ead | 2019-01-16 13:40:46 +0800 | [diff] [blame] | 77 |   bool test00(uint64_t timeout = 1000LL) { | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 78 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 79 |     shared_ptr<TimerManagerTests::Task> orphanTask | 
 | 80 |         = shared_ptr<TimerManagerTests::Task>(new TimerManagerTests::Task(_monitor, 10 * timeout)); | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 81 |  | 
| Marc Slemko | 9f27a4e | 2006-07-19 20:02:22 +0000 | [diff] [blame] | 82 |     { | 
| Marc Slemko | 9f27a4e | 2006-07-19 20:02:22 +0000 | [diff] [blame] | 83 |       TimerManager timerManager; | 
| cyy | ca8af9b | 2019-01-11 22:13:12 +0800 | [diff] [blame] | 84 |       timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory())); | 
| Marc Slemko | 9f27a4e | 2006-07-19 20:02:22 +0000 | [diff] [blame] | 85 |       timerManager.start(); | 
| James E. King III | 9bea32f | 2018-03-16 16:07:42 -0400 | [diff] [blame] | 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 |       } | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 90 |  | 
| David Reiss | 52687eb | 2009-06-04 00:32:57 +0000 | [diff] [blame] | 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; | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 94 |  | 
| Mark Slee | f5f2be4 | 2006-09-05 21:05:31 +0000 | [diff] [blame] | 95 |       { | 
 | 96 |         Synchronized s(_monitor); | 
| David Reiss | 96d2388 | 2007-07-26 21:10:32 +0000 | [diff] [blame] | 97 |         timerManager.add(orphanTask, 10 * timeout); | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 98 |  | 
| cyy | f7a4ead | 2019-01-16 13:40:46 +0800 | [diff] [blame] | 99 |         std::this_thread::sleep_for(std::chrono::milliseconds(timeout)); | 
| David Reiss | 52687eb | 2009-06-04 00:32:57 +0000 | [diff] [blame] | 100 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 101 |         task.reset(new TimerManagerTests::Task(_monitor, timeout)); | 
| David Reiss | 96d2388 | 2007-07-26 21:10:32 +0000 | [diff] [blame] | 102 |         timerManager.add(task, timeout); | 
| David Reiss | 96d2388 | 2007-07-26 21:10:32 +0000 | [diff] [blame] | 103 |         _monitor.wait(); | 
| Marc Slemko | 9f27a4e | 2006-07-19 20:02:22 +0000 | [diff] [blame] | 104 |       } | 
 | 105 |  | 
| James E. King III | 9bea32f | 2018-03-16 16:07:42 -0400 | [diff] [blame] | 106 |       if (!task->_done) { | 
 | 107 |         std::cerr << "task is not done, but it should have executed" << std::endl; | 
 | 108 |         return false; | 
 | 109 |       } | 
| Marc Slemko | 9f27a4e | 2006-07-19 20:02:22 +0000 | [diff] [blame] | 110 |  | 
| Marc Slemko | 9f27a4e | 2006-07-19 20:02:22 +0000 | [diff] [blame] | 111 |       std::cout << "\t\t\t" << (task->_success ? "Success" : "Failure") << "!" << std::endl; | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 112 |     } | 
 | 113 |  | 
| James E. King III | 9bea32f | 2018-03-16 16:07:42 -0400 | [diff] [blame] | 114 |     if (orphanTask->_done) { | 
 | 115 |       std::cerr << "orphan task is done, but it should not have executed" << std::endl; | 
 | 116 |       return false; | 
 | 117 |     } | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 118 |  | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 119 |     return true; | 
 | 120 |   } | 
 | 121 |  | 
| Francois Ferrand | cc2d558 | 2017-08-25 09:01:26 +0200 | [diff] [blame] | 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 |    */ | 
| cyy | f7a4ead | 2019-01-16 13:40:46 +0800 | [diff] [blame] | 127 |   bool test01(uint64_t timeout = 1000LL) { | 
| Francois Ferrand | cc2d558 | 2017-08-25 09:01:26 +0200 | [diff] [blame] | 128 |     TimerManager timerManager; | 
| cyy | ca8af9b | 2019-01-11 22:13:12 +0800 | [diff] [blame] | 129 |     timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory())); | 
| Francois Ferrand | cc2d558 | 2017-08-25 09:01:26 +0200 | [diff] [blame] | 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 |    */ | 
| cyy | f7a4ead | 2019-01-16 13:40:46 +0800 | [diff] [blame] | 160 |   bool test02(uint64_t timeout = 1000LL) { | 
| Francois Ferrand | cc2d558 | 2017-08-25 09:01:26 +0200 | [diff] [blame] | 161 |     TimerManager timerManager; | 
| cyy | ca8af9b | 2019-01-11 22:13:12 +0800 | [diff] [blame] | 162 |     timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory())); | 
| Francois Ferrand | cc2d558 | 2017-08-25 09:01:26 +0200 | [diff] [blame] | 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 |  | 
| Francois Ferrand | 6960370 | 2017-09-11 12:09:40 +0200 | [diff] [blame] | 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 |    */ | 
| cyy | f7a4ead | 2019-01-16 13:40:46 +0800 | [diff] [blame] | 193 |   bool test03(uint64_t timeout = 1000LL) { | 
| Francois Ferrand | 6960370 | 2017-09-11 12:09:40 +0200 | [diff] [blame] | 194 |     TimerManager timerManager; | 
| cyy | ca8af9b | 2019-01-11 22:13:12 +0800 | [diff] [blame] | 195 |     timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory())); | 
| Francois Ferrand | 6960370 | 2017-09-11 12:09:40 +0200 | [diff] [blame] | 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); | 
| Sebastian Zenker | 042580f | 2019-01-29 15:48:12 +0100 | [diff] [blame] | 220 |       assert(nullptr == "ERROR: This remove should send a NoSuchTaskException exception."); | 
| Francois Ferrand | 6960370 | 2017-09-11 12:09:40 +0200 | [diff] [blame] | 221 |     } catch (NoSuchTaskException&) { | 
 | 222 |     } | 
 | 223 |  | 
 | 224 |     return true; | 
 | 225 |   } | 
 | 226 |  | 
 | 227 |   /** | 
| James E. King III | 71afec0 | 2019-02-02 11:22:26 -0500 | [diff] [blame] | 228 |    * This test creates one task, and tries to remove it after it has expired. | 
| Francois Ferrand | 6960370 | 2017-09-11 12:09:40 +0200 | [diff] [blame] | 229 |    */ | 
| cyy | f7a4ead | 2019-01-16 13:40:46 +0800 | [diff] [blame] | 230 |   bool test04(uint64_t timeout = 1000LL) { | 
| Francois Ferrand | 6960370 | 2017-09-11 12:09:40 +0200 | [diff] [blame] | 231 |     TimerManager timerManager; | 
| cyy | ca8af9b | 2019-01-11 22:13:12 +0800 | [diff] [blame] | 232 |     timerManager.threadFactory(shared_ptr<ThreadFactory>(new ThreadFactory())); | 
| Francois Ferrand | 6960370 | 2017-09-11 12:09:40 +0200 | [diff] [blame] | 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); | 
| James E. King III | 71afec0 | 2019-02-02 11:22:26 -0500 | [diff] [blame] | 242 |     task.reset(); | 
| Francois Ferrand | 6960370 | 2017-09-11 12:09:40 +0200 | [diff] [blame] | 243 |  | 
 | 244 |     // Wait until the task has completed | 
 | 245 |     _monitor.wait(timeout); | 
 | 246 |  | 
 | 247 |     // Verify behavior when removing the expired task | 
| James E. King III | 71afec0 | 2019-02-02 11:22:26 -0500 | [diff] [blame] | 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 |       } | 
| Francois Ferrand | 6960370 | 2017-09-11 12:09:40 +0200 | [diff] [blame] | 260 |     } | 
 | 261 |  | 
 | 262 |     return true; | 
 | 263 |   } | 
 | 264 |  | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 265 |   friend class TestTask; | 
 | 266 |  | 
 | 267 |   Monitor _monitor; | 
 | 268 | }; | 
| Marc Slemko | 8a40a76 | 2006-07-19 17:46:50 +0000 | [diff] [blame] | 269 |  | 
| Konrad Grochowski | 16a23a6 | 2014-11-13 15:33:38 +0100 | [diff] [blame] | 270 | } | 
 | 271 | } | 
 | 272 | } | 
 | 273 | } // apache::thrift::concurrency |