//============================================================================= /** * @file Bug_4055_Regression_Test.cpp * * @author Johnny Willemsen (jwillemsen@remedy.nl) */ //============================================================================= #include "test_config.h" #include "ace/Reactor.h" #include "ace/Timer_Queue.h" #include "ace/Time_Value_T.h" #include "ace/Monotonic_Time_Policy.h" #include "ace/Condition_Thread_Mutex.h" #include "ace/Condition_Attributes.h" #include "ace/OS_NS_sys_time.h" #include "ace/High_Res_Timer.h" #include "ace/Get_Opt.h" #include "ace/Timer_Heap_T.h" #include "ace/Event_Handler_Handle_Timeout_Upcall.h" #include "ace/TP_Reactor.h" #include "ace/Task_T.h" #include "ace/Truncate.h" #if defined (ACE_HAS_MONOTONIC_TIME_POLICY) && defined (ACE_HAS_MONOTONIC_CONDITIONS) # if defined (ACE_HAS_THREADS) # if defined (ACE_WIN32) # include "ace/Date_Time.h" # endif // Create timer queue with hr support ACE_Timer_Queue * create_timer_queue () { ACE_Timer_Queue * tmq = 0; using timer_queue_type = ACE_Timer_Heap_T; ACE_NEW_RETURN (tmq, timer_queue_type (), 0); return tmq; } class MyTask : public ACE_Task { public: MyTask () : my_reactor_ (0), my_tq_ (0) {} ~MyTask () override { stop (); } int svc () override; int start (int num_threads); int stop (); ACE_Reactor* get_reactor (); int create_reactor (); private: int delete_reactor (); ACE_SYNCH_RECURSIVE_MUTEX lock_; ACE_Reactor *my_reactor_; ACE_Timer_Queue *my_tq_; }; ACE_Reactor* MyTask::get_reactor () { return my_reactor_; } int MyTask::create_reactor () { ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, monitor, this->lock_, -1); ACE_TEST_ASSERT (this->my_reactor_ == 0); this->my_tq_ = create_timer_queue (); ACE_TP_Reactor * pImpl = 0; ACE_NEW_RETURN (pImpl,ACE_TP_Reactor (0, this->my_tq_), -1); ACE_NEW_RETURN (my_reactor_, ACE_Reactor (pImpl ,1), -1); ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) Create TP_Reactor\n"))); this->reactor (my_reactor_); return 0; } int MyTask::delete_reactor () { ACE_GUARD_RETURN (ACE_SYNCH_RECURSIVE_MUTEX, monitor, this->lock_, -1); ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) Delete TP_Reactor\n"))); this->reactor (0); delete this->my_reactor_; this->my_reactor_ = 0; delete this->my_tq_; this->my_tq_ = 0; return 0; } int MyTask::start (int num_threads) { if (this->activate (THR_NEW_LWP, num_threads) == -1) ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%p.\n"), ACE_TEXT ("unable to activate thread pool")), -1); return 0; } int MyTask::stop () { if (this->my_reactor_ != 0) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("End TP_Reactor event loop\n"))); this->my_reactor_->end_reactor_event_loop (); } if (this->wait () == -1) ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p.\n"), ACE_TEXT ("unable to stop thread pool"))); if (this->delete_reactor () == -1) ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p.\n"), ACE_TEXT ("unable to delete reactor"))); return 0; } int MyTask::svc () { ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%P|%t) MyTask started\n"))); while (this->my_reactor_->reactor_event_loop_done () == 0) this->my_reactor_->run_reactor_event_loop (); ACE_DEBUG ((LM_DEBUG, ACE_TEXT (" (%t) MyTask finished\n"))); return 0; } class TestHandler : public ACE_Event_Handler { public: TestHandler (ACE_Reactor* reactor) : reactor_ (reactor), timeout_triggered_ (false) {} int handle_timeout (const ACE_Time_Value &tv, const void *arg) override; bool trigger_in(const ACE_Time_Value &delay); bool timeout_triggered () { return this->timeout_triggered_; } private: ACE_Reactor* reactor_; bool timeout_triggered_; }; int TestHandler::handle_timeout (const ACE_Time_Value &, const void *) { ACE_DEBUG ((LM_DEBUG, "(%P|%t) TestHandler::handle_timeout - timeout triggered\n")); this->timeout_triggered_ = true; return 0; } bool TestHandler::trigger_in(const ACE_Time_Value &delay) { ACE_DEBUG ((LM_DEBUG, "(%P|%t) TestHandler::trigger_in - scheduling timer\n")); return -1 != reactor_->schedule_timer (this, 0, delay, ACE_Time_Value (0)); } bool test_timer (ACE_Condition_Thread_Mutex& condition_, ACE_Time_Value& waittime, bool monotonic = false) { bool status = true; MyTask task1; task1.create_reactor (); task1.start (1); TestHandler test_handler (task1.get_reactor ()); // The second reactor that uses a hrtimer will trigger a timeout in // 5 seconds. At the same moment we calculate a timeout for the condition // 3 seconds in the future. Than we set the time 4 seconds back. // at the moment now the condition timeouts the trigger should have not // been executed. This is because with hrtime the trigger will call in 5 // seconds and the condition in 3 seconds, independent of any time change // meaning that the timeout should trigger if (!test_handler.trigger_in (ACE_Time_Value (5, 0))) ACE_ERROR_RETURN ((LM_ERROR, "(%P|%t) Unable to schedule trigger.\n"), false); waittime += ACE_Time_Value (3,0); // reset system clock 4 seconds backwards ACE_Time_Value curtime = ACE_OS::gettimeofday (); curtime -= ACE_Time_Value (4, 0); # if defined (ACE_WIN32) ACE_Date_Time curdt (curtime); SYSTEMTIME sys_time; sys_time.wDay = ACE_Utils::truncate_cast (curdt.day ()); sys_time.wMonth = ACE_Utils::truncate_cast (curdt.month ()); sys_time.wYear = ACE_Utils::truncate_cast (curdt.year ()); sys_time.wHour = ACE_Utils::truncate_cast (curdt.hour ()); sys_time.wMinute = ACE_Utils::truncate_cast (curdt.minute ()); sys_time.wSecond = ACE_Utils::truncate_cast (curdt.second ()); sys_time.wMilliseconds = ACE_Utils::truncate_cast (curdt.microsec () / 1000); if (!::SetLocalTime (&sys_time)) # else timespec_t curts; curts = curtime; if (ACE_OS::clock_settime (CLOCK_REALTIME, &curts) != 0) # endif { ACE_DEBUG((LM_INFO, "(%P|%t) Unable to reset OS time. Insufficient privileges or not supported.\n")); } else { ACE_DEBUG((LM_INFO, "(%P|%t) Going to wait on condition until %#T.\n", &waittime)); if (condition_.wait (&waittime) != -1 || errno != ETIME) { ACE_ERROR ((LM_ERROR, "ERROR: No errno or return -1\n")); status = 1; } ACE_DEBUG((LM_INFO, "(%P|%t) Condition wait returned at %#T.\n", &waittime)); if (test_handler.timeout_triggered ()) { if (monotonic) { ACE_ERROR ((LM_ERROR, "(%P|%t) ERROR: timer handler shouldn't have " "triggered because we used monotonic condition timing!\n")); status = false; } else ACE_DEBUG ((LM_INFO, "(%P|%t) timer handler " "triggered because we used non-monotonic condition timing!\n")); } else { if (!monotonic) { ACE_ERROR ((LM_ERROR, "(%P|%t) ERROR: timer handler should have " "triggered because we used non-monotonic condition timing!\n")); status = false; } else ACE_DEBUG ((LM_INFO, "(%P|%t) timer handler has not " "triggered because we used monotonic condition timing!\n")); } // reset system clock to correct time curtime = ACE_OS::gettimeofday (); curtime += ACE_Time_Value (4, 0); # if defined (ACE_WIN32) curdt.update (curtime); SYSTEMTIME sys_time; sys_time.wDay = ACE_Utils::truncate_cast (curdt.day ()); sys_time.wMonth = ACE_Utils::truncate_cast (curdt.month ()); sys_time.wYear = ACE_Utils::truncate_cast (curdt.year ()); sys_time.wHour = ACE_Utils::truncate_cast (curdt.hour ()); sys_time.wMinute = ACE_Utils::truncate_cast (curdt.minute ()); sys_time.wSecond = ACE_Utils::truncate_cast (curdt.second ()); sys_time.wMilliseconds = ACE_Utils::truncate_cast (curdt.microsec () / 1000); if (!::SetLocalTime (&sys_time)) # else curts = curtime; if (ACE_OS::clock_settime (CLOCK_REALTIME, &curts) != 0) # endif { ACE_DEBUG((LM_INFO, "(%P|%t) Unable to reset OS time. Insufficient privileges or not supported.\n")); } } ACE_DEBUG((LM_INFO, "(%P|%t) Asking worker thread to finish.\n")); task1.stop (); ACE_Thread_Manager::instance ()->wait (); return status; } # endif int run_main (int , ACE_TCHAR *[]) { ACE_START_TEST (ACE_TEXT ("Bug_4055_Regression_Test")); # if defined (ACE_HAS_THREADS) int status = 1; ACE_Thread_Mutex mutex_; ACE_Condition_Thread_Mutex condition_ (mutex_); ACE_Condition_Attributes_T monotonic_cond_attr_; ACE_Condition_Thread_Mutex monotonic_condition_ (mutex_, monotonic_cond_attr_); if (mutex_.acquire () != 0) { ACE_ERROR ((LM_ERROR, "(%P|%t) ERROR: Failed to acquire mutex.\n")); } else { ACE_Time_Value waittime; waittime = waittime.now (); if (test_timer (condition_, waittime)) { ACE_Time_Value_T monotonic_waittime; monotonic_waittime = monotonic_waittime.now (); if (test_timer (monotonic_condition_, monotonic_waittime, true)) status = 0; } } # else int status = 0; # endif ACE_END_TEST; return status; } #else int run_main (int , ACE_TCHAR *[]) { ACE_START_TEST (ACE_TEXT ("Bug_4055_Regression_Test")); ACE_DEBUG((LM_INFO, "(%P|%t) ACE not compiled with monotonic time.\n")); ACE_END_TEST; return 0; } #endif