summaryrefslogtreecommitdiff
path: root/ACE/tests/MT_Reactor_Timer_Test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/tests/MT_Reactor_Timer_Test.cpp')
-rw-r--r--ACE/tests/MT_Reactor_Timer_Test.cpp374
1 files changed, 374 insertions, 0 deletions
diff --git a/ACE/tests/MT_Reactor_Timer_Test.cpp b/ACE/tests/MT_Reactor_Timer_Test.cpp
new file mode 100644
index 00000000000..5a714be719b
--- /dev/null
+++ b/ACE/tests/MT_Reactor_Timer_Test.cpp
@@ -0,0 +1,374 @@
+// $Id$
+
+// ============================================================================
+//
+// = LIBRARY
+// tests
+//
+// = FILENAME
+// MT_Reactor_Timer_Test.cpp
+//
+// = DESCRIPTION
+// This is a simple test that illustrates the timer mechanism of
+// the reactor scheduling timers, handling expired timers and
+// cancelling scheduled timers from multiple threads. No command
+// line arguments are needed to run the test.
+//
+// = AUTHOR
+// Steve Huston <shuston@riverace.com>
+//
+// ============================================================================
+
+#include "test_config.h"
+#include "MT_Reactor_Timer_Test.h"
+#include "ace/ACE.h"
+#include "ace/OS_NS_unistd.h"
+
+ACE_RCSID(tests, MT_Reactor_Timer_Test, "$Id$")
+
+#if defined (ACE_HAS_THREADS)
+
+// This test exercises the setting and cancelling of timers from a
+// thread other than the one the reactor is running in. It sets up an
+// initial set of timers (3, 4, 5 seconds) from the main thread. When
+// the second thread starts, it cancels the 3 second timer and sets a
+// 2-second timer and an already-expired timer, which should be the
+// first to fire. It then sleeps for 3 seconds (letting the 2 second
+// timer fire, and if things are slow, the 4 second timer will also
+// fire. Then it sets 2 more timers at 10 and 12 seconds and cancels
+// the original 5 second timer. Then returns, ending the thread. The
+// destructor for Time_Handler insures that everything happened
+// correctly.
+
+Time_Handler::Time_Handler (void)
+{
+ for (int i = 0;
+ i < Time_Handler::TIMER_SLOTS;
+ this->timer_id_[i++] = Time_Handler::TIMER_NOTSET)
+ continue;
+
+ this->prev_timer_ = -1;
+}
+
+// Set up initial timer conditions. Timers set up at 3, 4, and 5
+// seconds. The one at 3 seconds will get cancelled when the thread
+// starts.
+
+void
+Time_Handler::setup (void)
+{
+ ACE_Reactor *r = ACE_Reactor::instance ();
+
+ this->timer_id_[2] = r->schedule_timer (this,
+ (const void *) 2,
+ ACE_Time_Value (3));
+ this->timer_id_[3] = r->schedule_timer (this,
+ (const void *) 3,
+ ACE_Time_Value (4));
+ this->timer_id_[4] = r->schedule_timer (this,
+ (const void *) 4,
+ ACE_Time_Value (5));
+ return;
+}
+
+int
+Time_Handler::verify_results (void)
+{
+ ACE_ASSERT (this->timer_id_[0] == Time_Handler::TIMER_FIRED);
+ ACE_ASSERT (this->timer_id_[1] == Time_Handler::TIMER_FIRED);
+ ACE_ASSERT (this->timer_id_[2] == Time_Handler::TIMER_CANCELLED);
+ ACE_ASSERT (this->timer_id_[3] == Time_Handler::TIMER_FIRED);
+ ACE_ASSERT (this->timer_id_[4] == Time_Handler::TIMER_CANCELLED);
+ ACE_ASSERT (this->timer_id_[5] == Time_Handler::TIMER_FIRED);
+ ACE_ASSERT (this->timer_id_[6] == Time_Handler::TIMER_FIRED);
+
+ for (int i = 7; i < Time_Handler::TIMER_SLOTS; i++)
+ ACE_ASSERT (this->timer_id_[i] == Time_Handler::TIMER_NOTSET);
+
+ return 0;
+}
+
+int
+Time_Handler::svc (void)
+{
+ ACE_Reactor *r = ACE_Reactor::instance ();
+
+ ACE_ASSERT (r->cancel_timer (this->timer_id_[2]) == 1);
+ this->timer_id_[2] = Time_Handler::TIMER_CANCELLED;
+
+ this->timer_id_[1] = r->schedule_timer(this,
+ (const void *) 1,
+ ACE_Time_Value (2));
+ // This one may get the callback before we return, so serialize.
+ this->lock_.acquire ();
+ this->timer_id_[0] = r->schedule_timer(this,
+ (const void *) 0,
+ ACE_Time_Value (0, -5));
+ this->lock_.release ();
+ ACE_OS::sleep(3);
+
+ this->timer_id_[5] = r->schedule_timer(this,
+ (const void *)5,
+ ACE_Time_Value (10));
+ this->timer_id_[6] = r->schedule_timer(this,
+ (const void *)6,
+ ACE_Time_Value (12));
+
+ ACE_ASSERT (r->cancel_timer (this->timer_id_[4]) == 1);
+ this->timer_id_[4] = Time_Handler::TIMER_CANCELLED;
+
+ return 0;
+}
+
+int
+Time_Handler::handle_timeout (const ACE_Time_Value &tv,
+ const void *arg)
+{
+ long time_tag = static_cast <long> (reinterpret_cast <size_t> (arg));
+ ACE_UNUSED_ARG(tv);
+
+ ACE_GUARD_RETURN (ACE_Thread_Mutex, id_lock, this->lock_, 0);
+
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("%T (%t): Timer #%d (id #%d) expired\n"),
+ time_tag,
+ this->timer_id_[time_tag]));
+
+ ACE_ASSERT (time_tag > this->prev_timer_);
+ ACE_ASSERT (this->timer_id_[time_tag] != Time_Handler::TIMER_NOTSET);
+ ACE_ASSERT (this->timer_id_[time_tag] != Time_Handler::TIMER_CANCELLED);
+ ACE_ASSERT (this->timer_id_[time_tag] != Time_Handler::TIMER_FIRED);
+ this->timer_id_[time_tag] = Time_Handler::TIMER_FIRED;
+ this->prev_timer_ = time_tag;
+
+ return 0;
+}
+
+#endif /* ACE_HAS_THREADS */
+
+Dispatch_Count_Handler::Dispatch_Count_Handler (void)
+{
+
+ ACE_Reactor *r = ACE_Reactor::instance ();
+
+ this->input_seen_ = this->notify_seen_ = 0;
+ this->timers_fired_ = 0;
+
+ // Initialize the pipe.
+ if (this->pipe_.open () == -1)
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("%p\n"),
+ ACE_TEXT ("ACE_Pipe::open")));
+ // Register the "read" end of the pipe.
+ else if (r->register_handler (this->pipe_.read_handle (),
+ this,
+ ACE_Event_Handler::READ_MASK) == -1)
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("%p\n"),
+ ACE_TEXT ("register_handler")));
+ // Put something in our pipe and smoke it... ;-)
+ else if (ACE::send (this->pipe_.write_handle (),
+ "z",
+ 1) == -1)
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("%p\n"),
+ ACE_TEXT ("send")));
+ // Call notify to prime the pump for this, as well.
+ else if (r->notify (this) == -1)
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("%p\n"),
+ ACE_TEXT ("notify")));
+}
+
+int
+Dispatch_Count_Handler::handle_close (ACE_HANDLE h,
+ ACE_Reactor_Mask m)
+{
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("%T (%t): handle_close\n")));
+
+ ACE_ASSERT (h == this->pipe_.read_handle ()
+ && m == ACE_Event_Handler::READ_MASK);
+
+ return 0;
+}
+
+int
+Dispatch_Count_Handler::handle_input (ACE_HANDLE h)
+{
+ char c;
+
+ ACE_ASSERT (this->input_seen_ == 0);
+ this->input_seen_ = 1;
+
+ if (ACE::recv (h, &c, 1) != 1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ ACE_TEXT ("%p\n"),
+ ACE_TEXT ("recv")),
+ -1);
+
+ ACE_ASSERT (c == 'z');
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("%T (%t): handle_input\n")));
+ // Trigger the <handle_close> hook.
+ return -1;
+}
+
+int
+Dispatch_Count_Handler::handle_exception (ACE_HANDLE h)
+{
+ ACE_UNUSED_ARG (h);
+
+ ACE_ASSERT (this->notify_seen_ == 0);
+ this->notify_seen_ = 1;
+
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("%T (%t): handle_exception\n")));
+ return 0;
+}
+
+int
+Dispatch_Count_Handler::handle_timeout (const ACE_Time_Value &tv,
+ const void *arg)
+{
+ ACE_UNUSED_ARG (tv);
+
+ ++this->timers_fired_;
+
+ long value = static_cast <long> (reinterpret_cast <size_t> (arg));
+
+ // This case just tests to make sure the Reactor is counting timer
+ // expiration correctly.
+ ACE_DEBUG ((LM_DEBUG,
+ ACE_TEXT ("%T (%t): expiration %d\n"),
+ value));
+ return 0;
+}
+
+int
+Dispatch_Count_Handler::verify_results (void)
+{
+ int result = 0;
+
+ if (this->input_seen_ != 1)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("input_seen_ is not 1 but %d\n"),
+ input_seen_));
+ result = -1;
+ }
+
+ if (this->notify_seen_ != 1)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("notify_seen_ is not 1 but %d\n"),
+ notify_seen_));
+ result = -1;
+ }
+
+ if (this->timers_fired_ != ACE_MAX_TIMERS)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("timers fired not equal max timers: %d != %d\n"),
+ this->timers_fired_,
+ ACE_MAX_TIMERS));
+ result = -1;
+ }
+
+ return result;
+}
+
+int
+run_main (int, ACE_TCHAR *[])
+{
+ ACE_START_TEST (ACE_TEXT ("MT_Reactor_Timer_Test"));
+
+ int status = 0;
+ int test_result = 0;
+
+ ACE_Reactor *r = ACE_Reactor::instance ();
+
+ Dispatch_Count_Handler callback;
+
+ for (int i = ACE_MAX_TIMERS; i > 0; i--)
+ // Schedule a timeout to expire immediately.
+ if (r->schedule_timer (&callback,
+ reinterpret_cast <const void *> (static_cast <size_t> (i)),
+ ACE_Time_Value (0)) == -1)
+ ACE_ERROR_RETURN ((LM_ERROR,
+ ACE_TEXT ("%p\n"),
+ ACE_TEXT ("schedule_timer")),
+ 1);
+
+ ACE_Time_Value no_waiting (0);
+ size_t events = 0;
+
+ while (1)
+ {
+ int result = r->handle_events (no_waiting);
+
+ // Timeout.
+ if (result == 0)
+ break;
+
+ // Make sure there were no errors.
+ ACE_ASSERT (result != -1);
+
+ events += result;
+ }
+
+ // All <ACE_MAX_TIMERS> + 2 I/O dispatches (one for <handle_input>
+ // and the other for <handle_exception>) should be counted in
+ // events.
+ if (events < ACE_MAX_TIMERS + 2)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("expected %d events, got %d instead\n"),
+ ACE_MAX_TIMERS + 2,
+ events));
+ }
+
+ status = callback.verify_results ();
+ if (status != 0)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("Dispatch counting test failed.\n")));
+ test_result = 1;
+ }
+
+#if defined (ACE_HAS_THREADS)
+
+ Time_Handler other_thread;
+ ACE_Time_Value time_limit (30);
+
+ // Set up initial set of timers.
+ other_thread.setup ();
+
+ other_thread.activate (THR_NEW_LWP | THR_JOINABLE);
+ status = ACE_Reactor::instance()->run_reactor_event_loop (time_limit);
+ // Should have returned only because the time limit is up...
+ ACE_ASSERT (status != -1);
+ ACE_ASSERT (time_limit.sec () == 0);
+
+ status = other_thread.wait ();
+ if (status == -1)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("%p, errno is %d\n"),
+ "wait ()",
+ errno));
+ ACE_ASSERT (status != -1);
+ }
+
+ status = other_thread.verify_results ();
+ if (status != 0)
+ test_result = 1;
+
+#else
+ ACE_ERROR ((LM_INFO,
+ ACE_TEXT ("threads not supported on this platform\n")));
+#endif /* ACE_HAS_THREADS */
+
+ ACE_END_TEST;
+ return test_result;
+}