1// Copyright 2010 The Kyua Authors. 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are 6// met: 7// 8// * Redistributions of source code must retain the above copyright 9// notice, this list of conditions and the following disclaimer. 10// * Redistributions in binary form must reproduce the above copyright 11// notice, this list of conditions and the following disclaimer in the 12// documentation and/or other materials provided with the distribution. 13// * Neither the name of Google Inc. nor the names of its contributors 14// may be used to endorse or promote products derived from this software 15// without specific prior written permission. 16// 17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29#include "utils/signals/timer.hpp" 30 31extern "C" { 32#include <signal.h> 33#include <unistd.h> 34} 35 36#include <cstddef> 37#include <iostream> 38#include <vector> 39 40#include <atf-c++.hpp> 41 42#include "utils/datetime.hpp" 43#include "utils/defs.hpp" 44#include "utils/format/containers.ipp" 45#include "utils/format/macros.hpp" 46#include "utils/signals/interrupts.hpp" 47#include "utils/signals/programmer.hpp" 48 49namespace datetime = utils::datetime; 50namespace signals = utils::signals; 51 52 53namespace { 54 55 56/// A timer that inserts an element into a vector on activation. 57class delayed_inserter : public signals::timer { 58 /// Vector into which to insert the element. 59 std::vector< int >& _destination; 60 61 /// Element to insert into _destination on activation. 62 const int _item; 63 64 /// Timer activation callback. 65 void 66 callback(void) 67 { 68 signals::interrupts_inhibiter inhibiter; 69 _destination.push_back(_item); 70 } 71 72public: 73 /// Constructor. 74 /// 75 /// \param delta Time to the timer activation. 76 /// \param destination Vector into which to insert the element. 77 /// \param item Element to insert into destination on activation. 78 delayed_inserter(const datetime::delta& delta, 79 std::vector< int >& destination, const int item) : 80 signals::timer(delta), _destination(destination), _item(item) 81 { 82 } 83}; 84 85 86/// Signal handler that does nothing. 87static void 88null_handler(const int /* signo */) 89{ 90} 91 92 93/// Waits for the activation of all given timers. 94/// 95/// \param timers Pointers to all the timers to wait for. 96static void 97wait_timers(const std::vector< signals::timer* >& timers) 98{ 99 std::size_t n_fired, old_n_fired = 0; 100 do { 101 n_fired = 0; 102 for (std::vector< signals::timer* >::const_iterator 103 iter = timers.begin(); iter != timers.end(); ++iter) { 104 const signals::timer* timer = *iter; 105 if (timer->fired()) 106 ++n_fired; 107 } 108 if (old_n_fired < n_fired) { 109 std::cout << "Waiting; " << n_fired << " timers fired so far\n"; 110 old_n_fired = n_fired; 111 } 112 ::usleep(100); 113 } while (n_fired < timers.size()); 114} 115 116 117} // anonymous namespace 118 119 120ATF_TEST_CASE(program_seconds); 121ATF_TEST_CASE_HEAD(program_seconds) 122{ 123 set_md_var("timeout", "10"); 124} 125ATF_TEST_CASE_BODY(program_seconds) 126{ 127 signals::timer timer(datetime::delta(1, 0)); 128 ATF_REQUIRE(!timer.fired()); 129 while (!timer.fired()) 130 ::usleep(1000); 131} 132 133 134ATF_TEST_CASE(program_useconds); 135ATF_TEST_CASE_HEAD(program_useconds) 136{ 137 set_md_var("timeout", "10"); 138} 139ATF_TEST_CASE_BODY(program_useconds) 140{ 141 signals::timer timer(datetime::delta(0, 500000)); 142 ATF_REQUIRE(!timer.fired()); 143 while (!timer.fired()) 144 ::usleep(1000); 145} 146 147 148ATF_TEST_CASE(multiprogram_ordered); 149ATF_TEST_CASE_HEAD(multiprogram_ordered) 150{ 151 set_md_var("timeout", "20"); 152} 153ATF_TEST_CASE_BODY(multiprogram_ordered) 154{ 155 static const std::size_t n_timers = 100; 156 157 std::vector< signals::timer* > timers; 158 std::vector< int > items, exp_items; 159 160 const int initial_delay_ms = 1000000; 161 for (std::size_t i = 0; i < n_timers; ++i) { 162 exp_items.push_back(i); 163 164 timers.push_back(new delayed_inserter( 165 datetime::delta(0, initial_delay_ms + (i + 1) * 10000), 166 items, i)); 167 ATF_REQUIRE(!timers[i]->fired()); 168 } 169 170 wait_timers(timers); 171 172 ATF_REQUIRE_EQ(exp_items, items); 173} 174 175 176ATF_TEST_CASE(multiprogram_reorder_next_activations); 177ATF_TEST_CASE_HEAD(multiprogram_reorder_next_activations) 178{ 179 set_md_var("timeout", "20"); 180} 181ATF_TEST_CASE_BODY(multiprogram_reorder_next_activations) 182{ 183 std::vector< signals::timer* > timers; 184 std::vector< int > items; 185 186 // First timer with an activation in the future. 187 timers.push_back(new delayed_inserter( 188 datetime::delta(0, 100000), items, 1)); 189 ATF_REQUIRE(!timers[timers.size() - 1]->fired()); 190 191 // Timer with an activation earlier than the previous one. 192 timers.push_back(new delayed_inserter( 193 datetime::delta(0, 50000), items, 2)); 194 ATF_REQUIRE(!timers[timers.size() - 1]->fired()); 195 196 // Timer with an activation later than all others. 197 timers.push_back(new delayed_inserter( 198 datetime::delta(0, 200000), items, 3)); 199 ATF_REQUIRE(!timers[timers.size() - 1]->fired()); 200 201 // Timer with an activation in between. 202 timers.push_back(new delayed_inserter( 203 datetime::delta(0, 150000), items, 4)); 204 ATF_REQUIRE(!timers[timers.size() - 1]->fired()); 205 206 wait_timers(timers); 207 208 std::vector< int > exp_items; 209 exp_items.push_back(2); 210 exp_items.push_back(1); 211 exp_items.push_back(4); 212 exp_items.push_back(3); 213 ATF_REQUIRE_EQ(exp_items, items); 214} 215 216 217ATF_TEST_CASE(multiprogram_and_cancel_some); 218ATF_TEST_CASE_HEAD(multiprogram_and_cancel_some) 219{ 220 set_md_var("timeout", "20"); 221} 222ATF_TEST_CASE_BODY(multiprogram_and_cancel_some) 223{ 224 std::vector< signals::timer* > timers; 225 std::vector< int > items; 226 227 // First timer with an activation in the future. 228 timers.push_back(new delayed_inserter( 229 datetime::delta(0, 100000), items, 1)); 230 231 // Timer with an activation earlier than the previous one. 232 timers.push_back(new delayed_inserter( 233 datetime::delta(0, 50000), items, 2)); 234 235 // Timer with an activation later than all others. 236 timers.push_back(new delayed_inserter( 237 datetime::delta(0, 200000), items, 3)); 238 239 // Timer with an activation in between. 240 timers.push_back(new delayed_inserter( 241 datetime::delta(0, 150000), items, 4)); 242 243 // Cancel the first timer to reprogram next activation. 244 timers[1]->unprogram(); delete timers[1]; timers.erase(timers.begin() + 1); 245 246 // Cancel another timer without reprogramming next activation. 247 timers[2]->unprogram(); delete timers[2]; timers.erase(timers.begin() + 2); 248 249 wait_timers(timers); 250 251 std::vector< int > exp_items; 252 exp_items.push_back(1); 253 exp_items.push_back(3); 254 ATF_REQUIRE_EQ(exp_items, items); 255} 256 257 258ATF_TEST_CASE(multiprogram_and_expire_before_activations); 259ATF_TEST_CASE_HEAD(multiprogram_and_expire_before_activations) 260{ 261 set_md_var("timeout", "20"); 262} 263ATF_TEST_CASE_BODY(multiprogram_and_expire_before_activations) 264{ 265 std::vector< signals::timer* > timers; 266 std::vector< int > items; 267 268 { 269 signals::interrupts_inhibiter inhibiter; 270 271 // First timer with an activation in the future. 272 timers.push_back(new delayed_inserter( 273 datetime::delta(0, 100000), items, 1)); 274 ATF_REQUIRE(!timers[timers.size() - 1]->fired()); 275 276 // Timer with an activation earlier than the previous one. 277 timers.push_back(new delayed_inserter( 278 datetime::delta(0, 50000), items, 2)); 279 ATF_REQUIRE(!timers[timers.size() - 1]->fired()); 280 281 ::sleep(1); 282 283 // Timer with an activation later than all others. 284 timers.push_back(new delayed_inserter( 285 datetime::delta(0, 200000), items, 3)); 286 287 ::sleep(1); 288 } 289 290 wait_timers(timers); 291 292 std::vector< int > exp_items; 293 exp_items.push_back(2); 294 exp_items.push_back(1); 295 exp_items.push_back(3); 296 ATF_REQUIRE_EQ(exp_items, items); 297} 298 299 300ATF_TEST_CASE(expire_before_firing); 301ATF_TEST_CASE_HEAD(expire_before_firing) 302{ 303 set_md_var("timeout", "20"); 304} 305ATF_TEST_CASE_BODY(expire_before_firing) 306{ 307 std::vector< int > items; 308 309 // The code below causes a signal to go pending. Make sure we ignore it 310 // when we unblock signals. 311 signals::programmer sigalrm(SIGALRM, null_handler); 312 313 { 314 signals::interrupts_inhibiter inhibiter; 315 316 delayed_inserter* timer = new delayed_inserter( 317 datetime::delta(0, 1000), items, 1234); 318 ::sleep(1); 319 // Interrupts are inhibited so we never got a chance to execute the 320 // timer before it was destroyed. However, the handler should run 321 // regardless at some point, possibly during deletion. 322 timer->unprogram(); 323 delete timer; 324 } 325 326 std::vector< int > exp_items; 327 exp_items.push_back(1234); 328 ATF_REQUIRE_EQ(exp_items, items); 329} 330 331 332ATF_TEST_CASE(reprogram_from_scratch); 333ATF_TEST_CASE_HEAD(reprogram_from_scratch) 334{ 335 set_md_var("timeout", "20"); 336} 337ATF_TEST_CASE_BODY(reprogram_from_scratch) 338{ 339 std::vector< int > items; 340 341 delayed_inserter* timer1 = new delayed_inserter( 342 datetime::delta(0, 100000), items, 1); 343 timer1->unprogram(); delete timer1; 344 345 // All constructed timers are now dead, so the interval timer should have 346 // been reprogrammed. Let's start over. 347 348 delayed_inserter* timer2 = new delayed_inserter( 349 datetime::delta(0, 200000), items, 2); 350 while (!timer2->fired()) 351 ::usleep(1000); 352 timer2->unprogram(); delete timer2; 353 354 std::vector< int > exp_items; 355 exp_items.push_back(2); 356 ATF_REQUIRE_EQ(exp_items, items); 357} 358 359 360ATF_TEST_CASE(unprogram); 361ATF_TEST_CASE_HEAD(unprogram) 362{ 363 set_md_var("timeout", "10"); 364} 365ATF_TEST_CASE_BODY(unprogram) 366{ 367 signals::timer timer(datetime::delta(0, 500000)); 368 timer.unprogram(); 369 usleep(500000); 370 ATF_REQUIRE(!timer.fired()); 371} 372 373 374ATF_TEST_CASE(infinitesimal); 375ATF_TEST_CASE_HEAD(infinitesimal) 376{ 377 set_md_var("descr", "Ensure that the ordering in which the signal, the " 378 "timer and the global state are programmed is correct; do so " 379 "by setting an extremely small delay for the timer hoping that " 380 "it can trigger such conditions"); 381 set_md_var("timeout", "10"); 382} 383ATF_TEST_CASE_BODY(infinitesimal) 384{ 385 const std::size_t rounds = 100; 386 const std::size_t exp_good = 90; 387 388 std::size_t good = 0; 389 for (std::size_t i = 0; i < rounds; i++) { 390 signals::timer timer(datetime::delta(0, 1)); 391 392 // From the setitimer(2) documentation: 393 // 394 // Time values smaller than the resolution of the system clock are 395 // rounded up to this resolution (typically 10 milliseconds). 396 // 397 // We don't know what this resolution is but we must wait for longer 398 // than we programmed; do a rough guess and hope it is good. This may 399 // be obviously wrong and thus lead to mysterious test failures in some 400 // systems, hence why we only expect a percentage of successes below. 401 // Still, we can fail... 402 ::usleep(1000); 403 404 if (timer.fired()) 405 ++good; 406 timer.unprogram(); 407 } 408 std::cout << F("Ran %s tests, %s passed; threshold is %s\n") 409 % rounds % good % exp_good; 410 ATF_REQUIRE(good >= exp_good); 411} 412 413 414ATF_INIT_TEST_CASES(tcs) 415{ 416 ATF_ADD_TEST_CASE(tcs, program_seconds); 417 ATF_ADD_TEST_CASE(tcs, program_useconds); 418 ATF_ADD_TEST_CASE(tcs, multiprogram_ordered); 419 ATF_ADD_TEST_CASE(tcs, multiprogram_reorder_next_activations); 420 ATF_ADD_TEST_CASE(tcs, multiprogram_and_cancel_some); 421 ATF_ADD_TEST_CASE(tcs, multiprogram_and_expire_before_activations); 422 ATF_ADD_TEST_CASE(tcs, expire_before_firing); 423 ATF_ADD_TEST_CASE(tcs, reprogram_from_scratch); 424 ATF_ADD_TEST_CASE(tcs, unprogram); 425 ATF_ADD_TEST_CASE(tcs, infinitesimal); 426} 427