在Windows系统下,调试跟踪 ACE 6.0.0 提供的示例程序,发现 Reactor 最终调用系统的 WaitForMultipleObjects 函数,Priority Reactor 和 Proactor 最终调用系统的 select 函数。
三个示例程序源代码都在 \ACE-6.0.0\ACE_wrappers\tests\ 目录下。
Reactor 的 Reactors_Test.cpp 文件部分内容:
int
run_main (int, ACE_TCHAR *[])
{
ACE_START_TEST (ACE_TEXT ("Reactors_Test"));
#if defined (ACE_HAS_THREADS)
ACE_ASSERT (ACE_LOG_MSG->op_status () != -1);
thr_mgr = ACE_Thread_Manager::instance ();
ACE_Reactor reactor;
ACE_ASSERT (ACE_LOG_MSG->op_status () != -1);
Test_Task tt1[MAX_TASKS];
Test_Task tt2[MAX_TASKS];
// Activate all of the Tasks.
for (int i = 0; i < MAX_TASKS; i++)
{
tt1[i].open (ACE_Reactor::instance ());
tt2[i].open (&reactor);
}
// Spawn two threads each running a different reactor.
if (ACE_Thread_Manager::instance ()->spawn
(ACE_THR_FUNC (worker),
(void *) ACE_Reactor::instance (),
THR_BOUND | THR_DETACHED) == -1)
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ("%p\n"),
ACE_TEXT ("spawn")),
-1);
else if (ACE_Thread_Manager::instance ()->spawn
(ACE_THR_FUNC (worker), (void *) &reactor,
THR_BOUND | THR_DETACHED) == -1)
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ("%p\n"),
ACE_TEXT ("spawn")),
-1);
if (ACE_Thread_Manager::instance ()->wait () == -1)
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ("%p\n"),
ACE_TEXT ("wait")),
-1);
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("(%t) all threads are finished\n")));
#else
ACE_ERROR ((LM_INFO,
ACE_TEXT ("threads not supported on this platform\n")));
#endif /* ACE_HAS_THREADS */
ACE_END_TEST;
return 0;
}
Priority Reactor 的 Priority_Reactor_Test.cpp 部分内容:
int
run_main (int argc, ACE_TCHAR *argv[])
{
ACE_START_TEST (ACE_TEXT ("Priority_Reactor_Test"));
//FUZZ: disable check_for_lack_ACE_OS
ACE_Get_Opt getopt (argc, argv, ACE_TEXT ("dc:l:m:t:"));
for (int c; (c = getopt ()) != -1; )
switch (c)
{
//FUZZ: enable check_for_lack_ACE_OS
case 'd':
opt_priority_reactor = 0;
break;
case 'c':
opt_nchildren = ACE_OS::atoi (getopt.opt_arg ());
break;
case 'l':
opt_nloops = ACE_OS::atoi (getopt.opt_arg ());
break;
case 'm':
max_retries = ACE_OS::atoi (getopt.opt_arg ());
break;
case 't':
opt_max_duration = ACE_OS::atoi (getopt.opt_arg ());
break;
case '?':
default:
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ("Usage: Priority_Reactor_Test ")
ACE_TEXT (" [-d] (disable priority reactor)\n")
ACE_TEXT (" [-c nchildren] (number of threads/processes)\n")
ACE_TEXT (" [-l loops] (number of loops per child)\n")
ACE_TEXT (" [-m maxretries] (attempts to connect)\n")
ACE_TEXT (" [-t max_time] (limits test duration)\n")),
-1);
ACE_NOTREACHED (break);
}
// Manage Reactor memory automagically.
// Note: If opt_priority_reactor is false, the default ACE_Reactor is used
// and we don't need to set one up.
ACE_Reactor *orig_reactor = 0;
auto_ptr<ACE_Reactor> reactor;
if (opt_priority_reactor)
{
ACE_Select_Reactor *impl_ptr;
ACE_NEW_RETURN (impl_ptr, ACE_Priority_Reactor, -1);
auto_ptr<ACE_Select_Reactor> auto_impl (impl_ptr);
ACE_Reactor *reactor_ptr;
ACE_NEW_RETURN (reactor_ptr, ACE_Reactor (impl_ptr, 1), -1);
auto_impl.release (); // ACE_Reactor dtor will take it from here
auto_ptr<ACE_Reactor> auto_reactor (reactor_ptr);
reactor = auto_reactor;
orig_reactor = ACE_Reactor::instance (reactor_ptr);
}
Read_Handler::set_countdown (opt_nchildren);
// Acceptor
ACCEPTOR acceptor;
acceptor.priority (ACE_Event_Handler::HI_PRIORITY);
ACE_INET_Addr server_addr;
// Bind acceptor to any port and then find out what the port was.
if (acceptor.open (ACE_sap_any_cast (const ACE_INET_Addr &)) == -1
|| acceptor.acceptor ().get_local_addr (server_addr) == -1)
ACE_ERROR_RETURN ((LM_ERROR,
ACE_TEXT ("(%P|%t) %p\n"),
ACE_TEXT ("open")),
-1);
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("(%P|%t) starting server at port %d\n"),
server_addr.get_port_number ()));
ACE_INET_Addr connection_addr (server_addr.get_port_number (),
ACE_DEFAULT_SERVER_HOST);
int i;
#if defined (ACE_HAS_THREADS)
for (i = 0; i < opt_nchildren; ++i)
{
if (ACE_Thread_Manager::instance ()->spawn
(ACE_THR_FUNC (client),
(void *) &connection_addr,
THR_NEW_LWP | THR_DETACHED) == -1)
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("(%P|%t) %p\n%a"),
ACE_TEXT ("thread create failed"),
1));
}
#elif !defined (ACE_LACKS_FORK)
for (i = 0; i < opt_nchildren; ++i)
{
switch (ACE_OS::fork ("child"))
{
case -1:
ACE_ERROR ((LM_ERROR,
ACE_TEXT ("(%P|%t) %p\n%a"),
ACE_TEXT ("fork failed"),
1));
ACE_OS::exit (-1);
/* NOTREACHED */
case 0:
client (&connection_addr);
ACE_OS::exit (0);
break;
/* NOTREACHED */
default:
break;
/* NOTREACHED */
}
}
#else
ACE_ERROR ((LM_INFO,
ACE_TEXT ("(%P|%t) ")
ACE_TEXT ("only one thread may be run ")
ACE_TEXT ("in a process on this platform\n")));
#endif /* ACE_HAS_THREADS */
ACE_Time_Value tv (opt_max_duration);
ACE_Reactor::instance()->register_handler
(&acceptor, ACE_Event_Handler::READ_MASK);
ACE_Reactor::instance()->run_reactor_event_loop (tv);
if (Read_Handler::get_countdown () != 0)
{
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("(%P|%t) running out of time, ")
ACE_TEXT ("probably due to failed connections.\n")));
}
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("(%P|%t) waiting for the children...\n")));
#if defined (ACE_HAS_THREADS)
ACE_Thread_Manager::instance ()->wait ();
#elif !defined (ACE_WIN32) && !defined (VXWORKS)
for (i = 0; i < opt_nchildren; ++i)
{
pid_t pid = ACE_OS::wait();
ACE_DEBUG ((LM_DEBUG,
ACE_TEXT ("(%P|%t) child %d terminated\n"),
pid));
}
#else
/* NOTREACHED */
// We aborted on the previous #ifdef
#endif /* ACE_HAS_THREADS */
if (orig_reactor != 0)
ACE_Reactor::instance (orig_reactor);
ACE_END_TEST;
return 0;
}
Proactor 的 Proactor_Test.cpp 文件部分内容:
int
run_main (int argc, ACE_TCHAR *argv[])
{
ACE_START_TEST (ACE_TEXT ("Proactor_Test"));
if (::parse_args (argc, argv) == -1)
return -1;
disable_signal (ACE_SIGRTMIN, ACE_SIGRTMAX);
disable_signal (SIGPIPE, SIGPIPE);
MyTask task1;
TestData test;
if (task1.start (threads, proactor_type, max_aio_operations) == 0)
{
Acceptor acceptor (&test);
Connector connector (&test);
ACE_INET_Addr addr (port);
int rc = 0;
if (both != 0 || host == 0) // Acceptor
{
// Simplify, initial read with zero size
if (acceptor.open (addr, 0, 1) == 0)
rc = 1;
}
if (both != 0 || host != 0)
{
if (host == 0)
host = ACE_LOCALHOST;
if (addr.set (port, host, 1, addr.get_type ()) == -1)
ACE_ERROR ((LM_ERROR, ACE_TEXT ("%p\n"), host));
else
rc += connector.start (addr, clients);
}
// Wait a few seconds to let things get going, then poll til
// all sessions are done. Note that when we exit this scope, the
// Acceptor and Connector will be destroyed, which should prevent
// further connections and also test how well destroyed handlers
// are handled.
ACE_OS::sleep (3);
}
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Sleeping til sessions run down.\n")));
while (!test.testing_done ())
ACE_OS::sleep (1);
test.stop_all ();
ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("(%t) Stop Thread Pool Task\n")));
task1.stop ();
ACE_END_TEST;
return 0;
}
Reactor 调用 \ACE-6.0.0\ACE_wrappers\ace\WFMO_Reactor.cpp 文件里的 ACE_WFMO_Reactor::poll_remaining_handles 函数,函数里调用了系统的 WaitForMultipleObjects 函数。下面是 ACE_WFMO_Reactor::poll_remaining_handles 函数:
DWORD
ACE_WFMO_Reactor::poll_remaining_handles (DWORD slot)
{
return ::WaitForMultipleObjects (this->handler_rep_.max_handlep1 () - slot,
this->handler_rep_.handles () + slot,
FALSE,
0);
}
Priority Reactor 和 Proactor 调用 \ACE-6.0.0\ACE_wrappers\ace\Select_Reactor_T.cpp 文件里的 ACE_Select_Reactor_T<ACE_SELECT_REACTOR_TOKEN>::wait_for_multiple_events 函数,其中调用了 ACE_OS::select 函数,而其中调用了系统的 select 函数。
ACE_Select_Reactor_T<ACE_SELECT_REACTOR_TOKEN>::wait_for_multiple_events 函数:
// Must be called with lock held.
template <class ACE_SELECT_REACTOR_TOKEN> int
ACE_Select_Reactor_T<ACE_SELECT_REACTOR_TOKEN>::wait_for_multiple_events
(ACE_Select_Reactor_Handle_Set &dispatch_set,
ACE_Time_Value *max_wait_time)
{
ACE_TRACE ("ACE_Select_Reactor_T::wait_for_multiple_events");
ACE_Time_Value timer_buf (0);
ACE_Time_Value *this_timeout = 0;
int number_of_active_handles = this->any_ready (dispatch_set);
// If there are any bits enabled in the <ready_set_> then we'll
// handle those first, otherwise we'll block in <select>.
if (number_of_active_handles == 0)
{
do
{
if (this->timer_queue_ == 0)
return 0;
this_timeout =
this->timer_queue_->calculate_timeout (max_wait_time,
&timer_buf);
#ifdef ACE_WIN32
// This arg is ignored on Windows and causes pointer
// truncation warnings on 64-bit compiles.
int const width = 0;
#else
int const width = this->handler_rep_.max_handlep1 ();
#endif /* ACE_WIN32 */
dispatch_set.rd_mask_ = this->wait_set_.rd_mask_;
dispatch_set.wr_mask_ = this->wait_set_.wr_mask_;
dispatch_set.ex_mask_ = this->wait_set_.ex_mask_;
number_of_active_handles = ACE_OS::select (width,
dispatch_set.rd_mask_,
dispatch_set.wr_mask_,
dispatch_set.ex_mask_,
this_timeout);
}
while (number_of_active_handles == -1 && this->handle_error () > 0);
if (number_of_active_handles > 0)
{
#if !defined (ACE_WIN32)
// Resynchronize the fd_sets so their "max" is set properly.
dispatch_set.rd_mask_.sync (this->handler_rep_.max_handlep1 ());
dispatch_set.wr_mask_.sync (this->handler_rep_.max_handlep1 ());
dispatch_set.ex_mask_.sync (this->handler_rep_.max_handlep1 ());
#endif /* ACE_WIN32 */
}
else if (number_of_active_handles == -1)
{
// Normally, select() will reset the bits in dispatch_set
// so that only those filed descriptors that are ready will
// have bits set. However, when an error occurs, the bit
// set remains as it was when the select call was first made.
// Thus, we now have a dispatch_set that has every file
// descriptor that was originally waited for, which is not
// correct. We must clear all the bit sets because we
// have no idea if any of the file descriptors is ready.
//
// NOTE: We dont have a test case to reproduce this
// problem. But pleae dont ignore this and remove it off.
dispatch_set.rd_mask_.reset ();
dispatch_set.wr_mask_.reset ();
dispatch_set.ex_mask_.reset ();
}
}
// Return the number of events to dispatch.
return number_of_active_handles;
}
ACE_OS::select 函数:
// It would be really cool to add another version of select that would
// function like the one we're defending against below!
ACE_INLINE int
ACE_OS::select (int width,
fd_set *rfds, fd_set *wfds, fd_set *efds,
const ACE_Time_Value *timeout)
{
ACE_OS_TRACE ("ACE_OS::select");
#if defined (ACE_HAS_NONCONST_SELECT_TIMEVAL)
// We must defend against non-conformity!
timeval copy;
timeval *timep = 0;
if (timeout != 0)
{
copy = *timeout;
timep = ©
}
else
timep = 0;
#else
const timeval *timep = (timeout == 0 ? (const timeval *)0 : *timeout);
#endif /* ACE_HAS_NONCONST_SELECT_TIMEVAL */
#if defined (ACE_LACKS_SELECT)
ACE_UNUSED_ARG (width);
ACE_UNUSED_ARG (rfds);
ACE_UNUSED_ARG (wfds);
ACE_UNUSED_ARG (efds);
ACE_UNUSED_ARG (timeout);
ACE_NOTSUP_RETURN (-1);
#elif defined(ACE_TANDEM_T1248_PTHREADS)
ACE_SOCKCALL_RETURN (::spt_select (width, rfds, wfds, efds, timep),
int, -1);
#else
ACE_SOCKCALL_RETURN (::select (width, rfds, wfds, efds, timep),
int, -1);
#endif
}