1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
|
//=============================================================================
/**
* @file Bug_2653_Regression_Test.cpp
*
* $Id: Bug_2653_Regression_Test.cpp 94113 2011-05-27 14:44:29Z msmit $
*
* This bug occurs when schedule_wakeup is called for a handle that does
* not already have an event handler registered. This can happen quite
* legitimately in multithreaded applications where one thread schedules
* the wakeup while another thread is handling the closure of the
* connection and unregistering.
*
*
* @author Phil Mesnier <mesnier_p@ociweb.com>
*/
//=============================================================================
#include "test_config.h"
#include "ace/OS_NS_string.h"
#include "ace/Reactor.h"
#include "ace/TP_Reactor.h"
#include "ace/Pipe.h"
#include "ace/ACE.h"
#include "ace/Task.h"
#include "ace/OS_NS_unistd.h"
static const char *message =
"Hello there! Hope you get this message";
class Watchdog : public ACE_Task_Base
{
public:
int svc (void);
int my_grp_;
};
int
Watchdog::svc (void)
{
ACE_OS::sleep (5);
// If we make it through the sleep and haven't been canceled, that
// means the process is hung.
if (!this->thr_mgr ()->testcancel (ACE_Thread::self ()))
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Watchdog slept without cancel - we're hung\n")));
return 0;
}
class Handler : public ACE_Event_Handler
{
public:
Handler (ACE_Reactor &reactor, bool close_other);
~Handler();
int handle_input (ACE_HANDLE fd);
int handle_output (ACE_HANDLE fd);
ACE_Pipe pipe_;
ACE_Pipe other_pipe_;
};
Handler::Handler (ACE_Reactor &reactor, bool close_other)
: ACE_Event_Handler (&reactor)
{
// Create the pipe.
if (0 != this->other_pipe_.open () || 0 != this->pipe_.open())
{
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("pipe")));
}
else
{
// Register for all events.
if (0 != this->reactor ()->register_handler
(this->pipe_.read_handle (),
this,
ACE_Event_Handler::READ_MASK))
{
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("register")));
}
if (-1 == this->reactor ()->schedule_wakeup
(this->other_pipe_.write_handle(),
ACE_Event_Handler::WRITE_MASK))
{
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), ACE_TEXT ("Schedule")));
}
// closing the other pipe sets up the spinner error.
// leaving it open sets up the segv.
if (close_other)
this->other_pipe_.close();
}
}
Handler::~Handler (void)
{
this->other_pipe_.close();
this->pipe_.close ();
}
int
Handler::handle_output (ACE_HANDLE)
{
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Handler::handle_output\n")));
#if defined (__OpenBSD__) || defined (ACE_VXWORKS)
// All that we need written has been written, so don't
// call handle_output again.
this->reactor ()->mask_ops (this->pipe_.read_handle (),
ACE_Event_Handler::WRITE_MASK,
ACE_Reactor::CLR_MASK);
#endif /* __OpenBSD__ || ACE_VXWORKS */
return 0;
}
int
Handler::handle_input (ACE_HANDLE fd)
{
char buffer[BUFSIZ];
ssize_t result = ACE::recv (fd, buffer, sizeof buffer);
if (result != ssize_t (ACE_OS::strlen (message)))
ACE_ERROR ((LM_ERROR, ACE_TEXT ("Handler recv'd %b bytes; expected %B\n"),
result, ACE_OS::strlen (message)));
buffer[result] = '\0';
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Handler::handle_input: %C\n"), buffer));
if (ACE_OS::strcmp (buffer, message) != 0)
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("Handler text mismatch; received \"%C\"; ")
ACE_TEXT ("expected \"%C\"\n"),
buffer, message));
this->reactor ()->end_reactor_event_loop ();
return 0;
}
static void
test_for_crash (ACE_Reactor &reactor)
{
Handler handler (reactor, false);
// This should trigger a call to <handle_input>.
ssize_t result =
ACE::send_n (handler.pipe_.write_handle (),
message,
ACE_OS::strlen (message));
if (result != ssize_t (ACE_OS::strlen (message)))
ACE_ERROR ((LM_ERROR, ACE_TEXT ("Handler sent %b bytes; should be %B\n"),
result, ACE_OS::strlen (message)));
reactor.run_reactor_event_loop ();
if (0 != reactor.remove_handler (handler.pipe_.read_handle (),
ACE_Event_Handler::ALL_EVENTS_MASK |
ACE_Event_Handler::DONT_CALL))
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("%p\n"),
ACE_TEXT ("test_for_handler, remove pipe")));
if (0 == reactor.remove_handler (handler.other_pipe_.write_handle (),
ACE_Event_Handler::ALL_EVENTS_MASK |
ACE_Event_Handler::DONT_CALL))
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("test_for_crash remove other_pipe succeeded ")
ACE_TEXT ("but shouldn't\n")));
}
static void
test_for_spin (ACE_Reactor &reactor)
{
Handler handler (reactor, true);
// This should trigger a call to <handle_input>.
ssize_t result =
ACE::send_n (handler.pipe_.write_handle (),
message,
ACE_OS::strlen (message));
if (result != ssize_t (ACE_OS::strlen (message)))
ACE_ERROR ((LM_ERROR, ACE_TEXT ("Handler sent %b bytes; should be %B\n"),
result, ACE_OS::strlen (message)));
reactor.run_reactor_event_loop ();
if (0 != reactor.remove_handler (handler.pipe_.read_handle (),
ACE_Event_Handler::ALL_EVENTS_MASK |
ACE_Event_Handler::DONT_CALL))
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("%p\n"),
ACE_TEXT ("test_for_spin, remove pipe")));
if (0 == reactor.remove_handler (handler.other_pipe_.write_handle (),
ACE_Event_Handler::ALL_EVENTS_MASK |
ACE_Event_Handler::DONT_CALL))
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("test_for_spin remove other_pipe succeeded ")
ACE_TEXT ("but shouldn't\n")));
}
int
run_main (int, ACE_TCHAR *[])
{
ACE_START_TEST (ACE_TEXT ("Bug_2653_Regression_Test"));
ACE_TP_Reactor tp_reactor_impl;
ACE_Reactor tp_reactor (&tp_reactor_impl);
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing for crash\n")));
test_for_crash (tp_reactor);
// if that passes, start the watchdog. We don't need to wait
Watchdog wd;
wd.activate ();
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("Testing for spin\n")));
test_for_spin(tp_reactor);
// If test_for_spin returns, all is well.
wd.thr_mgr ()->cancel_grp (wd.grp_id ());
wd.wait ();
ACE_END_TEST;
return 0;
}
|