text: // This file is part of MLDB. Copyright 2015 mldb.ai inc. All rights reserved.
  
  /* asio_timer_test.cc                                              -*- C++ -*-
     Jeremy Barnes, 20 June 2014
     Copyright (c) 2014 mldb.ai inc.  All rights reserved.
  
  
  */
  
  #include "mldb/io/event_loop.h"
  #include "mldb/io/asio_timer.h"
  #include "mldb/io/asio_thread_pool.h"
  #include "mldb/watch/watch_impl.h"
  #include <boost/asio.hpp>
  #include <thread>
  #include "mldb/utils/testing/watchdog.h"
  
  #define BOOST_TEST_MAIN
  #define BOOST_TEST_DYN_LINK
  
  #include <boost/test/unit_test.hpp>
  
  using namespace std;
  using namespace MLDB;
  
  
  // Disarm from a different thread whilst it's firing; check there is no
  // deadlock
  BOOST_AUTO_TEST_CASE( test_destroy_from_handler_no_deadlock )
  {
      EventLoop eventLoop;
      AsioThreadPool threads(eventLoop, 4 /* threads */);
      getTimer(Date::now().plusSeconds(0.1), -1, eventLoop).wait();
  
      std::atomic<int> numFired(0);
  
      // Make sure the test bombs if there is a deadlock rather than hanging
      MLDB::Watchdog watchdog(5);
  
      auto timer = getTimer(Date::now().plusSeconds(0.01), -1, eventLoop);
      timer.bind([&] (Date now) {  timer = WatchT<Date>();  ++numFired;  });
  
      for (int i = 0;  i < 10 && numFired == 0;  ++i) {
          std::this_thread::sleep_for(std::chrono::milliseconds(100));
      }
  
      BOOST_CHECK_EQUAL(numFired, 1);
  }
  
  BOOST_AUTO_TEST_CASE( test_rapid_creation_destruction_1 )
  {
      EventLoop eventLoop;
      AsioThreadPool threads(eventLoop, 4 /* min threads */);
      getTimer(Date::now().plusSeconds(0.1), -1, eventLoop).wait();
  
      Date future = Date::now().plusSeconds(1);
  
      std::atomic<int> numFired(0);
  
      for (unsigned i = 0;  i < 10000;  ++i) {
          auto timer = getTimer(future, -1, eventLoop);
          timer.bind([&] (Date now) { ++numFired; });
          // now let it die
      }
  
      cerr << "numFired = " << numFired << endl;
  }
  
  BOOST_AUTO_TEST_CASE( test_rapid_creation_destruction_2 )
  {
      EventLoop eventLoop;
      AsioThreadPool threads(eventLoop, 4 /* min threads */);
      getTimer(Date::now().plusSeconds(0.1), -1, eventLoop).wait();
  
      Date past = Date::now();
  
      std::atomic<int> numFired(0);
  
      for (unsigned i = 0;  i < 10000;  ++i) {
          auto timer = getTimer(past, 1.0, eventLoop);
          timer.bind([&] (Date now) { ++numFired; });
          // Again, let it die
      }
  
      cerr << "numFired = " << numFired << endl;
  }
  
  // Try to find a race condition between firing and destroying
  BOOST_AUTO_TEST_CASE( test_rapid_creation_destruction_3 )
  {
      EventLoop eventLoop;
      AsioThreadPool threads(eventLoop, 4 /* threads */);
      getTimer(Date::now().plusSeconds(0.1), -1, eventLoop).wait();
  
      std::atomic<int> numFired(0);
  
      for (unsigned i = 0;  i < 1000;  ++i) {
          auto timer = getTimer(Date::now().plusSeconds(0.00001), 0.1, eventLoop);
          timer.bind([&] (Date now) { ++numFired; });
          getTimer(Date::now().plusSeconds(0.000001), -0.1, eventLoop).wait();
          //std::this_thread::yield();
          //timer.wait();
          // Again, let it die
      }
  
      cerr << "numFired = " << numFired << endl;
  }
  
  // Disarm from a different thread whilst it's firing
  BOOST_AUTO_TEST_CASE( test_rapid_creation_destruction_4 )
  {
      EventLoop eventLoop;
      AsioThreadPool threads(eventLoop, 4 /* threads */);
      getTimer(Date::now().plusSeconds(0.1), -1, eventLoop).wait();
  
      std::atomic<int> numFired(0);
  
      for (unsigned i = 0;  i < 100;  ++i) {
          auto timer = getTimer(Date::now().plusSeconds(0.00001), 0.00001,
                                eventLoop);
          timer.bind([&] (Date now) { ++numFired; });
          
          std::atomic<bool> done(false);
          
          auto fn = [&] () { getTimer(Date::now().plusSeconds(0.0001), -1, eventLoop).wait();  timer = WatchT<Date>();  done = true; };
          std::thread t(fn);
          t.join();
      }
  
      cerr << "numFired = " << numFired << endl;
  }
  