调试C++NPv2_Reactor_Log_Server程序
调试C++NPv2_Reactor_Log_Server程序,main函数中会创建一个ACE_Reactor对象,在其构造函数中将其成员变量ACE_Reactor_Impl *implementation_;赋值为ACE_WFMO_Reactor对象,所以需要构造ACE_WFMO_Reactor对象。
ACE_WFMO_Reactor构造函数中会调用其成员函数open,在该函数中先初始化成员变量notify_handler_为ACE_WFMO_Reactor_Notify对象,然后调用ACE_WFMO_Reactor_Notify::open函数,最后会调用ACE_WFMO_Reactor::register_handler函数进而调用ACE_WFMO_Reactor_Handler_Repository::bind_i函数,将ACE_WFMO_Reactor_Notify对象ACE_Reactor_Notify *notify_handler_;保存在ACE_WFMO_Reactor_Handler_Repository类的To_Be_Added_Info *to_be_added_info_;成员变量中 。
同样调用ACE_WFMO_Reactor::register_handler函数注册ACE_Manual_Event wakeup_all_threads_;事件。然后在ACE_WFMO_Reactor::open函数的最后根据注释"Since we have added two handles into the handler repository,update the <handler_repository_>"更新了ACE_WFMO_Reactor_Handler_Repository handler_rep_;在这类主要是将前面添加到to_be_added_info_的两项赋值到current_info_中,然后ACE_WFMO_Reactor::open中将其成员变量wakeup_all_threads_重置。
main函数中在创建Server_Logging_Daemon对象时,会调用examples\C++NPv2\Logging_Acceptor.cpp文件中的Logging_Acceptor::open函数,Logging_Acceptor类继承自ACE_Event_Handler类,该函数中调用ACE_Reactor::register_handler函数进而调用ACE_WFMO_Reactor::register_handler函数,同样会调用ACE_WFMO_Reactor::register_handler_i函数并调用ACE_WFMO_Reactor_Handler_Repository::bind_i,将ACE_Event_Handler即其子类Logging_Acceptor添加到ACE_WFMO_Reactor_Handler_Repository类的handles_to_be_added_成员变量中,然后调用ACE_WFMO_Reactor::wakeup_all_threads即将ACE_WFMO_Reactor的成员变量ACE_Manual_Event wakeup_all_threads_;置为有信号状态。
main函数中调用ACE_Reactor::run_reactor_event_loop,该函数中调用ACE_WFMO_Reactor::handle_events函数进而调用ACE_WFMO_Reactor::event_handling函数,该函数中调用ACE_WFMO_Reactor::wait_for_multiple_events函数,WaitForMultipleObjects等待ACE_WFMO_Reactor::open中添加的两个ACE_HANDLE,其中第一个为ACE_WFMO_Reactor_Notify::get_handle返回的成员变量ACE_Auto_Event wakeup_one_thread_;初始化为无信号状态,第二个为ACE_Manual_Event wakeup_all_threads_;的句柄,由于在Logging_Acceptor::open函数中会调用到ACE_WFMO_Reactor_Handler_Repository::bind_i并将ACE_WFMO_Reactor的成员变量ACE_Manual_Event wakeup_all_threads_;置为有信号状态。所以这里会返回1。
然后在ACE_WFMO_Reactor::event_handling函数中调用ACE_WFMO_Reactor::safe_dispatch,该函数首先调用ACE_WFMO_Reactor::dispatch,然后由于__try、__finally机制调用ACE_WFMO_Reactor::update_state。
ACE_WFMO_Reactor::dispatch函数调用ACE_WFMO_Reactor::dispatch_handles函数,该函数中循环调用ACE_WFMO_Reactor::dispatch_handler,进而调用ACE_WFMO_Reactor::simple_dispatch_handler,该函数中调用虚函数ACE_Event_Handler::handle_signal,由于多态这里调用的是ACE_Wakeup_All_Threads_Handler::handle_signal函数不做任何处理。
ACE_WFMO_Reactor::update_state函数中对ACE_Process_Mutex lock_;变量使用ACE_GUARD_RETURN宏锁定,lock_成员变量的定义处有两点注释,一个是不用ACE_Thread_Mutex是因为其实现为临界区,临界区不用于::WaitForMultipleObjects函数,另一个是并不是一个进程互斥量,因为没有名字而且其他进程也不能用到这个变量。ACE_WFMO_Reactor::update_state函数中主要是对ACE_WFMO_Reactor_Handler_Repository handler_rep_;进行更新,因为之前Logging_Acceptor::open函数添加了继承自ACE_Event_Handler类的Logging_Acceptor,将其从ACE_WFMO_Reactor_Handler_Repository的to_be_added_info_中更新到current_info_中。然后重置ACE_WFMO_Reactor的ACE_Manual_Event wakeup_all_threads_;成员变量。这样当下一次调用ACE_WFMO_Reactor::event_handling时就会阻塞到ACE_WFMO_Reactor::wait_for_multiple_events函数的WaitForMultipleObjectsEx,此处将等待ACE_WFMO_Reactor::open中添加的两个ACE_HANDLE以及监听套接字关联的ACE_HANDLE。
主程序进行监听会阻塞在ACE_WFMO_Reactor::event_handling函数中调用ACE_WFMO_Reactor::wait_for_multiple_events的地方即WaitForMultipleObjects处。当有一个客户端进行连接时,ACE_WFMO_Reactor::wait_for_multiple_events函数返回调用ACE_WFMO_Reactor::safe_dispatch处理客户端的连接。在ACE_WFMO_Reactor::dispatch_handler函数中判断当前是一个io_entry_即IO事件,所以调用ACE_WFMO_Reactor::complex_dispatch_handler函数,根据事件类型调用Logging_Acceptor_Ex::handle_input函数处理连接请求,然后将新建立的连接作为参数调用ACE_WFMO_Reactor::register_handler注册到ACE_WFMO_Reactor_Handler_Repository的to_be_added_info_中。与监听套接字一样关心连接套接字,继承自类ACE_Event_Handler的Logging_Acceptor类和Logging_Event_Handler类分别对相应事件进行处理。
调试C++NPv2_Reactor_Log_Server例子,客户端成功连接服务端后,服务端ACE_WFMO_Reactor::event_handling函数调用ACE_WFMO_Reactor::wait_for_multiple_events函数阻塞在::WaitForMultipleObjectsEx函数中,当客户端发送数据服务端从ACE_WFMO_Reactor::wait_for_multiple_events返回3,3代表的是收到数据,因为调试为了简单就一个服务端和一个客户端。然后服务端调用ACE_WFMO_Reactor::safe_dispatch进行处理,本以为处理之后下一次服务端ACE_WFMO_Reactor::event_handling函数调用ACE_WFMO_Reactor::wait_for_multiple_events函数会阻塞,结果并没有阻塞而是又一次返回3,服务端再次调用调用ACE_WFMO_Reactor::safe_dispatch进行处理。然后再一次的服务端ACE_WFMO_Reactor::event_handling函数调用ACE_WFMO_Reactor::wait_for_multiple_events函数会阻塞。就是本以为客户端发送一次数据后服务端ACE_WFMO_Reactor::event_handling函数调用ACE_WFMO_Reactor::wait_for_multiple_events函数只返回一次结果却返回两次。
当客户端发送数据到服务端,服务端ACE_WFMO_Reactor::event_handling函数调用ACE_WFMO_Reactor::wait_for_multiple_events函数返回后,调用ACE_WFMO_Reactor::safe_dispatch进行处理,在该例子中最终是调用Logging_Event_Handler_Ex::handle_input函数,该函数调用父类函数Logging_Event_Handler::handle_input进行处理,进而调用examples\C++NPv2\Logging_Handler.cpp文件中的Logging_Handler::log_record函数。
该函数与C++NPv1_Iterative_Logging_Server例子中的对应文件中的对应函数一致。用C++NPv1_Logging_Client例子作为客户端。该程序中发送数据时用iovec结构体进行封装了数据的长度和内容,数据长度固定用8个字节来表示,当服务端即Logging_Handler::log_record函数中接收数据时先接收8个字节的长度,再根据长度来接收数据。
一开始直接在Logging_Event_Handler_Ex::handle_input函数中屏蔽掉调用父类函数的语句,直接定义一个512字节的缓存,接收512个数据,用recv((SOCKET)h, recvbuf, 512, 0)语句来接收客户端发送的数据,然后服务端ACE_WFMO_Reactor::event_handling函数调用ACE_WFMO_Reactor::wait_for_multiple_events函数只触发了一次,当接收到数据后再次执行到这里时就会阻塞在ACE_WFMO_Reactor::wait_for_multiple_events函数中的WaitForMultipleObjectsEx语句。
同样如果Logging_Event_Handler_Ex::handle_input修改成recv((SOCKET)h, recvbuf, 8, 0);return 0;这样的语句,这样服务端ACE_WFMO_Reactor::event_handling函数调用ACE_WFMO_Reactor::wait_for_multiple_events函数被触发的就不仅仅只是两次了,而是很多次。原本当服务端收到客户端发送的数据被触发后,会调用到ACE_WFMO_Reactor::complex_dispatch_handler函数,该函数会调用ACE_WFMO_Reactor::upcall函数,进而调用Logging_Event_Handler_Ex::handle_input函数,ACE_WFMO_Reactor::upcall函数中接收数据并且如果返回结果<= 0就清除接收标记,ACE_WFMO_Reactor::complex_dispatch_handler函数中根据标记循环调用ACE_WFMO_Reactor::upcall。所以如果像上面没有接收完数据并return 0;的,下一次还会触发服务端ACE_WFMO_Reactor::event_handling函数调用ACE_WFMO_Reactor::wait_for_multiple_events函数继续进行处理。
根据上面的分析看来socket可读的标记并非边缘触发,即并不是触发一次。只要没有读到数据尾都可以触发调用WSAEventSelect绑定到socket的事件。因为examples\C++NPv2\Logging_Handler.cpp文件中的Logging_Handler::log_record函数先接收8个字节的长度,再根据长度来接收数据。虽然把数据全部都接收了,但是并没有读到边界,所以跟socket关联的事件未被重置(这些可能是猜测)。
调试运行C++NPv2_WFMO_Reactor_Log_Server例子,examples\C++NPv2\WFMO_Reactor_Logging_Server.cpp文件中的main函数内定义Quit_Handler变量,同文件内定义Quit_Handler类,继承自ACE_Event_Handler类,Quit_Handler类的构造函数中先将其成员变量ACE_Manual_Event quit_seen_;通过ACE_Reactor类注册到ACE_WFMO_Reactor类,然后将this作为参数调用ACE_Event_Handler::register_stdin_handler,该函数中启动一个线程来处理控制台的输入。
当用户在控制台输入quit后控制台线程结束将Quit_Handler的成员变量ACE_Manual_Event quit_seen_;置为有信号状态,由于该事件为手动重置,所以ACE_WFMO_Reactor::event_handling函数中检测到该事件后都会调用该事件的相关处理函数,由于虚函数的多态性,在这里是Quit_Handler::handle_signal。该函数中调用ACE_Reactor::end_reactor_event_loop,main函数中创建线程调用ACE_Reactor::run_reactor_event_loop循环处理,此处结束线程。
由于是手动重置事件,所以main函数中创建的所有的线程都可以执行Quit_Handler::handle_signal来退出当前这个线程。该例子调用SetConsoleMode并用ENABLE_LINE_INPUT作为参数,msdn看了下说ENABLE_LINE_INPUT的作用是使得ReadFile或者ReadConsole函数只有督导一个回车键才返回。
总结C++NPv2_WFMO_Reactor_Log_Server例子就是控制台输入线程读到退出字符结束当前线程,然后触发Quit_Handler类的 ACE_Manual_Event quit_seen_;事件,该事件为手动重置,且在类Quit_Handler的构造函数中绑定响应处理函数,所以main函数中创建的各个线程都会响应该事件退出线程。然后主线程等待结束退出程序。这个例子运行后用的线程总数可以用procexp.exe工具来查看,打开这个工具后双击要查看的线程名即可。线程个数为主线程+控制台线程+main函数中创建的线程的个数。
调试C++NPv2_Reactor_Log_Server例子,ACE_WFMO_Reactor构造函数中调用ACE_WFMO_Reactor::open函数,该函数中将两个成员变量:ACE_Reactor_Notify *notify_handler_;(指向ACE_WFMO_Reactor_Notify对象)及ACE_Wakeup_All_Threads_Handler wakeup_all_threads_handler_;注册到其成员变量ACE_WFMO_Reactor_Handler_Repository handler_rep_;。即调用ACE_WFMO_Reactor_Handler_Repository::bind_i函数。ACE_WFMO_Reactor::open函数会根据ACE_WFMO_Reactor_Handler_Repository handler_rep_;是否需要修改,此处由于添加了两个所以会调用ACE_WFMO_Reactor_Handler_Repository::make_changes函数,进而调用ACE_WFMO_Reactor_Handler_Repository::make_changes_in_to_be_added_infos清空其成员To_Be_Added_Info *to_be_added_info_;。
继续调试运行该程序会执行到examples\C++NPv2\Reactor_Logging_Server_T.cpp文件中的模板类Reactor_Logging_Server的构造函数,会调用examples\C++NPv2\Logging_Acceptor.cpp文件中的Logging_Acceptor::open函数,该函数中调用ACE_Reactor::register_handler函数进而调用ACE_WFMO_Reactor::register_handler函数,最终会也会调用ACE_WFMO_Reactor_Handler_Repository::bind_i将继承自ACE_Event_Handler类的Logging_Acceptor添加到ACE_WFMO_Reactor_Handler_Repository中。
函数ACE_WFMO_Reactor_Handler_Repository::bind_i的最后会调用ACE_WFMO_Reactor::wakeup_all_threads函数,将ACE_WFMO_Reactor的成员变量ACE_Manual_Event wakeup_all_threads_;置为有信号状态。main函数中继续运行调用ACE_Reactor::run_reactor_event_loop函数,在这个函数的while循环中会不断调用ACE_WFMO_Reactor::handle_events函数进而调用ACE_WFMO_Reactor::event_handling函数,由于ACE_WFMO_Reactor_Handler_Repository::bind_i的最后将ACE_WFMO_Reactor的成员变量ACE_Manual_Event wakeup_all_threads_;置为有信号状态,所以ACE_WFMO_Reactor::event_handling函数中调用ACE_WFMO_Reactor::wait_for_multiple_events会返回,然后调用ACE_WFMO_Reactor::safe_dispatch函数,该函数依次调用成员函数dispatch和update_state。这里的dispatch就是调用ACE_Wakeup_All_Threads_Handler::handle_signal函数,该函数不做具体工作直接返回。update_state函数的注释是更新handler repository的状态。然后才会调用ACE_WFMO_Reactor_Handler_Repository::make_changes函数,将第三个添加的继承自ACE_Event_Handler类的Logging_Acceptor对象更新到ACE_WFMO_Reactor_Handler_Repository类的Current_Info *current_info_;成员中,同时更新ACE_HANDLE *current_handles_;成员变量,下一次调用ACE_WFMO_Reactor::event_handling函数中的ACE_WFMO_Reactor::wait_for_multiple_events等待this->handler_rep_.handles ()时,就会等待新加的待关注对象。
调试C++NPv2_WFMO_Reactor_Log_Server例子,该程序在main函数中调用ACE_Thread_Manager::spawn_n创建了N_THREADS个线程,为了方便调试将该值修改为2即创建两个线程。examples\C++NPv2\WFMO_Reactor_Logging_Server.cpp文件中定义了继承自ACE_Event_Handler类的Quit_Handler类,该类的构造函数中调用ACE_Reactor::register_handler,该函数中调用ACE_WFMO_Reactor::register_handler进而调用ACE_WFMO_Reactor_Handler_Repository::bind_i函数。
main函数中以同文件内的event_loop函数作为线程函数调用ACE_Thread_Manager::spawn_n创建线程,event_loop中首先调用ACE_Reactor::owner函数,然后调用ACE_Reactor::run_reactor_event_loop函数。ACE_Reactor::owner函数调用ACE_WFMO_Reactor::owner函数,该函数的实现为先将当前线程的id赋值给ACE_WFMO_Reactor类的成员变量 ACE_thread_t new_owner_;(注释为The owner to be of the WFMO_Reactor),然后将ACE_WFMO_Reactor类的成员变量ACE_Manual_Event wakeup_all_threads_;置为有信号状态。event_loop函数中调用ACE_Reactor::run_reactor_event_loop函数,该函数调用ACE_WFMO_Reactor::handle_events进而调用ACE_WFMO_Reactor::event_handling函数。
同C++NPv2_Reactor_Log_Server例子中一样,当调用ACE_WFMO_Reactor::event_handling函数,等待到事件后调用ACE_WFMO_Reactor::safe_dispatch函数,该函数中执行完ACE_WFMO_Reactor::dispatch后会调用ACE_WFMO_Reactor::update_state,将Logging_Acceptor::open函数和Quit_Handler类的构造函数中会调用ACE_Reactor::register_handler,最终调用ACE_WFMO_Reactor_Handler_Repository::bind_i函数所绑定的事件更新到ACE_WFMO_Reactor_Handler_Repository类的Current_Info *current_info_;及ACE_HANDLE *current_handles_;成员变量中。这是通过调用ACE_WFMO_Reactor_Handler_Repository::make_changes函数实现的。
将C++NPv2_Reactor_Log_Server例子main函数中修改为调用ACE_Thread_Manager::spawn_n创建2个线程,第一个线程函数event_loop最终调用ACE_WFMO_Reactor::event_handling函数,在该函数中当从ACE_WFMO_Reactor::wait_for_multiple_events返回0后,当再一次while循环到这里时会阻塞在这里。第二个线程函数event_loop中会先调用ACE_WFMO_Reactor::owner,该函数中将调用ACE_WFMO_Reactor::wakeup_all_threads将其成员变量ACE_Manual_Event wakeup_all_threads_;置为有信号状态,这样两个线程调用ACE_WFMO_Reactor::event_handling函数,在该函数中调用ACE_WFMO_Reactor::wait_for_multiple_events时都会返回,第一个线程返回1,第二个线程则返回0,原因则是ACE_WFMO_Reactor类成员变量ACE_thread_t owner_;设置为第一个线程的id。
两个线程都会调用ACE_WFMO_Reactor::safe_dispatch函数,该函数先调用ACE_WFMO_Reactor::dispatch函数,再调用ACE_WFMO_Reactor::update_state。ACE_WFMO_Reactor::dispatch函数中会先调用ACE_WFMO_Reactor::expire_timers函数,第二个线程执行到这里时由于线程id不等于ACE_WFMO_Reactor类成员变量ACE_thread_t owner_所以直接返回0,而第一个线程调用到此时则会执行this->timer_queue_->expire(),调试发现在该例子中只做了简单的操作。第二个线程会先执行ACE_WFMO_Reactor::update_state函数,会阻塞在this->waiting_to_change_state_.wait ();语句处,第一个线程调用ACE_WFMO_Reactor::update_state函数,执行this->waiting_to_change_state_.signal ();语句唤醒第二个线程。第二个线程将ACE_WFMO_Reactor类成员变量ACE_thread_t owner_修改为自己的线程id,并执行this->wakeup_all_threads_.reset ();重置该变量。然后两个线程继续while执行ACE_WFMO_Reactor::event_handling,会阻塞在该函数中调用ACE_WFMO_Reactor::wait_for_multiple_events处。