diff options
Diffstat (limited to 'ACE/tests/Reactor_Remove_Resume_Test.cpp')
-rw-r--r-- | ACE/tests/Reactor_Remove_Resume_Test.cpp | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/ACE/tests/Reactor_Remove_Resume_Test.cpp b/ACE/tests/Reactor_Remove_Resume_Test.cpp new file mode 100644 index 00000000000..820dfc46e1d --- /dev/null +++ b/ACE/tests/Reactor_Remove_Resume_Test.cpp @@ -0,0 +1,486 @@ +/** + * @file Reactor_Remove_Resume_Test.cpp + * + * $Id$ + * + * This test verifies that ACE reactors only remove or resume the event + * handler used during an upcall, not another with same handle value. + * There is are least one case where the event handler can change + * during an upcall. The event handler could be removed by another + * thread, and a new one is registered for a handle of the same value + * (e.g. closed and then reopened) during an upcall. "Misbehaved" + * event handlers could also cause this problem by closing and + * deregistering the event handler during an upcall. + * + * @author Ossama Othman + */ + + +#include "test_config.h" +#include "ace/Reactor.h" +#include "ace/TP_Reactor.h" +#include "ace/Dev_Poll_Reactor.h" +#include "ace/Pipe.h" +#include "ace/Auto_Ptr.h" + +#include <algorithm> +#include <functional> + +int overall_result = 0; + +// ------------------------------------------------------------ + +class Bogus_Handler : public ACE_Event_Handler +{ +public: + + Bogus_Handler (ACE_Reactor * reactor, + ACE_HANDLE read_handle, + bool & okay_to_close); + +protected: + + virtual ~Bogus_Handler (void); + + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + virtual int resume_handler (void); + +private: + + ACE_HANDLE const read_handle_; + + // If the reactor closes the event handler before it gets the okay, + // we will issue an error. + bool & okay_to_close_; + +}; + +Bogus_Handler::Bogus_Handler (ACE_Reactor * reactor, + ACE_HANDLE read_handle, + bool & okay_to_close) + : ACE_Event_Handler (reactor) + , read_handle_ (read_handle) + , okay_to_close_ (okay_to_close) +{ + this->reference_counting_policy ().value ( + ACE_Event_Handler::Reference_Counting_Policy::ENABLED); +} + +Bogus_Handler::~Bogus_Handler (void) +{ +} + +ACE_HANDLE +Bogus_Handler::get_handle (void) const +{ + return this->read_handle_; +} + +int +Bogus_Handler::handle_input (ACE_HANDLE) +{ + // This event handler should have been suspended, meaning it should + // not have received any events. + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Bogus_Handler received an ") + ACE_TEXT ("unexpected event.\n")), + -1); +} + +int +Bogus_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + if (this->okay_to_close_) + return 0; + + overall_result = -1; + + // This event handler is being closed by the reactor unexpectedly. + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Bogus_Handler unexpectedly closed\n ")), + -1); +} + +int +Bogus_Handler::resume_handler (void) +{ + // We don't want the reactor to resume this event handler. + return ACE_APPLICATION_RESUMES_HANDLER; +} + +// ------------------------------------------------------------ + +class Bad_Handler : public ACE_Event_Handler +{ +public: + + Bad_Handler (ACE_Reactor * reactor, + ACE_HANDLE read_handle, + bool & okay_to_close, + bool suspension_test); + + ACE_HANDLE write_handle (void) const; + +protected: + + virtual ~Bad_Handler (void); + +private: + + virtual ACE_HANDLE get_handle (void) const; + virtual int handle_input (ACE_HANDLE handle); + virtual int handle_close (ACE_HANDLE handle, + ACE_Reactor_Mask close_mask); + + int handle_input_result (void) const; + +private: + + ACE_HANDLE const read_handle_; + + bool handle_close_called_; + + // Passed on to the Bogus_Handler constructor. Not used by the + // Bad_Handler, otherwise. + bool & okay_to_close_; + + // Are we running the event handler suspension or removal test? + bool suspension_test_; + +}; + +Bad_Handler::Bad_Handler (ACE_Reactor * reactor, + ACE_HANDLE read_handle, + bool & okay_to_close, + bool suspension_test) + : ACE_Event_Handler (reactor) + , read_handle_ (read_handle) + , handle_close_called_ (false) + , okay_to_close_ (okay_to_close) + , suspension_test_ (suspension_test) +{ + this->reference_counting_policy ().value ( + ACE_Event_Handler::Reference_Counting_Policy::ENABLED); + +} + +Bad_Handler::~Bad_Handler (void) +{ +} + +ACE_HANDLE +Bad_Handler::get_handle (void) const +{ + return this->read_handle_; +} + +int +Bad_Handler::handle_input (ACE_HANDLE handle) +{ + // Remove ourselves from the reactor, and trigger a different one + // with the same handle to be registered. + if (this->reactor ()->remove_handler (handle, + ACE_Event_Handler::READ_MASK) != 0) + { + overall_result = -1; + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Unable to remove Bad_Handler ") + ACE_TEXT ("from reactor\n")), + 1); + } + + ACE_Event_Handler_var bogus_handler ( + new Bogus_Handler (this->reactor (), + handle, + this->okay_to_close_)); + + // Register and suspend a new handler. + if (this->reactor ()->register_handler (handle, + bogus_handler.handler (), + ACE_Event_Handler::READ_MASK) != 0 + || this->reactor ()->suspend_handler (handle) != 0) + { + overall_result = -1; + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Unable to register or suspend ") + ACE_TEXT ("Bogus_Handler\n")), + 1); + } + + // Make sure a read event exists for dispatching during the next + // event loop iteration. + if (this->reactor ()->schedule_wakeup (handle, + ACE_Event_Handler::READ_MASK) == -1) + { + overall_result = -1; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Wakeup scheduling failed\n"))); + } + + return this->handle_input_result (); +} + +int +Bad_Handler::handle_close (ACE_HANDLE, + ACE_Reactor_Mask) +{ + if (this->handle_close_called_) + { + overall_result = -1; + + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Bad_Handler::handle_close() called ") + ACE_TEXT ("more than once\n")), + -1); + } + + this->handle_close_called_ = true; + + return 0; +} + +int +Bad_Handler::handle_input_result (void) const +{ + return + (this->suspension_test_ + + // Attempt to force reactor to resume the handle after the + // upcall completes. + ? 0 + + // Attempt to force the new event handler to be removed from the + // reactor. + : -1); +} + +// ------------------------------------------------------------ +int +register_handler (ACE_Reactor * reactor, + ACE_HANDLE read_handle, + bool & okay_to_close, + bool suspension_test) +{ + ACE_Event_Handler_var bad_handler (new Bad_Handler (reactor, + read_handle, + okay_to_close, + suspension_test)); + + // Register for read events. + if (reactor->register_handler (bad_handler.handler (), + ACE_Event_Handler::READ_MASK) != 0) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("Unable to register Bad_Handler with ") + ACE_TEXT ("reactor\n")), + -1); + } + + return 0; +} + +// ------------------------------------------------------------ + +int +send_data (ACE_HANDLE write_handle) +{ + char const foo[] = "foo"; + size_t const len = sizeof (foo); // We want the number of bytes, not + // the number of characters. + + ACE_Time_Value const timeout (2); + + // Trigger a read event on the pipe handle. This shouldn't timeout + // since the pipe should be able to hold such a small amount of + // data. + size_t bytes_transferred = 0; + ssize_t const result = + ACE::send_n (write_handle, + foo, + len, + &timeout, // timeout + &bytes_transferred); + + if (result == -1 + || bytes_transferred != len) + { + ACE_ERROR_RETURN ((LM_ERROR, + ACE_TEXT ("%p\n"), + ACE_TEXT ("Unable to send data")), + -1); + } + + return 0; +} + +// ------------------------------------------------------------ + +int +handle_events (ACE_Reactor & reactor, + bool & okay_to_close) +{ + ACE_Time_Value timeout (2); + + // Only one event handler should have been dispatched. + if (reactor.handle_events (&timeout) != 1) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Initial event dispatch failed\n"))); + } + else + { + okay_to_close = true; + + // Run the event loop again in an attempt to make the reactor + // dispatch the newly registered event handler. No events + // should be dispatched. + timeout.sec (2); + int const result = reactor.handle_events (&timeout); + + + if (result > 0) + { + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Unexpectedly dispatched an event\n"))); + } + else if (result < 0) + { + overall_result = -1; + + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Event loop failed unexpectedly\n"))); + } + else + return 0; + } + + return -1; +} + +// ------------------------------------------------------------ + +typedef auto_ptr<ACE_Reactor_Impl> (*reactor_factory_type) (void); + +auto_ptr<ACE_Reactor_Impl> +tp_reactor_factory (void) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Creating ACE_TP_Reactor.\n"))); + + return auto_ptr<ACE_Reactor_Impl> (new ACE_TP_Reactor); +} + +#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) +auto_ptr<ACE_Reactor_Impl> +dev_poll_reactor_factory (void) +{ + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("Creating ACE_Dev_Poll_Reactor.\n"))); + + return auto_ptr<ACE_Reactor_Impl> (new ACE_Dev_Poll_Reactor); +} + +#endif /* ACE_HAS_EVENT_POLL || ACE_HAS_DEV_POLL */ + +// ------------------------------------------------------------ +/** + * @struct Caller + * + * @brief Reactor test execution functor. + * + * Reactor test execution functor. + */ +struct Run_Test : public std::unary_function<reactor_factory_type, void> +{ + /// Function call operator overload. + void operator() (reactor_factory_type factory) + { + bool const suspension_test[] = + { + true, // Run suspension test. + false // Run removal test. + }; + + bool const * const begin = suspension_test; + bool const * const end = + suspension_test + + sizeof (suspension_test) / sizeof (suspension_test[0]); + + for (bool const * i = begin; i != end; ++i) + { + bool const suspension_test = *i; + + if (suspension_test) + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("** Running suspension test **\n"))); + } + else + { + ACE_DEBUG ((LM_INFO, + ACE_TEXT ("** Running removal test **\n"))); + } + + auto_ptr<ACE_Reactor_Impl> the_factory (factory ()); + ACE_Reactor reactor (the_factory.get ()); + + // In this test, it's only okay to close the Bogus_Handler + // when the reactor is destroyed. + bool okay_to_close = false; + + ACE_Pipe the_pipe; + if (the_pipe.open () != 0 + || register_handler (&reactor, + the_pipe.read_handle (), + okay_to_close, + suspension_test) != 0 + || send_data (the_pipe.write_handle ()) != 0 + || handle_events (reactor, okay_to_close) != 0) + { + overall_result = -1; + } + + okay_to_close = true; + + ACE_DEBUG ((LM_INFO, ACE_TEXT ("\n"))); // For log aesthetics. + } + } +}; + +// ------------------------------------------------------------ + +int +run_main (int, ACE_TCHAR *[]) +{ + ACE_START_TEST (ACE_TEXT ("Reactor_Remove_Resume_Test")); + + static reactor_factory_type const factories[] = + { + tp_reactor_factory +#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL) + , dev_poll_reactor_factory +#endif /* ACE_HAS_EVENT_POLL || ACE_HAS_DEV_POLL */ + }; + + static size_t const factory_count = sizeof (factories) / sizeof (factories[0]); + + std::for_each (factories, factories + factory_count, Run_Test ()); + + if (overall_result != 0) + ACE_ERROR ((LM_ERROR, + ACE_TEXT ("Test failed.\n"))); + else + ACE_ERROR ((LM_INFO, + ACE_TEXT ("Test passed.\n"))); + + ACE_END_TEST; + + return overall_result; +} |