summaryrefslogtreecommitdiff
path: root/ACE/tests/Bug_2815_Regression_Test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ACE/tests/Bug_2815_Regression_Test.cpp')
-rw-r--r--ACE/tests/Bug_2815_Regression_Test.cpp495
1 files changed, 495 insertions, 0 deletions
diff --git a/ACE/tests/Bug_2815_Regression_Test.cpp b/ACE/tests/Bug_2815_Regression_Test.cpp
new file mode 100644
index 00000000000..7fd812a0886
--- /dev/null
+++ b/ACE/tests/Bug_2815_Regression_Test.cpp
@@ -0,0 +1,495 @@
+/**
+ * @file Bug_2815_Regression_Test.cpp
+ *
+ * $Id$
+ *
+ * Verify that the notification queue can be used with large numbers
+ * of event handlers.
+ *
+ * Normally the ACE_Reactor uses a pipe to implement the notify()
+ * methods. ACE can be compiled with
+ * ACE_HAS_REACTOR_NOTIFICATION_QUEUE, with this configuration flag
+ * the Reactor uses a user-space queue to contain the notifications.
+ * A single message is sent through the pipe to indicate "pipe not
+ * empty."
+ *
+ * In this configuration, if an event handler is removed
+ * from the Reactor the user-space queue has to be searched for
+ * pending notifications and the notifications must be removed.
+ *
+ * The original implementation used a naive algorithm to search and
+ * remove the handlers, which resulted in very high overhead when
+ * removing handlers while having a very long notification queue.
+ *
+ * @author Carlos O'Ryan <coryan@atdesk.com>
+ *
+ */
+
+#include "test_config.h"
+#include "ace/Reactor.h"
+#include "ace/TP_Reactor.h"
+#include "ace/Select_Reactor.h"
+
+class One_Shot_Handler;
+
+/**
+ * @class Driver
+ *
+ * @brief Main driver for the test, generates notification events and
+ * verifies they are received correctly.
+ *
+ */
+class Driver
+{
+public:
+ Driver(ACE_Reactor * reactor,
+ int max_notifications,
+ char const *test_name);
+
+ /// Run the test
+ void run (void);
+
+ /// One of the sub-handlers has received a notification
+ void notification_received ();
+
+ /// One of the sub-handlers has decided to skip several notifications
+ void notifications_skipped (int skip_count);
+
+ /**
+ * @brief Return the reactor configured for this test
+ */
+ ACE_Reactor * reactor ();
+
+private:
+ /**
+ * @brief Implement a single iteration.
+ *
+ * Each iteration of the test consists of sending multiple
+ * notifications simultaneously.
+ */
+ void send_notifications (void);
+
+ /**
+ * @brief Return true if the test is finished.
+ */
+ bool done (void) const;
+
+ /**
+ * @brief Return true if there are more iterations to run.
+ */
+ bool more_iterations () const;
+
+ /**
+ * @brief Return true if the current iteration is completed.
+ */
+ bool current_iteration_done () const;
+
+ /**
+ * @brief Run one iteration of the test, each iteration doubles
+ * the number of events.
+ */
+ int run_one_iteration (void);
+
+ /**
+ * @brief Initialize a bunch of One_Shot_Handlers
+ */
+ void initialize_handlers(
+ int nhandlers, One_Shot_Handler ** handlers);
+
+ /**
+ * @brief Dispatch events to the One_Shot_Handlers
+ */
+ void notify_handlers(
+ int nhandlers, One_Shot_Handler ** handlers);
+
+ /**
+ * @brief Helpful for debugging
+ *
+ * The number of notifications received, skipped and sent are
+ * subject to simple invariants. During debugging any violation of
+ * those invariants indicates a problem in the application or the
+ * Reactor.
+ */
+ void check_notification_invariants();
+
+ /// A good place to set break points.
+ void invariant_failed();
+
+private:
+ /**
+ * @brief The reactor used in this test
+ */
+ ACE_Reactor * reactor_;
+
+ /**
+ * @brief The maximum number of notifications in any single
+ * iteration.
+ */
+ int max_notifications_;
+
+ /**
+ * @brief The name of the test
+ */
+ char const * test_name_;
+
+ /**
+ * @brief Number of notifications received
+ */
+ int notifications_sent_;
+
+ /**
+ * @brief Number of notifications sent
+ */
+ int notifications_recv_;
+
+ /**
+ * @brief Number of notifications skipped because
+ * the handler was removed
+ */
+ int notifications_skipped_;
+
+ /**
+ * @brief Number of notifications sent on each iteration
+ */
+ int notifications_curr_;
+};
+
+/**
+ * @class One_Shot_Handler
+ *
+ * @brief A handler that removes itself from the reactor
+ * after its first notification.
+ *
+ * To demonstrate the problems with the first implementation of
+ * the notification queue we generate multiple event handlers.
+ * Then we generate multiple notifications for each, but the handlers
+ * remove themselves from the reactor when the first notification
+ * is delivered. This causes a lot of activity in the notification
+ * queue.
+ *
+ */
+class One_Shot_Handler : public ACE_Event_Handler
+{
+public:
+ One_Shot_Handler(
+ Driver * master_handler,
+ char const * test_name,
+ int id);
+
+ /// Increase the number of expected notifications
+ void notification_queued();
+
+ /// Receive the notifications, but remove itself from the reactor on
+ /// on the first one.
+ virtual int handle_exception(ACE_HANDLE);
+
+private:
+ /// The driver for this test, communicate results to it
+ Driver * master_handler_;
+
+ /// The number of expected notifications
+ int expected_notifications_;
+
+ /// Identify the test and handler for debugging and better error output
+ char const * test_name_;
+ int id_;
+};
+
+int
+run_main (int, ACE_TCHAR *[])
+{
+ ACE_START_TEST (ACE_TEXT ("Bug_2815_Regression_Test"));
+
+#if !defined(ACE_HAS_REACTOR_NOTIFICATION_QUEUE)
+ ACE_DEBUG ((LM_INFO,
+ ACE_TEXT ("Notification queue disabled. ")
+ ACE_TEXT ("This test depends on purge_pending_notifications. ")
+ ACE_TEXT ("Thus, the test is disabled in this case\n")));
+
+#else
+ int max_notifications = 512 * 1024;
+
+ {
+ ACE_Reactor select_reactor (
+ new ACE_Select_Reactor,
+ 1);
+
+ Driver handler(&select_reactor,
+ max_notifications,
+ "Select_Reactor");
+
+ handler.run ();
+ }
+
+ {
+ ACE_Reactor tp_reactor (new ACE_TP_Reactor,
+ 1);
+ Driver handler(&tp_reactor,
+ max_notifications,
+ "TP_Reactor");
+ handler.run();
+ }
+
+#endif /* ACE_HAS_THREADS */
+ ACE_END_TEST;
+
+ return 0;
+}
+
+Driver::Driver (
+ ACE_Reactor * reactor,
+ int max_notifications,
+ char const * test_name)
+ : reactor_(reactor)
+ , max_notifications_(max_notifications)
+ , test_name_(test_name)
+ , notifications_sent_(0)
+ , notifications_recv_(0)
+ , notifications_skipped_(0)
+ , notifications_curr_(1)
+{
+}
+
+void
+Driver::run (void)
+{
+ while(more_iterations())
+ {
+ if(run_one_iteration() == -1)
+ {
+ return;
+ }
+
+ notifications_curr_ *= 2;
+ }
+
+ ACE_DEBUG ((LM_INFO,
+ ACE_TEXT ("Test %C passed sent=%d, recv=%d, skip=%d\n"),
+ test_name_,
+ notifications_sent_,
+ notifications_recv_,
+ notifications_skipped_));
+}
+
+void
+Driver::notification_received ()
+{
+ ++notifications_recv_;
+ check_notification_invariants();
+}
+
+void
+Driver::notifications_skipped (int skip_count)
+{
+ notifications_skipped_ += skip_count;
+ check_notification_invariants();
+}
+
+ACE_Reactor *
+Driver::reactor()
+{
+ return reactor_;
+}
+
+void
+Driver::send_notifications (void)
+{
+ int const nhandlers = 16;
+ One_Shot_Handler * handlers[nhandlers];
+ initialize_handlers(nhandlers, handlers);
+
+ for (int i = 0; i != notifications_curr_; ++i)
+ {
+ notify_handlers(nhandlers, handlers);
+ }
+}
+
+bool
+Driver::done (void) const
+{
+ return !more_iterations() && current_iteration_done();
+}
+
+bool
+Driver::more_iterations() const
+{
+ return notifications_curr_ < max_notifications_;
+}
+
+bool
+Driver::current_iteration_done() const
+{
+ return notifications_sent_ == (notifications_recv_ + notifications_skipped_);
+}
+
+int
+Driver::run_one_iteration (void)
+{
+ ACE_DEBUG ((LM_INFO,
+ ACE_TEXT ("Running iteration with %d events for %C test\n"),
+ notifications_curr_,
+ test_name_));
+
+ send_notifications ();
+
+ // Run for 30 seconds or until the test is done.
+
+ ACE_Time_Value const timeout(30,0);
+
+ while (!current_iteration_done())
+ {
+ ACE_Time_Value start = ACE_OS::gettimeofday();
+ ACE_Time_Value interval(1,0);
+ reactor()->run_reactor_event_loop(interval);
+ ACE_Time_Value end = ACE_OS::gettimeofday();
+
+ if (end - start >= timeout)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT ("Test %C failed due to timeout ")
+ ACE_TEXT (" sent=%d,recv=%d,skip=%d\n"),
+ test_name_,
+ notifications_sent_,
+ notifications_recv_,
+ notifications_skipped_));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void
+Driver::initialize_handlers(
+ int nhandlers, One_Shot_Handler ** handlers)
+{
+ for (int j = 0; j != nhandlers; ++j)
+ {
+ handlers[j] = new One_Shot_Handler(this, test_name_, j);
+ }
+}
+
+void
+Driver::notify_handlers(
+ int nhandlers, One_Shot_Handler ** handlers)
+{
+ for(int i = 0; i != nhandlers; ++i)
+ {
+ if(reactor()->notify (handlers[i]) == -1)
+ {
+ ACE_ERROR((LM_ERROR,
+ ACE_TEXT ("Cannot send notifications in %C test (%d/%d)\n"),
+ test_name_, i, notifications_curr_));
+ return;
+ }
+ handlers[i]->notification_queued();
+
+ ++notifications_sent_;
+ }
+}
+
+void Driver::
+check_notification_invariants()
+{
+ if (notifications_sent_ < 0)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT("The number of notifications sent (%d)")
+ ACE_TEXT(" should be positive\n"),
+ notifications_sent_));
+ invariant_failed();
+ }
+
+ if (notifications_recv_ < 0)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT("The number of notifications received (%d)")
+ ACE_TEXT(" should be positive\n"),
+ notifications_recv_));
+ invariant_failed();
+ }
+
+ if (notifications_skipped_ < 0)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT("The number of notifications skipped (%d)")
+ ACE_TEXT(" should be positive\n"),
+ notifications_skipped_));
+ invariant_failed();
+ }
+
+ if (notifications_sent_ < notifications_recv_)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT("Too many notifications received (%d)")
+ ACE_TEXT(" vs sent (%d)\n"),
+ notifications_recv_, notifications_sent_));
+ invariant_failed();
+ }
+
+ if (notifications_sent_ < notifications_skipped_)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT("Too many notifications skipped (%d)")
+ ACE_TEXT(" vs sent (%d)\n"),
+ notifications_skipped_, notifications_sent_));
+ invariant_failed();
+ }
+
+ if (notifications_skipped_ + notifications_recv_ > notifications_sent_)
+ {
+ ACE_ERROR ((LM_ERROR,
+ ACE_TEXT("Too many notifications skipped (%d)")
+ ACE_TEXT(" and received (%d) vs sent (%d)\n"),
+ notifications_skipped_, notifications_recv_,
+ notifications_sent_));
+ invariant_failed();
+ }
+}
+
+void Driver::
+invariant_failed()
+{
+ // Just a good place to set a breakpoint
+}
+
+// ============================================
+
+One_Shot_Handler::One_Shot_Handler(
+ Driver * master_handler,
+ char const * test_name, int id)
+ : ACE_Event_Handler(master_handler->reactor())
+ , master_handler_(master_handler)
+ , expected_notifications_(0)
+ , test_name_(test_name)
+ , id_(id)
+{
+}
+
+void One_Shot_Handler::
+notification_queued()
+{
+ ++expected_notifications_;
+}
+
+int One_Shot_Handler::
+handle_exception(ACE_HANDLE)
+{
+ --expected_notifications_;
+ master_handler_->notification_received();
+
+ int r = reactor()->purge_pending_notifications(this);
+ if (r >= 0)
+ {
+ master_handler_->notifications_skipped(r);
+ delete this;
+ return 0;
+ }
+
+ ACE_ERROR((LM_ERROR,
+ ACE_TEXT ("Cannot remove handler %d in %C test\n"),
+ id_, test_name_));
+
+ delete this;
+ return 0;
+}