在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 }