关注微信公众号“池边小树”~~获取更多分析~~(文末二维码~~)
目录
3.5.2 ACE_Select_Reactor_Impl类分析
3.5.3 ACE_Select_Reactor_T类的分析
3 Reactor框架
Reactor框架是ACE各个框架中最基础的一个框架,其他框架都或多或少地用到了Reactor框架。
Reactor框架支持的事件类型包括I/O事件、信号量事件、定时器事件和Notify事件。
3.1 Reactor构架模式
(1)意图
在事件驱动的应用中,将一个或多个客户的服务请求分离(demultiplex)和调度(dispatch)给应用程序。
(2)上下文
在事件驱动的应用中,同步地、有序地处理同事接收的多个服务请求。
(3)问题
在分布式系统尤其是服务器这一类事件驱动应用中,虽然这些请求最终会被序列化的处理,但是必须时刻准备着处理多个同时到来的服务请求。在实际应用中,这些请求总是通过一个事件(如CONNNECT、READ、WRITE等)来表示的。在有序地处理这些服务请求之前,应用程序必须先分离和调度这些同时到达的时间。
(4)解决方案
每个应用程序提供的每个服务都有一个独立的事件处理器与之对应。有事件处理器处理来自事件源的特定类型的事件。每个事件处理器都事先注册到Reactor管理器中。Reactor管理器使用同步事件分离器在一个或多个时间源中等待事件的发生。当事件发生后,同步事件分离器通知Reactor管理器,最后由Reactor 管理器调度和该事件相关的事件处理器来完成请求的服务。
(5)结构
在Reactor模式中,有5个关键的参与者。
- 描述符(handle):由操作系统提供,用于识别每一个事件,如Socket描述符、文件描述符等。在Linux中,它用一个整数来表示。事件可以来自外部,如来自客户端的连接请求、数据等。事件也可以来自外部,如定时器事件。
- 同步事件分离器(demultiplexer):是由一个或多个模板函数组成的接口。这些模板函数描述了和应用程序相关的对某个事件的操作。
- 具体的事件处理器:是事件处理器接口的实现。它实现了应用程序提供的某个服务。每个具体的事件处理器和一个描述符相关。它使用描述符来识别事件、识别应用程序提供的服务。
- Reactor管理器(reactor):定义了一些接口,用于应用程序控制事件的调度,以及应用程序注册、删除事件处理器和相关的描述符。它是事件处理器的调度核心。
【Reactor管理器而不是应用程序负责等待事件、分离事件和调度事件。实际上,Reactor管理器并没有被具体的事件处理器调用,而是管理器调度具体的事件处理器,由事件处理器对发生的事件作出处理。】
Reactor构架模式结构如下图:
图3-1 Reactor构架模式结构图
3.2 Reactor框架概述
从对Reactor构架模式的分析中可以看出,我们如果要自己设计和实现一个简单的Reactor框架以支持I/O事件,需要实现两个组件:事件处理器接口和Reactor管理器。至于其他组件,如同步事件分离器可以使用操作系统提供的select、poll或其他类似的函数;而描述符可以使用文件描述符或其他可以识别事件的数据结构,一般操作系统都会提供。
事件处理器接口包含一系列模板函数,可以根据实际处理的数据进行设计;Reactor管理器肩负着事件的分离和调度,是整个框架设计的核心。
ACE的Reactor框架在Linux平台下①使用文件描述符作为I/O事件的描述符,②使用ACE_Event_Handler类作为各类事件的处理器接口。③将同步事件分离函数放到Reactor管理器中,这样使用不同的同步事件分离函数就需要实现不同的Reactor管理器。
3.3 Reactor框架应用实例
我们先不对这部分总结(可以直接阅读文献[1]2.3节)。
3.4 事件处理器接口实现
事件处理器接口定义了用户需要遵循的框架中的约束,是框架调度过程中不变的部分,是框架设计中非常重要的元素。事件处理器接口中的方法直接定义了框架所提供的功能,由于框架是面向某一应用领域的,所以这些方法也应该和具体的应用领域相匹配。
ACE_Event_Handler接口是Reactor框架的事件处理器接口,它为应用程序的事件处理提供模板函数。ACE_Event_Handler接口的定义如下述清单所示:
class ACE_Export ACE_Event_Handler
{
public:
enum
{
LO_PRIORITY = 0,
HI_PRIORITY = 10,
NULL_MASK = 0,
#if defined (ACE_USE_POLL)
READ_MASK = POLLIN,
WRITE_MASK = POLLOUT,
EXCEPT_MASK = POLLPRI,
#else /* USE SELECT */
READ_MASK = (1 << 0),
WRITE_MASK = (1 << 1),
EXCEPT_MASK = (1 << 2),
#endif /* ACE_USE_POLL */
ACCEPT_MASK = (1 << 3),
CONNECT_MASK = (1 << 4),
TIMER_MASK = (1 << 5),
QOS_MASK = (1 << 6),
GROUP_QOS_MASK = (1 << 7),
SIGNAL_MASK = (1 << 8),
ALL_EVENTS_MASK = READ_MASK |
WRITE_MASK |
EXCEPT_MASK |
ACCEPT_MASK |
CONNECT_MASK |
TIMER_MASK |
QOS_MASK |
GROUP_QOS_MASK |
SIGNAL_MASK,
RWE_MASK = READ_MASK |
WRITE_MASK |
EXCEPT_MASK,
DONT_CALL = (1 << 9)
};
/// Destructor is virtual to enable proper cleanup.
virtual ~ACE_Event_Handler (void);
/// Get the I/O handle.
virtual ACE_HANDLE get_handle (void) const;
/// Set the I/O handle.
virtual void set_handle (ACE_HANDLE);
// = Get/set priority
/// Get the priority of the Event_Handler.
/// @note Priorities run from MIN_PRIORITY (which is the "lowest priority")
/// to MAX_PRIORITY (which is the "highest priority").
virtual int priority (void) const;
/// Set the priority of the Event_Handler.
virtual void priority (int priority);
/// Called when input events occur (e.g., connection or data).
virtual int handle_input (ACE_HANDLE fd = ACE_INVALID_HANDLE);
/// Called when output events are possible (e.g., when flow control
/// abates or non-blocking connection completes).
virtual int handle_output (ACE_HANDLE fd = ACE_INVALID_HANDLE);
/// Called when an exceptional events occur (e.g., SIGURG).
virtual int handle_exception (ACE_HANDLE fd = ACE_INVALID_HANDLE);
/**
* Called when timer expires. @a current_time represents the current
* time that the Event_Handler was selected for timeout
* dispatching and @a act is the asynchronous completion token that
* was passed in when <schedule_timer> was invoked.
*/
virtual int handle_timeout (const ACE_Time_Value ¤t_time,
const void *act = 0);
/// Called when a process exits.
virtual int handle_exit (ACE_Process *);
/// Called when a handle_*() method returns -1 or when the
/// remove_handler() method is called on an ACE_Reactor. The
/// @a close_mask indicates which event has triggered the
/// handle_close() method callback on a particular @a handle.
virtual int handle_close (ACE_HANDLE handle,
ACE_Reactor_Mask close_mask);
/// Called when object is signaled by OS (either via UNIX signals or
/// when a Win32 object becomes signaled).
virtual int handle_signal (int signum, siginfo_t * = 0, ucontext_t * = 0);
enum
{
/// The handler is not resumed at all. Could lead to deadlock..
ACE_EVENT_HANDLER_NOT_RESUMED = -1,
/// The reactor takes responsibility of resuming the handler and
/// is the default
ACE_REACTOR_RESUMES_HANDLER = 0,
/// The application takes responsibility of resuming the handler
ACE_APPLICATION_RESUMES_HANDLER
};
/**
* Called to figure out whether the handler needs to resumed by the
* reactor or the application can take care of it. The default
* value of 0 would be returned which would allow the reactor to
* take care of resumption of the handler. The application can
* return a value more than zero and decide to resume the handler
* themselves.
*
* @note This method has an affect only when used with the
* ACE_Dev_Poll_Reactor (and then, only on Linux) or the ACE_TP_Reactor.
*/
virtual int resume_handler (void);
virtual int handle_qos (ACE_HANDLE = ACE_INVALID_HANDLE);
virtual int handle_group_qos (ACE_HANDLE = ACE_INVALID_HANDLE);
// = Accessors to set/get the various event demultiplexors.
/// Set the event demultiplexors.
virtual void reactor (ACE_Reactor *reactor);
/// Get the event demultiplexors.
virtual ACE_Reactor *reactor (void) const;
/// Get only the reactor's timer related interface.
virtual ACE_Reactor_Timer_Interface *reactor_timer_interface (void) const;
/**
* Used to read from non-socket ACE_HANDLEs in our own thread to
* work around Win32 limitations that don't allow us to <select> on
* non-sockets (such as ACE_STDIN). This is commonly used in
* situations where the Reactor is used to demultiplex read events
* on ACE_STDIN on UNIX. Note that @a event_handler must be a
* subclass of ACE_Event_Handler. If the get_handle() method of
* this event handler returns ACE_INVALID_HANDLE we default to
* reading from ACE_STDIN.
*/
static ACE_THR_FUNC_RETURN read_adapter (void *event_handler);
/**
* Abstracts away from the differences between Win32 and ACE with
* respect to reading from ACE_STDIN, which is non-<select>'able on
* Win32.
*/
static int register_stdin_handler (ACE_Event_Handler *eh,
ACE_Reactor *reactor,
ACE_Thread_Manager *thr_mgr,
int flags = THR_DETACHED);
/// Performs the inverse of the register_stdin_handler() method.
static int remove_stdin_handler (ACE_Reactor *reactor,
ACE_Thread_Manager *thr_mgr);
/// Reference count type.
typedef long Reference_Count;
/// Increment reference count on the handler.
/**
* This method is called when the handler is registered with the
* Reactor and when the Reactor makes an upcall on the handler.
* Reference count is 1 when the handler is created.
*
* @return Current reference count.
*/
virtual Reference_Count add_reference (void);
/// Decrement reference count on the handler.
/**
* This method is called when the handler is removed from the
* Reactor and when an upcall made on the handler by the Reactor
* completes. Handler is deleted when the reference count reaches
* 0.
*
* @return Current reference count.
*/
virtual Reference_Count remove_reference (void);
/**
* @class Policy
*
* @brief Base class for all handler policies.
*/
class ACE_Export Policy
{
public:
/// Virtual destructor.
virtual ~Policy (void);
};
/**
* @class Reference_Counting_Policy
*
* @brief
* This policy dictates the reference counting requirements
* for the handler.
*
* This policy allows applications to configure whether it wants the
* Reactor to call add_reference() and remove_reference() during
* registrations, removals, and upcalls.
*
* <B>Default:</B> DISABLED.
*/
class ACE_Export Reference_Counting_Policy : public Policy
{
/// This policy can only be created by the handler.
friend class ACE_Event_Handler;
public:
enum Value
{
/// Perform reference counting.
ENABLED,
/// Don't perform reference counting.
DISABLED
};
/// Current Reference_Counting_Policy.
Value value (void) const;
/// Update Reference_Counting_Policy.
void value (Value value);
private:
/// Private constructor.
Reference_Counting_Policy (Value value);
/// The value of the policy.
Value value_;
};
/// Current Reference_Counting_Policy.
Reference_Counting_Policy &reference_counting_policy (void);
protected:
/// Force ACE_Event_Handler to be an abstract base class.
ACE_Event_Handler (ACE_Reactor * = 0,
int priority = ACE_Event_Handler::LO_PRIORITY);
/// Typedef for implementation of reference counting.
typedef ACE_Atomic_Op<ACE_SYNCH_MUTEX, Reference_Count> Atomic_Reference_Count;
/// Reference count.
Atomic_Reference_Count reference_count_; // 事件处理器的引用计数
private:
/// Priority of this Event_Handler.
int priority_; // 事件处理器的优先级
/// Pointer to the various event demultiplexors.
ACE_Reactor *reactor_; // 指向Reactor管理器
/// Reference counting requirements.//一个Policy,用于保存事件处理器的引用计数属性
Reference_Counting_Policy reference_counting_policy_;
};
ACE_Event_Handler是一个接口,用于封装Reactor框架支持的I/O事件、信号量事件、定时器事件和Notify事件:
①每一个事件处理器都有一个优先级,用于支持有优先级的时间调度器;
②事件处理器具有引用计数功能,这是因为同一个事件处理器可以被多次使用,比如一个处理I/O的事件处理器可以用作一个定时器。引用计数可以追踪事件处理器的使用计数,当使用计数为0时,框架会删除事件处理器。是否启用引用计数功能,取决于应用程序。在默认情况下,引用计数功能是关闭的,引用计数功能是否开启的属性保存在一个Policy中。
ACE_Event_Handler接口中事件的类型和属性见上述代码清单中的枚举项,对这些事件的类型和属性的描述,见表3-1。
表3-1 ACE_Event_Handler接口中事件的类型和属性描述
ACE_Event_Handler接口的主要功能是提供事件处理的模板函数,它们用于处理不同的事件,这些模板函数的功能如表3-2所示。
表3-2 事件处理函数与处理事件对应关系
注:事件处理器接口的使用规范
①应用程序的事件处理器必须继承ACE_Event_Handler接口,然后注册到框架中,才能被框架调用。
②ACE_Event_Handler接口的模板函数会被Reactor管理器调用。对于handle_xxx()函数(handle_close除外),框架为它们预设了返回值;小于0,等于0和大于0.
- 返回值小于0:表示处理失败,或者虽然处理成功但需要执行关闭操作。如果使用了DONT_CAIL标志,Reactor管理器在不调用handle_close函数的情况下将该事件处理器从Reactor管理器中删除;否则Reactor管理器会调用handle_close函数,同事将该事件处理器从Reactor管理器中删除。
- 返回值等于0:表示处理正常,Reactor管理器继续等待事件。
- 返回值大于0:表示在不等待下一个事件的情况下,继续调用该事件处理器。
③Reactor框架建议应用程序使用handle_close而不是delete来释放资源。
④为了提供更大的灵活性,Reactor框架还在ACE_Event_Handler中使用了引用计数功能。如果应用程序使能了事件处理器的应用计数功能,那么一旦事件处理器的引用计数为0,Reactor管理器会自动删除事件处理器。
3.5 Reactor 管理器的设计分析
框架管理器是整个框架的核心。一方面,它需要向应用程序提供接口,这样应用程序才能访问框架;另一方面,它又需要管理框架内的所有事件,完成框架提供的领域的功能。ACE的Reactor框架使用ACE_Reactor类作为框架管理器,应用程序只能通过ACE_Reactor类中的方法注册和调度事件。
ACE Reactor框架的分离器直接使用了操作系统的分离函数。Reactor管理器直接调用分离函数进行事件分离,然后调度事件。为了将分离函数和Reactor管理器解耦,Reactor管理器使用了Bridge设计模式。ACE Reactor框架支持多种不同的分离函数,每一种分离函数都有一个管理器实现与之对应。
- ACE_Dev_Poll_Reactor:基于poll调用的Reactor管理器。
- ACE_Select_Reactor_T:基于select调用的Reactor管理器。
- ACE_WFMO_Reactor:基于WaitForMultipleObjects调用的Reactor管理器。
- ACE_TP_Reactor:基于select调用,采用线程池调度的Reactor管理器。
下面把基于select调用的Reactor管理器作为分析的例子。整个Reactor管理器由多个类组成,它们的关系如图3-2所示。
图3-2 Reactor管理器结构图
从图中可以看出:
①ACE_Reactor类使用Facade设计模式,定义了用于应用程序访问ACE Reactor框架的接口;
②ACE_Reactor_Impl是一个接口类,为不同类型的Reactor管理器提供统一的接口,在Bridge设计模式中,它就是参与者Implementor;
③ACE_Select_Reactor_Impl也是一个接口,它为不同同步类型的管理器提供数据成员和统一的接口;
④ACE_Select_Reactor_T是基于select分离函数的Reactor管理器的真正实现,它是一个模板类,模板函数用于定义同步的类型。
整个Reactor管理器除了包含图3-2中所示的控制类以外,还包含很多辅助类。
3.5.1 Reactor管理器接口分析
ACE_Reactor类是Reactor管理器面向应用程序的接口。它使用Facade设计模式来简化应用程序对Reactor框架的访问,同时又采用Bridge设计模式来实现跨平台的功能。
ACE_Reactor是一个接口转发类,除了构造和析构函数,其他函数都是通过数据成员implementation_来实现。ACE_Reactor还采用了Singleton设计模式。下面简单给出ACE_Reactor类的定义代码清单(全部的定义太多,没有必要全部列出来,按照文献[1]中给出的清单列出):
// 代码在ace/Reactor.h中
class ACE_Export ACE_Reactor : public ACE_Reactor_Timer_Interface
{
public:
// ...省略
// Singleton设计模式接口由两个instance函数提供。
/* instance函数采用Double-checked锁模式,该模式应用于全局变量的初始化,即可以避免不必要的同步,又可以保护临界区的安全。*/
/*instance函数内部的锁要在instance函数被调用之前就完成初始化。ACE使用static对象来解决“框架必须的初始化工作”的问题,static对象会在main函数之前完成初始化,这些static对象由ACE_Object_Manager类管理。同样对于框架必需的清理工作,也可以由static对象来完成,因为static对象会在main函数之后被释放。*/
/// Get pointer to a process-wide ACE_Reactor.
static ACE_Reactor *instance (void);
/**
* Set pointer to a process-wide ACE_Reactor and return existing
* pointer. If @a delete_reactor == true then we'll delete the Reactor
* at destruction time.
*/
static ACE_Reactor *instance (ACE_Reactor *, bool delete_reactor = false);
/**
* Run the event loop until the
* <ACE_Reactor::handle_events/ACE_Reactor::alertable_handle_events>
* method returns -1 or the end_event_loop() method is invoked.
* Note that this method can only be used by the singleton
* ACE_Reactor::instance(). Thus, to run another reactor use
* <ACE_Reactor::run_reactor_event_loop>.
*
* @deprecated Use ACE_Reactor::instance()->run_reactor_event_loop() instead
*/
static int run_event_loop (void);
static int run_alertable_event_loop (void);
/**
* Run the event loop until the ACE_Reactor::handle_events() or
* <ACE_Reactor::alertable_handle_events> methods returns -1, the
* end_event_loop() method is invoked, or the ACE_Time_Value
* expires. Note that this method can only be used by the singleton
* ACE_Reactor::instance(). Thus, to run another reactor use
* <ACE_Reactor::run_reactor_event_loop>.
*
* @deprecated Use ACE_Reactor::instance()->run_reactor_event_loop() instead
*/
static int run_event_loop (ACE_Time_Value &tv);
static int run_alertable_event_loop (ACE_Time_Value &tv);
/**
* Instruct the ACE_Reactor::instance() to terminate its event loop
* and notifies the ACE_Reactor::instance() so that it can wake up
* and close down gracefully. Note that this method can only be
* used by the singleton ACE_Reactor::instance(). Thus, to
* terminate another reactor, use
* <ACE_Reactor::end_reactor_event_loop>.
*
* @deprecated Use ACE_Reactor::instance()->end_reactor_event_loop() instead
*/
static int end_event_loop (void);
/**
* Report if the ACE_Reactor::instance()'s event loop is finished.
* Note that this method can only be used by the singleton
* ACE_Reactor::instance(). Thus, to check another reactor use
* <ACE_Reactor::reactor_event_loop_done>.
*
* @deprecated Use ACE_Reactor::instance()->reactor_event_loop_done() instead
*/
static int event_loop_done (void);
/**
* Resets the ACE_Reactor::end_event_loop_ static so that the
* run_event_loop() method can be restarted. Note that this method
* can only be used by the singleton ACE_Reactor::instance(). Thus,
* to reset another reactor use ACE_Reactor::reset_reactor_event_loop().
*
* @deprecated Use ACE_Reactor::instance()->reset_reactor_event_loop()
* instead
*/
static void reset_event_loop (void);
/**
* The singleton reactor is used by the ACE_Service_Config.
* Therefore, we must check for the reconfiguration request and
* handle it after handling an event.
*/
static int check_reconfiguration (ACE_Reactor *);
// = Reactor event loop management methods.
// These methods work with an instance of a reactor.
/**
* Run the event loop until the
* ACE_Reactor::handle_events()/ACE_Reactor::alertable_handle_events()
* method returns -1 or the end_reactor_event_loop() method is invoked.
*/
int run_reactor_event_loop (REACTOR_EVENT_HOOK = 0);
int run_alertable_reactor_event_loop (REACTOR_EVENT_HOOK = 0);
/**
* Run the event loop until the ACE_Reactor::handle_events() or
* <ACE_Reactor::alertable_handle_events> methods returns -1, the
* end_reactor_event_loop() method is invoked, or the ACE_Time_Value
* expires.
*/
int run_reactor_event_loop (ACE_Time_Value &tv,
REACTOR_EVENT_HOOK = 0);
int run_alertable_reactor_event_loop (ACE_Time_Value &tv,
REACTOR_EVENT_HOOK = 0);
/**
* Instruct the Reactor to terminate its event loop and notifies the
* Reactor so that it can wake up and deactivate
* itself. Deactivating the Reactor would allow the Reactor to be
* shutdown gracefully. Internally the Reactor calls deactivate ()
* on the underlying implementation.
* Any queued notifications remain queued on return from this method.
* If the event loop is restarted in the future, the notifications
* will be dispatched then. If the reactor is closed or deleted without
* further dispatching, the notifications will be lost.
*/
int end_reactor_event_loop (void);
/// Indicate if the Reactor's event loop has been ended.
int reactor_event_loop_done (void);
/// Resets the ACE_Reactor::end_event_loop_ static so that the
/// run_event_loop() method can be restarted.
void reset_reactor_event_loop (void);
/**
* Create the Reactor using @a implementation. The flag
* @a delete_implementation tells the Reactor whether or not to
* delete the @a implementation on destruction.
*/
ACE_Reactor (ACE_Reactor_Impl *implementation = 0,
bool delete_implementation = false);
/// Close down and release all resources.
/**
* Any notifications that remain queued on this reactor instance are
* lost.
*/
virtual ~ACE_Reactor (void);
/**
* Initialize the ACE_Reactor to manage @a max_number_of_handles.
* If @a restart is false then the ACE_Reactor's handle_events()
* method will be restarted automatically when @c EINTR occurs. If
* @a signal_handler or @a timer_queue are non-0 they are used as the
* signal handler and timer queue, respectively.
*/
int open (size_t max_number_of_handles,
bool restart = false,
ACE_Sig_Handler *signal_handler = 0,
ACE_Timer_Queue *timer_queue = 0);
/// Use a user specified signal handler instead.
int set_sig_handler (ACE_Sig_Handler *signal_handler);
/// Set a user-specified timer queue.
int timer_queue (ACE_Timer_Queue *tq);
/// Return the current ACE_Timer_Queue.
ACE_Timer_Queue *timer_queue (void) const;
/// Close down and release all resources.
int close (void);
// = Event loop drivers.
/**
* Returns non-zero if there are I/O events "ready" for dispatching,
* but does not actually dispatch the event handlers. By default,
* don't block while checking this, i.e., "poll".
*/
int work_pending (const ACE_Time_Value &max_wait_time = ACE_Time_Value::zero);
/**
* This event loop driver blocks for up to @a max_wait_time before
* returning. It will return earlier if events occur. Note that
* @a max_wait_time can be 0, in which case this method blocks
* indefinitely until events occur.
*
* @a max_wait_time is decremented to reflect how much time this call
* took. For instance, if a time value of 3 seconds is passed to
* handle_events and an event occurs after 2 seconds,
* @a max_wait_time will equal 1 second. This can be used if an
* application wishes to handle events for some fixed amount of
* time.
*
* Returns the total number of timers and I/O ACE_Event_Handlers
* that were dispatched, 0 if the @a max_wait_time elapsed without
* dispatching any handlers, or -1 if an error occurs.
*
* The only difference between alertable_handle_events() and
* handle_events() is that in the alertable case, the eventloop will
* return when the system queues an I/O completion routine or an
* Asynchronous Procedure Call.
*/
int handle_events (ACE_Time_Value *max_wait_time = 0);
int alertable_handle_events (ACE_Time_Value *max_wait_time = 0);
/**
* This method is just like the one above, except the
* @a max_wait_time value is a reference and can therefore never be
* NULL.
*
* The only difference between alertable_handle_events() and
* handle_events() is that in the alertable case, the eventloop will
* return when the system queues an I/O completion routine or an
* Asynchronous Procedure Call.
*/
int handle_events (ACE_Time_Value &max_wait_time);
int alertable_handle_events (ACE_Time_Value &max_wait_time);
// = Register and remove handlers.
/**
* Register handler for I/O events.
*
* A handler can be associated with multiple handles. A handle
* cannot be associated with multiple handlers.
*
* The handle will come from ACE_Event_Handler::get_handle().
*
* Reactor will call ACE_Event_Handler::add_reference() for a new
* handler/handle pair.
*
* If this handler/handle pair has already been registered, any new
* masks specified will be added. In this case,
* ACE_Event_Handler::add_reference() will not be called.
*
* If the registered handler is currently suspended, it will remain
* suspended. When the handler is resumed, it will have the
* existing masks plus any masks added through this call. Handlers
* do not have partial suspensions.
*/
int register_handler (ACE_Event_Handler *event_handler,
ACE_Reactor_Mask mask);
/**
* Register handler for I/O events.
*
* Same as register_handler(ACE_Event_Handler*,ACE_Reactor_Mask),
* except handle is explicitly specified.
*/
int register_handler (ACE_HANDLE io_handle,
ACE_Event_Handler *event_handler,
ACE_Reactor_Mask mask);
#if defined (ACE_WIN32)
/**
* Register handler for OS events.
*
* Register an @a event_handler that will be notified when
* <event_handle> is signaled. This will call back its
* <handle_signal> hook method.
*
* Reactor will call ACE_Event_Handler::add_reference() for a new
* handler/handle pair.
*
* This interface is only available Win32 platforms because
* ACE_HANDLE is an int on non-Win32 platforms and compilers are not
* able to tell the difference between
* register_handler(ACE_Event_Handler*,ACE_Reactor_Mask) and
* register_handler(ACE_Event_Handler*,ACE_HANDLE).
*/
int register_handler (ACE_Event_Handler *event_handler,
ACE_HANDLE event_handle = ACE_INVALID_HANDLE);
#endif /* ACE_WIN32 */
/**
* Register handler for I/O events.
*
* Similar to
* register_handler(ACE_HANDLE,ACE_Event_Handler*,ACE_Reactor_Mask),
* except that the user gets to specify the event handle that will
* be used for this registration. This only applies to Reactors
* that use event handles for I/O registrations.
*/
int register_handler (ACE_HANDLE event_handle,
ACE_HANDLE io_handle,
ACE_Event_Handler *event_handler,
ACE_Reactor_Mask mask);
/**
* Register handler for multiple I/O events.
*
* Shorthand for calling
* register_handler(ACE_HANDLE,ACE_Event_Handler*,ACE_Reactor_Mask),
* multiple times for the same @a event_handler and @a masks but
* different @a handles.
*/
int register_handler (const ACE_Handle_Set &handles,
ACE_Event_Handler *event_handler,
ACE_Reactor_Mask masks);
/**
* Register handler for signals.
*
* Register @a new_sh to handle the signal @a signum using the
* @a new_disp. Returns the @a old_sh that was previously registered
* (if any), along with the @a old_disp of the signal handler.
*
* Reactor will call ACE_Event_Handler::add_reference() on @a new_sh
* and ACE_Event_Handler::remove_reference() on @a old_sh.
*/
int register_handler (int signum,
ACE_Event_Handler *new_sh,
ACE_Sig_Action *new_disp = 0,
ACE_Event_Handler **old_sh = 0,
ACE_Sig_Action *old_disp = 0);
/**
* Register handler for multiple signals.
*
* Shorthand for calling
* register_handler(int,ACE_Event_Handler*,ACE_Sig_Action*,ACE_Event_Handler**,ACE_Sig_Action*)
* multiple times for the same @a event_handler and @a sig_action but
* different <signals>.
*/
int register_handler (const ACE_Sig_Set &sigset,
ACE_Event_Handler *event_handler,
ACE_Sig_Action *sig_action = 0);
/**
* Remove @a masks from @a handle registration.
*
* For I/O handles, @a masks are removed from the Reactor. Unless
* @a masks includes ACE_Event_Handler::DONT_CALL,
* ACE_Event_Handler::handle_close() will be called with the @a masks
* that have been removed. If all masks have been removed,
* ACE_Event_Handler::remove_reference() will be called.
*
* For OS handles, the @a handle is removed from the Reactor. Unless
* @a masks includes ACE_Event_Handler::DONT_CALL,
* ACE_Event_Handler::handle_close() will be called with
* ACE_Event_Handler::NULL_MASK.
* ACE_Event_Handler::remove_reference() will also be called.
*/
int remove_handler (ACE_HANDLE handle,
ACE_Reactor_Mask masks);
/**
* Remove @a masks from @a event_handler registration.
*
* Same as remove_handler(ACE_HANDLE,ACE_Reactor_Mask), except
* @a handle comes from ACE_Event_Handler::get_handle().
*/
int remove_handler (ACE_Event_Handler *event_handler,
ACE_Reactor_Mask masks);
/**
* Remove @a masks from multiple <handle> registrations.
*
* Shorthand for calling remove_handler(ACE_HANDLE,ACE_Reactor_Mask)
* multiple times for the same @a masks but different @a handles.
*/
int remove_handler (const ACE_Handle_Set &handles,
ACE_Reactor_Mask masks);
/**
* Remove signal handler registration.
*
* Remove the ACE_Event_Handler currently associated with @a signum.
* Install the new disposition (if given) and return the previous
* disposition (if desired by the caller).
*
* Note that, unlike removing handler for I/O events,
* ACE_Event_Handler::handle_close() will not be called when the
* handler is removed. Neither will any reference-counting activity be
* involved.
*
* @note There's an existing enhancement request in Bugzilla,
* #2368, to change this behavior so that ACE_Event_Handler::handle_close()
* is called when the signal handler is removed. Thus, there's some chance
* this behavior will change in a future version of ACE.
*/
int remove_handler (int signum,
ACE_Sig_Action *new_disp,
ACE_Sig_Action *old_disp = 0,
int sigkey = -1);
/**
* Remove multiple signal handler registrations.
*
* Shorthand for calling
* remove_handler(int,ACE_Sig_Action*,ACE_Sig_Action*,int) multiple
* times for every signal in @a sigset.
*/
int remove_handler (const ACE_Sig_Set &sigset);
// = Suspend and resume Handlers.
/**
* Suspend @a handle temporarily.
*/
int suspend_handler (ACE_HANDLE handle);
/**
* Suspend @a event_handler temporarily.
*
* Handle is obtained from ACE_Event_Handler::get_handle().
*/
int suspend_handler (ACE_Event_Handler *event_handler);
/**
* Suspend @a handles temporarily.
*
* Shorthand for calling suspend_handler(ACE_HANDLE) with multiple
* @a handles.
*/
int suspend_handler (const ACE_Handle_Set &handles);
/**
* Suspend all registered handles temporarily.
*/
int suspend_handlers (void);
/**
* Resume @a handle.
*/
int resume_handler (ACE_HANDLE handle);
/**
* Resume @a event_handler.
*
* Handle is obtained from ACE_Event_Handler::get_handle().
*/
int resume_handler (ACE_Event_Handler *event_handler);
/**
* Resume @a handles.
*
* Shorthand for calling resume_handler(ACE_HANDLE) with multiple
* @a handles.
*/
int resume_handler (const ACE_Handle_Set &handles);
/**
* Resume all registered handles.
*/
int resume_handlers (void);
/// Does the reactor allow the application to resume the handle on
/// its own ie. can it pass on the control of handle resumption to
/// the application. A positive value indicates that the handlers
/// are application resumable. A value of 0 indicates otherwise.
int resumable_handler (void);
// = Timer management.
/**
* Schedule a timer event.
*
* Schedule a timer event that will expire after an @a delay amount
* of time. The return value of this method, a timer_id value,
* uniquely identifies the @a event_handler in the ACE_Reactor's
* internal list of timers. This timer_id value can be used to
* cancel the timer with the cancel_timer() call.
*
* Reactor will call ACE_Event_Handler::add_reference() on the
* handler. After the timeout occurs and
* ACE_Event_Handler::handle_timeout() has completed, the handler
* will be implicitly removed from the Reactor and
* ACE_Event_Handler::remove_reference() will be called.
*
* @see cancel_timer()
* @see reset_timer_interval()
*
* @param event_handler Event handler to schedule on reactor. The handler's
* handle_timeout() method will be called when this
* scheduled timer expires.
* @param arg Argument passed to the handle_timeout() method of
* event_handler.
* @param delay Time interval after which the timer will expire.
* @param interval Time interval for which the timer will be
* automatically rescheduled if the handle_timeout()
* callback does not return a value less than 0.
*
* @retval timer id, on success. The id can be used to
* cancel or reschedule this timer.
* @retval -1 on failure, with errno set.
*/
virtual long schedule_timer (ACE_Event_Handler *event_handler,
const void *arg,
const ACE_Time_Value &delay,
const ACE_Time_Value &interval =
ACE_Time_Value::zero);
/**
* Reset recurring timer interval.
*
* Resets the interval of the timer represented by @a timer_id to
* @a interval, which is specified in relative time to the current
* gettimeofday(). If @a interval is equal to
* ACE_Time_Value::zero, the timer will become a non-rescheduling
* timer. Returns 0 if successful, -1 if not.
*
* This change will not take effect until the next timeout.
*/
virtual int reset_timer_interval (long timer_id,
const ACE_Time_Value &interval);
// ...省略
/// Get the implementation class
ACE_Reactor_Impl *implementation (void) const;
// ...省略
protected:
/// Set the implementation class.
void implementation (ACE_Reactor_Impl *implementation);
/// Delegation/implementation class that all methods will be
/// forwarded to.
/*用于指示在ACE_Reactor_Impl是一个委派/实现类,ACE_Reactor的所有方法都由它实现,ACE根据不同
的分离函数使用不同的implementation_ */
ACE_Reactor_Impl *implementation_;
/// Flag used to indicate whether we are responsible for cleaning up
/// the implementation instance
/*用于指示在ACE_Reactor退出时是否删除implementation_,由于implementation_可以动态生成,也
可以静态生成,但是ACE_Reactor并不知道它的生成方式,对于动态生成的情况,需要执行delete操作,
而静态生成时不需要*/
bool delete_implementation_;
/// Pointer to a process-wide ACE_Reactor singleton.
/*reactor_是进程及Singleton指针*/
static ACE_Reactor *reactor_;
/// Must delete the reactor_ singleton if true.
// 如果delete_reactor_为TRUE,那么框架必须删除reactor_指针
static bool delete_reactor_;
/// Deny access since member-wise won't work...
ACE_Reactor (const ACE_Reactor &);
ACE_Reactor &operator = (const ACE_Reactor &);
};
3.5.2 ACE_Select_Reactor_Impl类分析
ACE_Select_Reactor_Impl仍然是一个抽象类,它为基于select调用的分离函数提供统一的接口。ACE_Select_Reactor_Impl的程序清单如下(其中ACE_Select_Reactor_Impl有一个非常重要的函数bit_ops,我们放在I/O事件中分析):
// 代码在ace/Select_Reactor_Base.h中
/*ACE_Select_Reactor_Impl类即为基于select调用的Reactor管理器的真正实现提供接口函数,又为使用不同同步策略的管理器提供一个公共的基类*/
class ACE_Export ACE_Select_Reactor_Impl : public ACE_Reactor_Impl
{
public:
enum
{
/// Default size of the Select_Reactor's handle table.
DEFAULT_SIZE = ACE_DEFAULT_SELECT_REACTOR_SIZE
};
/// Constructor.
ACE_Select_Reactor_Impl (bool mask_signals = true);
friend class ACE_Select_Reactor_Notify;
friend class ACE_Select_Reactor_Handler_Repository;
/**
* Purge any notifications pending in this reactor for the specified
* ACE_Event_Handler object. Returns the number of notifications
* purged. Returns -1 on error.
*/
virtual int purge_pending_notifications (ACE_Event_Handler * = 0,
ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
/// Does the reactor allow the application to resume the handle on
/// its own ie. can it pass on the control of handle resumption to
/// the application. The select reactor has no handlers that can be
/// resumed by the application. So return 0;
virtual int resumable_handler (void);
/*
* Hook to add concrete methods required to specialize the
* implementation with concrete methods required for the concrete
* reactor implementation, for example, select, tp reactors.
*/
//@@ REACTOR_SPL_PUBLIC_METHODS_ADD_HOOK
protected:
/// Allow manipulation of the <wait_set_> mask and <ready_set_> mask.
virtual int bit_ops (ACE_HANDLE handle,
ACE_Reactor_Mask mask,
ACE_Select_Reactor_Handle_Set &handle_set,
int ops);
/// Enqueue ourselves into the list of waiting threads at the
/// appropriate point specified by <requeue_position_>.
virtual void renew (void) = 0;
/// Check to see if the Event_Handler associated with @a handle is
/// suspended. Returns 0 if not, 1 if so.
virtual int is_suspended_i (ACE_HANDLE handle) = 0;
/// When register/unregister occur, then we need to re-eval our
/// wait/suspend/dispatch set.
virtual void clear_dispatch_mask (ACE_HANDLE handle,
ACE_Reactor_Mask mask);
/// Table that maps <ACE_HANDLEs> to <ACE_Event_Handler *>'s.
/*用于将描述符映射到事件处理器的表,就是I/O事件处理仓库*/
ACE_Select_Reactor_Handler_Repository handler_rep_;
/*以下4个事件集合表示I/O事件调度集合,这几个事件的集合用于事件的注册、分离、调度和删除。它们的使用将在I/O调度一节中详细分析*/
/// Tracks handles that are ready for dispatch from <select>
ACE_Select_Reactor_Handle_Set dispatch_set_;// 用于记录将要被调度的事件
/// Tracks handles that are waited for by <select>.
ACE_Select_Reactor_Handle_Set wait_set_;// 用于记录将要被select分离的事件
/// Tracks handles that are currently suspended.// 用于记录将要被挂起的事件
ACE_Select_Reactor_Handle_Set suspend_set_;
/// Track HANDLES we are interested in for various events that must
/// be dispatched *without* going through <select>.
// 用于记录不需要select分离就可以直接调度的事件
ACE_Select_Reactor_Handle_Set ready_set_;
/// Defined as a pointer to allow overriding by derived classes...
ACE_Timer_Queue *timer_queue_;// 定时器事件管理指针
/// Handle signals without requiring global/static variables.
ACE_Sig_Handler *signal_handler_; // 信号量事件管理指针
/// Callback object that unblocks the ACE_Select_Reactor if it's
/// sleeping.
ACE_Reactor_Notify *notify_handler_;//Notify事件管理指针
/// Keeps track of whether we should delete the timer queue (if we
/// didn't create it, then we don't delete it).
bool delete_timer_queue_; // 用于标志是否删除定时器事件管理器指针
/// Keeps track of whether we should delete the signal handler (if we
/// didn't create it, then we don't delete it).
bool delete_signal_handler_;// 用于标志是否删除信号量事件管理器指针
/// Keeps track of whether we need to delete the notify handler (if
/// we didn't create it, then we don't delete it).
bool delete_notify_handler_; // 用于标志是否删除Notify事件管理指针
/// True if we've been initialized yet...
bool initialized_;// 初始化标志,默认false
/// Restart the <handle_events> event-loop method automatically when
/// <select> is interrupted via <EINTR>.
bool restart_;//用于标志select函数被中断后是否重新开始调度,默认0
/**
* Position that the main ACE_Select_Reactor thread is requeued in
* the list of waiters during a <notify> callback. If this value ==
* -1 we are requeued at the end of the list. Else if it's 0 then
* we are requeued at the front of the list. Else if it's > 1 then
* that indicates the number of waiters to skip over.
*/
int requeue_position_;// 用于同步令牌,不做分析
/// The original thread that created this Select_Reactor.
ACE_thread_t owner_;// 用于保存创建者的线程号
/**
* True if state has changed during dispatching of
* ACE_Event_Handlers, else false. This is used to determine
* whether we need to make another trip through the
* <Select_Reactor>'s <wait_for_multiple_events> loop.
*/
bool state_changed_;// 用于标示调度事件集是否发生了变化
/**
* If false then the Reactor will not mask the signals during the event
* dispatching. This is useful for applications that do not
* register any signal handlers and want to reduce the overhead
* introduce by the kernel level locks required to change the mask.
*/
bool mask_signals_;// 用于标示事件调度时是否屏蔽信号 默认true
/// Controls/access whether the notify handler should renew the
/// Select_Reactor's token or not.
int supress_notify_renew (void);
void supress_notify_renew (int sr);
private:
/// Determine whether we should renew Select_Reactor's token after handling
/// the notification message.
int supress_renew_;// 用于同步令牌,不做分析
/// Deny access since member-wise won't work...
ACE_Select_Reactor_Impl (const ACE_Select_Reactor_Impl &);
ACE_Select_Reactor_Impl &operator = (const ACE_Select_Reactor_Impl &);
};
3.5.3 ACE_Select_Reactor_T类的分析
ACE_Select_Reactor_T类是Reactor管理器的一种实现,它以操作系统的select函数作为事件分离器,它是一个模板类,模板参数用于指明当前使用的令牌类型。
ACE_Select_Reactor_T类的数据成员非常少,其中deactivated_用来控制Reactor管理器的活动状态。如果deactivated_设置为1,那么会关闭Reactor管理器。
3.6 I/O事件调度的分析
在分析框架对I/O事件的调度之前,我们先来复习一下select函数是如何来分离事件的。select函数的原型为:
int select(int maxfdp1, fd_set * readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout)
其中,maxfdp1表示当前调度的最大描述符加1,readset表示读描述符集,writeset表示写描述符集,exceptset表示异常描述
符集,timeout表示超时时间。
3.6.1 I/O事件调度集的设计
在Linux下,一个文件描述符可以对应3中不同的I/O事件:READ、WRITE和EXCEPT,select函数可以同时分离出这些事件。为了表达这种关系,ACE设计了ACE_Select_Reactor_Handle_Set类。其代码清单如下:
// 代码在ace/Select_Reactor_Base.h中
class ACE_Export ACE_Select_Reactor_Handle_Set
{
public:
/// Read events (e.g., input pending, accept pending).
// 读描述符集,(例如,输入事件和Socket中的accept事件)
ACE_Handle_Set rd_mask_;
/// Write events (e.g., flow control abated, non-blocking connection
// 写描述符集(例如,流量控制减弱和非阻塞模式下的connnect完成事件)
/// complete).
ACE_Handle_Set wr_mask_;
/// Exception events (e.g., SIG_URG).
// 异常描述符集(例如,TCP带外数据)
ACE_Handle_Set ex_mask_;
};
仅仅设计了select函数所使用的数据结构还不够,这是因为框架在处理事件的过程中,应用程序还可以注册、删除事件,框架甚至还提供了挂起、恢复事件的接口,要满足这些对事件调度的应用,还必须要有新的数据结构。
为了实现对I/O事件的调度,ACE使用了4个描述符调度集,它们分别是:dispatch_set_、wait_set_、suspend_set_和ready_set_。通过将描述符在这4个调度集中的移动来实现事件的调度。
这4个调度集描述了一个I/O描述符在框架调度过程中的不同状态。状态的变换关系如图3-3所示。
图3-3 调度集状态图
①当向框架注册一个文件描述符时,该文件描述符被加入到wait_set中,wait_set一共有3个描述符集,描述符被加到哪个(哪几个)描述符集由注册时的标志决定。
②文件描述符、事件一旦注册好之后,框架便会对其进行调度。在调度开始时,框架会将wait_set中国的描述符复制到dispatch_set中,dispatch_set是调度过程中的一个中间集。
③然后框架会调用select函数对dispatch_set中的事件进行分离,select函数需要3个描述符集,正好与dispatch_set数据结构中的3个描述符集相对应。
④接收到I/O事件后,select函数先分离该事件,获取事件的描述符,然后根据描述符找到对应的事件处理器,找到事件处理器后,调用事件处理函数handle_xxx来处理该事件。
⑤在处理过程中,Reactor管理器会根据handle_xxx函数的返回值做出相应的处理。
-
a.如果handle_xxx函数返回值等于0,那么Reactor管理器继续等待、分离、调度。
-
b.如果handle_xxx函数返回值小于0,那么从Reactor管理器中删除该事件。如果与该事件对应的描述符没有其他事件调度,则同时删除该描述符的事件处理器。
-
c.如果handle_xxx函数返回值大于0,那么将该文件描述符复制到ready_set中。ready_set中的文件描述符在不等待事件发生的情况下,可以直接复制到dispatch_set中,接受框架调度。
⑥suspend_set用于对某个描述符暂时停止调度,之后可以恢复调度。
注:应用程序可以通过Reactor管理器接口来实现对某个描述符的挂起和恢复操作。在整个调度状态变化过程中,只有1,7,8,9是应用程序可操作的,其他的状态在框架调度过程中内部切换,应用程序只能通过handle_xxx的返回值来决定部分流程。
3.6.2 调度集操作函数的分析
bit_ops是调度集操作函数。它的功能是根据事件类型在调度集中对描述符进行GET、ADD、SET、DELETE操作。
相关调度操作常量的代码如下列清单所示。
// 代码在ace/Reactor.h中
enum
{
/// Retrieve current value of the the "ready" mask or the
/// "dispatch" mask.
// 获取当前描述符在ready_set或dispatch_set中的事件
GET_MASK = 1,
/// Set value of bits to new mask (changes the entire mask).
// 对描述符集进行比特位置位操作(改变整个描述符集)
SET_MASK = 2,
/// Bitwise "or" the value into the mask (only changes enabled
// 对描述符集进行比特位或操作(仅仅改变需要注册的描述符集)
/// bits).
ADD_MASK = 3,
/// Bitwise "and" the negation of the value out of the mask (only
/// changes enabled bits).
// 对描述符集进行比特位与非操作(仅仅改变需要删除的描述符集)
CLR_MASK = 4
};
3.6.3 I/O事件处理器仓库的分析
Reactor管理器对I/O事件的调度分为两步,先是通过操作系统提供的select函数对事件分离,然后根据应用程序注册的事件处理器进行调度。
I/O事件处理器和I/O描述符是一一对应的。要满足应用程序注册、删除和调度事件处理的任务,Reactor管理器就要有个事件处理器仓库,通过事件处理器仓库可以根据描述符找到I/O事件处理器。
事件处理器仓库实现如下面的代码清单所示,它根据描述符和事件处理器之间的关系来使用一个容器,容器的设计目的是可以根据描述符快速地找到事件处理器。
// 代码在ace/Select_Reactor_Base中
class ACE_Export ACE_Select_Reactor_Handler_Repository
{
public:
friend class ACE_Select_Reactor_Handler_Repository_Iterator;
typedef ACE_HANDLE key_type;
typedef ACE_Event_Handler * value_type;
// = The mapping from <HANDLES> to <Event_Handlers>.
#ifdef ACE_WIN32
/**
* The NT version implements this via a hash map
* @c ACE_Event_Handler*. Since NT implements @c ACE_HANDLE
* as a void * we can't directly index into this array. Therefore,
* we must explicitly map @c ACE_HANDLE to @c ACE_Event_Handler.
*/
typedef ACE_Hash_Map_Manager_Ex<key_type,
value_type,
ACE_Hash<key_type>,
std::equal_to<key_type>,
ACE_Null_Mutex> map_type;
typedef map_type::size_type max_handlep1_type;
#else
/**
* The UNIX version implements this via a dynamically allocated
* array of @c ACE_Event_Handler* that is indexed directly using
* the @c ACE_HANDLE value.
*/
typedef ACE_Array_Base<value_type> map_type;
typedef ACE_HANDLE max_handlep1_type;
#endif /* ACE_WIN32 */
typedef map_type::size_type size_type;
// = Initialization and termination methods.
/// Default "do-nothing" constructor.
ACE_Select_Reactor_Handler_Repository (ACE_Select_Reactor_Impl &);
/// Initialize a repository of the appropriate @a size.
/**
* On Unix platforms, the size parameter should be as large as the
* maximum number of file descriptors allowed for a given process.
* This is necessary since a file descriptor is used to directly
* index the array of event handlers maintained by the Reactor's
* handler repository. Direct indexing is used for efficiency
* reasons.
*/
/*open函数是仓库的初始化函数*/
int open (size_type size);
/// Close down the repository.
int close (void);
// = Search structure operations.
/**
* Return the @c ACE_Event_Handler* associated with @c ACE_HANDLE.
*/
ACE_Event_Handler * find (ACE_HANDLE handle);
/// Bind the ACE_Event_Handler * to the ACE_HANDLE with the
/// appropriate ACE_Reactor_Mask settings.
/*bind函数先将描述符、事件处理器插入到事件处理器仓库中,然后将描述符合事件添加到调度集中*/
int bind (ACE_HANDLE,
ACE_Event_Handler *,
ACE_Reactor_Mask);
/// Remove the binding of ACE_HANDLE in accordance with the @a mask.
/*unbind函数用于解除文件描述符和事件处理器之间的关系,同时将文件描述符从调度集中删除*/
int unbind (ACE_HANDLE,
ACE_Reactor_Mask mask);
/// Remove all the <ACE_HANDLE, ACE_Event_Handler> tuples.
int unbind_all (void);
// = Sanity checking.
// Check the @a handle to make sure it's a valid @c ACE_HANDLE that
// is within the range of legal handles (i.e., >= 0 && < max_size_).
bool invalid_handle (ACE_HANDLE handle);
// Check the @a handle to make sure it's a valid @c ACE_HANDLE that
// within the range of currently registered handles (i.e., >= 0 && <
// @c max_handlep1_).
bool handle_in_range (ACE_HANDLE handle);
// = Accessors.
/// Returns the current table size.
size_type size (void) const;
/// Maximum ACE_HANDLE value, plus 1.
max_handlep1_type max_handlep1 (void) const;
/// Dump the state of an object.
void dump (void) const;
/// Declare the dynamic allocation hooks.
ACE_ALLOC_HOOK_DECLARE;
private:
/// Remove the binding of @a handle corresponding to position @a pos
/// in accordance with the @a mask.
int unbind (ACE_HANDLE handle,
map_type::iterator pos,
ACE_Reactor_Mask mask);
/**
* @return @c iterator corresponding @c ACE_Event_Handler*
* associated with @c ACE_HANDLE.
*/
map_type::iterator find_eh (ACE_HANDLE handle);
private:
/// Reference to our @c Select_Reactor.
ACE_Select_Reactor_Impl &select_reactor_;
#ifndef ACE_WIN32
/// The highest currently active handle, plus 1 (ranges between 0 and
/// @c max_size_.
max_handlep1_type max_handlep1_;//max_handlep1_的值是当前最大的描述符加1
#endif /* !ACE_WIN32 */
/// Underlying table of event handlers.
map_type event_handlers_;// 事件处理器仓库
};
3.6.4 I/O事件注册流程的分析
Reactor框架来调度应用程序的I/O事件,应用程序必须将I/O事件注册到框架中,这不仅需要将一个具体的事件处理器放到事件处理器仓库中,还必须将事件处理器的描述符和事件处理器的事件加入到框架的调度集中,这些操作都由bind函数来完成。
由于bind函数是一个框架内部使用的函数,因此框架必须给应用程序提供一个注册接口,使应用程序可以将事件处理器注册到框架中,它就是register_handler函数。
register_handler函数有多个重载的版本,主要分为两类:一类用于I/O事件的注册,一类用于信号量事件的注册。定时器事件和Notify事件使用了另外两个函数,分别是schedule_timer函数和notify函数。
注册I/O事件处理器的UML顺序图,如图3-4所示。
图3-4 注册I/O事件处理器顺序图
3.6.5 I/O事件调度流程的分析
对事件的调度其实是一个无限循环,每次循环都调用select函数等待和分离事件,一旦select函数有事件返回,那么针对返回的描述符进行I/O处理。
为了方便应用程序编程,框架提供了多个调度入口函数,run_event_loop函数就是其中之一。
所有的调度入口函数可以分为两类:一类是static类型的,应用于Singleton设计模式;另一类是非static类型的,应用于实例对象。
调度I/O事件处理器的顺序图如图3-5所示。
图3-5 调度I/O事件处理器的顺序图
3.6.6 I/O事件删除流程
如果要删除某个事件,那么可以调用remove_handler函数。同样remove_handler函数也有几个重载版本。
删除I/O事件处理器的顺序图如图3-6所示。
图3-6 删除I/O事件处理器的顺序图
3.7 信号量事件调度的分析
在Linux操作系统中,信号量事件和I/O事件的处理方式是完成不同的:
①I/O事件一般使用分离函数等待事件,然后对描述符进行读写等操作;
②信号量事件则通过应用程序向操作系统注册信号处理函数来完成。
信号量事件调度不受应用程序的控制,只要信号量不被应用程序阻塞,一旦信号发生,处理函数就可以得到执行。
在Reactor框架中,信号量的事件处理器也必须是ACE_Event_Handler的子类,但是处理函数是handle_signal。信号量事件的注册和I/O事件一样,也使用register_handler函数,只是使用了函数的重载功能。信号量事件管理器由ACE_Sig_Handler类担任。
3.7.1 信号量事件管理器的分析
由于信号量事件完全区别于I/O事件,所以I/O事件的调度方法不能用于调度信号量事件。要处理信号量事件,框架必须先封装操作系统用于处理信号量事件的原始数据结构和接口。这一工作由ACE_Sig_Set类和ACE_Sig_Action类来完成。其中ACE_Sig_Set类是对信号集合信号集操作函数的C++封装。
// 代码在ace/Signal.h中
class ACE_Export ACE_Sig_Set
{
public:
// = Initialization and termination methods.
/// Initialize <sigset_> with @a sigset. If @a sigset == 0 then fill
/// the set.
ACE_Sig_Set (sigset_t *sigset);
/// Initialize <sigset_> with @a sigset. If @a sigset == 0 then fill
/// the set.
ACE_Sig_Set (ACE_Sig_Set *sigset);
/// If @a fill == 0 then initialize the <sigset_> to be empty, else
/// full.
ACE_Sig_Set (int fill = 0);
~ACE_Sig_Set (void);
/// Create a set that excludes all signals defined by the system.
int empty_set (void);
/// Create a set that includes all signals defined by the system.
int fill_set (void);
/// Adds the individual signal specified by @a signo to the set.
int sig_add (int signo);
/// Deletes the individual signal specified by @a signo from the set.
int sig_del (int signo);
/// Checks whether the signal specified by @a signo is in the set.
int is_member (int signo) const;
/// Returns a pointer to the underlying @c sigset_t.
operator sigset_t *();
/// Returns a copy of the underlying @c sigset_t.
sigset_t sigset (void) const;
/// Dump the state of an object.
void dump (void) const;
/// Declare the dynamic allocation hooks.
ACE_ALLOC_HOOK_DECLARE;
private:
/// Set of signals.
sigset_t sigset_; // 信号集
};
ACE_Sig_Aciton类是信号处理句柄和信号处理句柄操作的C++封装。代码清单如下:
// 代码在ace/Signal.h中
class ACE_Export ACE_Sig_Action
{
public:
//...省略
private:
/// Controls signal behavior.
struct sigaction sa_; // 信号处理
};
ACE_Sig_Handler类是信号事件处理的核心,与信号量事件相关的操作都在这个类中,我们称之为信号量事件管理器。Reactor管理器有有一个ACE_Sig_Handler对象----signal_handler_,用于信号量事件的处理。ACE_Select_Reactor_T类在它的初始化函数open中初始化ACE_Sig_Handler对象。
ACE_Sig_Handler类的代码清单如下。
// 代码在ace/Sig_Handler.h中
class ACE_Export ACE_Sig_Handler
{
public:
/// Default constructor.
ACE_Sig_Handler (void);
/// Destructor
virtual ~ACE_Sig_Handler (void);
// = Registration and removal methods.
/**
* Add a new ACE_Event_Handler and a new sigaction associated with
* @a signum. Passes back the existing ACE_Event_Handler and its
* sigaction if pointers are non-zero. Returns -1 on failure and >=
* 0 on success.
*/
virtual int register_handler (int signum,
ACE_Event_Handler *new_sh,
ACE_Sig_Action *new_disp = 0,
ACE_Event_Handler **old_sh = 0,
ACE_Sig_Action *old_disp = 0);
/**
* Remove the ACE_Event_Handler currently associated with
* @a signum. @a sigkey is ignored in this implementation since there
* is only one instance of a signal handler. Install the new
* disposition (if given) and return the previous disposition (if
* desired by the caller). Returns 0 on success and -1 if @a signum
* is invalid.
*/
virtual int remove_handler (int signum,
ACE_Sig_Action *new_disp = 0,
ACE_Sig_Action *old_disp = 0,
int sigkey = -1);
// Set/get signal status.
/// True if there is a pending signal.
static int sig_pending (void);
/// Reset the value of <sig_pending_> so that no signal is pending.
static void sig_pending (int);
// = Set/get the handler associated with a particular signal.
/// Return the ACE_Sig_Handler associated with @a signum.
virtual ACE_Event_Handler *handler (int signum);
/// Set a new ACE_Event_Handler that is associated with @a signum.
/// Return the existing handler.
virtual ACE_Event_Handler *handler (int signum, ACE_Event_Handler *);
/**
* Callback routine registered with sigaction(2) that dispatches the
* <handle_signal> method of the appropriate pre-registered
* ACE_Event_Handler.
*/
static void dispatch (int, siginfo_t *,
ucontext_t *);
/// Dump the state of an object.
void dump (void) const;
/// Declare the dynamic allocation hooks.
ACE_ALLOC_HOOK_DECLARE;
protected:
// = These methods and data members are shared by derived classes.
/**
* Set a new ACE_Event_Handler that is associated with @a signum.
* Return the existing handler. Does not acquire any locks so that
* it can be called from a signal handler, such as <dispatch>.
*/
static ACE_Event_Handler *handler_i (int signum,
ACE_Event_Handler *);
/**
* This implementation method is called by <register_handler> and
* @c dispatch. It doesn't do any locking so that it can be called
* within a signal handler, such as @c dispatch. It adds a new
* ACE_Event_Handler and a new sigaction associated with @a signum.
* Passes back the existing ACE_Event_Handler and its sigaction if
* pointers are non-zero. Returns -1 on failure and >= 0 on
* success.
*/
static int register_handler_i (int signum,
ACE_Event_Handler *new_sh,
ACE_Sig_Action *new_disp = 0,
ACE_Event_Handler **old_sh = 0,
ACE_Sig_Action *old_disp = 0);
/// Check whether the SIGNUM is within the legal range of signals.
static int in_range (int signum);
/// Keeps track of whether a signal is pending.
/*信号量处理标志,如果有信号量需要处理,将它置为TRUE*/
static sig_atomic_t sig_pending_;
private:
/// Array used to store one user-defined Event_Handler for every
/// signal. // signal_handlers_用于保存事件处理器的数组
static ACE_Event_Handler *signal_handlers_[ACE_NSIG];
};
(1)register_handler函数
ACE_Sig_Handler类的register_handler函数被框架的管理器调用,用于注册一个信号量事件。register_handler函数直接将操作转给了内部的register_handler_i函数。
ACE_Sig_Handler类是一个独立的组件,当然就不能使用Reactor管理器中的令牌。
(2)register_handler_i函数是信号量事件注册的真正实现,它将一个信号量和一个事件处理器绑定在一起。register_handler_i函数为每一个信号量注册的处理函数都是ace_signal_handlers_dispatcher。
(3)信号量事件的调度
如果有信号量事件注册到Reactor框架中,那么,一旦该信号量事件发生,操作系统就会自动调用ace_signal_handlers_dispatcher函数,该函数又会调用dispatch函数,在dispatch函数中实现了信号量事件处理器的调度。
图3-7 注册信号量事件处理器的顺序图
信号量事件处理器的调度顺序如下图所示,其中OS表示操作系统调用。
图3-8 调度信号量事件处理器的顺序图
3.7.2 Reactor管理器中的信号量事件处理
尽管信号量事件的调度没有出现在ACE_Select_Reactor_T类的dispatch函数中,但是该函数还是对信号量事件做了一些处理。
ready_set是I/O事件的一个调度集,应用程序处理信号量事件时,可能会改变ready_set,在这种情况下,框架需要把ready_set中的描述符及时赋值给dispatch_set,以便在接下来的I/O事件调度中及时处理这些描述符。
3.7.3 信号量事件删除流程的分析
信号量事件处理器的删除函数是remove_handler。如下图所示为删除信号量事件处理器的顺序图。
图3-9 删除信号量事件处理器的顺序图
3.8 定时器事件调度的分析
Reactor框架并没有使用操作系统提供的定时器功能,ACE认为操作系统提供的定时器移植性较差,而且数量上也有一定的限制,不适合在网络上应用。
Reactor框架的定时器设计采用了Strategy设计模式。这是因为定时器对超时时间的管理可以使用不同的算法。框架提供了多个定时器时间调度算法----ACE_Timer_Heap、ACE_Timer_Wheel、ACE_Timer_Hash、ACE_Timer_List,默认使用ACE_Timer_Heap,应用程序可以根据实际应用选择一个时间调度算法。如表3-3所示,是这些算法的描述。
表3-3 定时器算法描述
下面的分析基于ACE_Timer_List。ACE_Timer_List使用一个有序的(以超时时间大小排序)链表仓库来保存定时器的超时时间。
定时器事件处理的相关类结构如图3-10所示。其中ACE_Time_Queue_T是Strategy模式中的Strategy参与者,它是一个抽象类,实现了定时器调度算法的公共部分,其他则留给特定的算法去实现。ACE_Time_Queue_T是一个模板类,3个模板参数分别表示事件处理器类型、回调函数和锁类型。将ACE_Time_Queue_T设计为一个模板类,是为了提高定时器组件的可重用性。
图3-10 定时器事件相关结构图
3.8.1 定时器事件管理器的分析
定时器事件管理器结构图如图3-11所示。
图3-11 定时器事件管理器结构图
ACE_Time_Queue_T是定时器管理器的根基类,为不同的定时器事件调度算法提供统一的接口。其代码清单如下:
// 代码在ace/Timer_Queue_T.h中
template <class TYPE, class FUNCTOR, class ACE_LOCK>
class ACE_Timer_Queue_T
{
public:
/// Type of Iterator.
typedef ACE_Timer_Queue_Iterator_T<TYPE, FUNCTOR, ACE_LOCK> ITERATOR;
// = Initialization and termination methods.
/**
* Default constructor. @a upcall_functor is the instance of the
* FUNCTOR to be used by the queue. If @a upcall_functor is 0, Timer
* Queue will create a default FUNCTOR. @a freelist the freelist of
* timer nodes. If 0, then a default freelist will be created.
*/
ACE_Timer_Queue_T (FUNCTOR *upcall_functor = 0,
ACE_Free_List<ACE_Timer_Node_T <TYPE> > *freelist = 0);
/// Destructor - make virtual for proper destruction of inherited
/// classes.
virtual ~ACE_Timer_Queue_T (void);
/// True if queue is empty, else false.
virtual bool is_empty (void) const = 0;
/// Returns the time of the earlier node in the Timer_Queue. Must
/// be called on a non-empty queue.
virtual const ACE_Time_Value &earliest_time (void) const = 0;
/**
* Schedule @a type that will expire at @a future_time, which is
* specified in absolute time. If it expires then @a act is passed
* in as the value to the <functor>. If @a interval is != to
* ACE_Time_Value::zero then it is used to reschedule the @a type
* automatically, using relative time to the current <gettimeofday>.
* This method returns a <timer_id> that uniquely identifies the the
* @a type entry in an internal list. This <timer_id> can be used to
* cancel the timer before it expires. The cancellation ensures
* that <timer_ids> are unique up to values of greater than 2
* billion timers. As long as timers don't stay around longer than
* this there should be no problems with accidentally deleting the
* wrong timer. Returns -1 on failure (which is guaranteed never to
* be a valid <timer_id>).
*/
virtual long schedule (const TYPE &type,
const void *act,
const ACE_Time_Value &future_time,
const ACE_Time_Value &interval = ACE_Time_Value::zero);
/**
* Resets the interval of the timer represented by @a timer_id to
* @a interval, which is specified in relative time to the current
* <gettimeofday>. If @a interval is equal to
* ACE_Time_Value::zero, the timer will become a non-rescheduling
* timer. Returns 0 if successful, -1 if not.
*/
virtual int reset_interval (long timer_id,
const ACE_Time_Value &interval) = 0;
/**
* Cancel all timer associated with @a type. If
* @a dont_call_handle_close is 0 then the <functor> will be invoked,
* which typically invokes the <handle_close> hook. Returns number
* of timers cancelled.
*/
virtual int cancel (const TYPE &type,
int dont_call_handle_close = 1) = 0;
/**
* Cancel the single timer that matches the @a timer_id value (which
* was returned from the <schedule> method). If act is non-NULL
* then it will be set to point to the ``magic cookie'' argument
* passed in when the timer was registered. This makes it possible
* to free up the memory and avoid memory leaks. If
* @a dont_call_handle_close is 0 then the <functor> will be invoked,
* which typically calls the <handle_close> hook. Returns 1 if
* cancellation succeeded and 0 if the @a timer_id wasn't found.
*/
virtual int cancel (long timer_id,
const void **act = 0,
int dont_call_handle_close = 1) = 0;
/**
* Run the <functor> for all timers whose values are <= @a current_time.
* This does not account for <timer_skew>. Returns the number of
* timers canceled.
*/
virtual int expire (const ACE_Time_Value ¤t_time);
/**
* Get the dispatch information for a timer whose value is <= @a current_time.
* This does not account for <timer_skew>. Returns 1 if
* there is a node whose value <= @a current_time else returns a 0.
*
*/
virtual int dispatch_info (const ACE_Time_Value ¤t_time,
ACE_Timer_Node_Dispatch_Info_T<TYPE> &info);
/**
* Run the <functor> for all timers whose values are <=
* <ACE_OS::gettimeofday>. Also accounts for <timer_skew>.
*
* Depending on the resolution of the underlying OS the system calls
* like select()/poll() might return at time different than that is
* specified in the timeout. Suppose the OS guarantees a resolution of t ms.
* The timeline will look like
*
* A B
* | |
* V V
* |-------------|-------------|-------------|-------------|
* t t t t t
*
*
* If you specify a timeout value of A, then the timeout will not occur
* at A but at the next interval of the timer, which is later than
* that is expected. Similarly, if your timeout value is equal to B,
* then the timeout will occur at interval after B. Now depending upon the
* resolution of your timeouts and the accuracy of the timeouts
* needed for your application, you should set the value of
* <timer_skew>. In the above case, if you want the timeout A to fire
* no later than A, then you should specify your <timer_skew> to be
* A % t.
*
* The timeout value should be specified via the macro ACE_TIMER_SKEW
* in your config.h file. The default value is zero.
*
* Things get interesting if the t before the timeout value B is zero
* i.e your timeout is less than the interval. In that case, you are
* almost sure of not getting the desired timeout behaviour. Maybe you
* should look for a better OS :-)
*
* Returns the number of timers canceled.
*/
/* virtual */ int expire (void);
/**
* Returns the current time of day. This method allows different
* implementations of the timer queue to use special high resolution
* timers.
*/
/* virtual */ ACE_Time_Value gettimeofday (void);
/// Allows applications to control how the timer queue gets the time
/// of day.
void gettimeofday (ACE_Time_Value (*gettimeofday)(void));
/// Determine the next event to timeout. Returns @a max if there are
/// no pending timers or if all pending timers are longer than max.
/// This method acquires a lock internally since it modifies internal state.
virtual ACE_Time_Value *calculate_timeout (ACE_Time_Value *max);
/**
* Determine the next event to timeout. Returns @a max if there are
* no pending timers or if all pending timers are longer than max.
* <the_timeout> should be a pointer to storage for the timeout value,
* and this value is also returned. This method does not acquire a
* lock internally since it doesn't modify internal state. If you
* need to call this method when the queue is being modified
* concurrently, however, you should make sure to acquire the <mutex()>
* externally before making the call.
*/
virtual ACE_Time_Value *calculate_timeout (ACE_Time_Value *max,
ACE_Time_Value *the_timeout);
/// Set the timer skew for the Timer_Queue.
void timer_skew (const ACE_Time_Value &skew);
/// Get the timer skew for the Timer_Queue.
const ACE_Time_Value &timer_skew (void) const;
/// Synchronization variable used by the queue
ACE_LOCK &mutex (void);
/// Accessor to the upcall functor
FUNCTOR &upcall_functor (void);
/// Returns a pointer to this ACE_Timer_Queue's iterator.
virtual ITERATOR &iter (void) = 0;
/// Removes the earliest node from the queue and returns it
virtual ACE_Timer_Node_T<TYPE> *remove_first (void) = 0;
/// Dump the state of a object.
virtual void dump (void) const;
/// Reads the earliest node from the queue and returns it.
virtual ACE_Timer_Node_T<TYPE> *get_first (void) = 0;
/// Method used to return a timer node to the queue's ownership
/// after it is returned by a method like <remove_first>.
virtual void return_node (ACE_Timer_Node_T<TYPE> *);
/// This method will call the preinvoke() on <functor>.
void preinvoke (ACE_Timer_Node_Dispatch_Info_T<TYPE> &info,
const ACE_Time_Value &cur_time,
const void *&upcall_act);
/// This method will call the timeout() on <functor>.
void upcall (ACE_Timer_Node_Dispatch_Info_T<TYPE> &info,
const ACE_Time_Value &cur_time);
/// This method will call the postinvoke() on <functor>.
void postinvoke (ACE_Timer_Node_Dispatch_Info_T<TYPE> &info,
const ACE_Time_Value &cur_time,
const void *upcall_act);
protected:
/// Schedule a timer.
virtual long schedule_i (const TYPE &type,
const void *act,
const ACE_Time_Value &future_time,
const ACE_Time_Value &interval) = 0;
/// Reschedule an "interval" ACE_Timer_Node.
virtual void reschedule (ACE_Timer_Node_T<TYPE> *) = 0;
/// Factory method that allocates a new node.
virtual ACE_Timer_Node_T<TYPE> *alloc_node (void);
/// Factory method that frees a previously allocated node.
virtual void free_node (ACE_Timer_Node_T<TYPE> *);
/// Non-locking version of dispatch_info ()
virtual int dispatch_info_i (const ACE_Time_Value ¤t_time,
ACE_Timer_Node_Dispatch_Info_T<TYPE> &info);
/// Recompute when the next time is that this interval timer should fire.
void recompute_next_abs_interval_time (ACE_Timer_Node_T<TYPE>* expired,
const ACE_Time_Value &cur_time);
/// Synchronization variable for ACE_Timer_Queue.
/// @note The right name would be lock_, but HP/C++ will choke on that!
ACE_LOCK mutex_;
/// Class that implements a free list
/*定时器节点链表;预先初始化的链表,用于申请和释放定时器节点*/
ACE_Free_List<ACE_Timer_Node_T<TYPE> > *free_list_;
/*用于指向获取当前时间的函数指针,默认使用ACE_OS::gettimeofday函数*/
/// Pointer to function that returns the current time of day.
ACE_Time_Value (*gettimeofday_)(void);
/*回调函数指针*/
/// Upcall functor
FUNCTOR *upcall_functor_;
/*是否删除回调函数指针标志*/
/// To delete or not to delete is the question?
bool const delete_upcall_functor_;
/*是否删除时间节点标志*/
/// Flag to delete only if the class created the <free_list_>
bool const delete_free_list_;
private:
/*timeout_是calculate_timeout函数的返回值*/
/// Returned by <calculate_timeout>.
ACE_Time_Value timeout_;
/*timer_skew_是时间调整参数*/
/// Adjusts for timer skew in various clocks.
ACE_Time_Value timer_skew_;
/*关闭拷贝构造函数和赋值构造函数*/
// = Don't allow these operations for now.
ACE_UNIMPLEMENTED_FUNC (ACE_Timer_Queue_T (const ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK> &))
ACE_UNIMPLEMENTED_FUNC (void operator= (const ACE_Timer_Queue_T<TYPE, FUNCTOR, ACE_LOCK> &))
};
(1)定时器节点信息描述
每个定时器事件都由两部分组成:一是定时器的时间信息,不同的定时器时间调度算法有不同的定时器时间管理方式;二是事件处理器,用于定时器超时后处理事件。这两部分信息我们称之为定时器节点信息。ACE_Timer_Node_T类用于描述定时器节点信息,其代码清单如下:
// 代码在ace/Timer_Queue_T.h
template <class TYPE>
class ACE_Timer_Node_T
{
public:
// ...省略
private:
/// Type of object stored in the Queue
TYPE type_; // type_是存储在队列中的对象,在Reactor框架中,type_是ACE_Event_Handler对象
/// Asynchronous completion token associated with the timer.
//和定时器相关的异步事件完全令牌
// act_用于保存事件处理的参数
const void *act_;
/// Time until the timer expires.
// 定时器的超时时间
ACE_Time_Value timer_value_;
/// If this is a periodic timer this holds the time until the next
/// timeout. 如果是循环定时器,interval_用于保存循环间隔时间
ACE_Time_Value interval_;
/// Pointer to previous timer.
ACE_Timer_Node_T<TYPE> *prev_; // 表示前节点指针
/// Pointer to next timer.
ACE_Timer_Node_T<TYPE> *next_; // 表示后节点指针
/// Id of this timer (used to cancel timers before they expire).
// timer_id_表示定时器ID(用于在定时器超时之前删除定时器)
long timer_id_;
};
(2)schedule函数
schedule函数被Reactor管理器调用,用于向框架注册一个定时器事件。对于不同的时间管理算法,schedule函数的实现不一样,它们之间的差异由schedule_i函数封装。schedule_i函数是一个虚函数,不同的定时器调度算法有不同的实现。它的作用是构造一个定时器节点,然后将参数中的值存入该节点,最后插入到调度算法的仓库中。
(注:定时器有两种类型:①非循环定时器,超时后,定时器事件只执行一次;②循环定时器,定时器每隔一段时间就会自动执行。interval参数用于区别非循环定时器和循环定时器,默认值是ACE_Time_Value::zero,表示非循环定时器。)
ACE_Event_Handler_Handle_Timeout_Upcall类的registration函数的功能是执行引用计数加操作。
(3)dispatch_info_i函数
用于从定时器节点中取出与定时器调度相关的信息。
循环定时器和非循环定时器在调度过程中的区别:非循环定时器在超时后,将从定时器管理器中删除定时器节点,而由于循环定时器需要循环调度,因此在取出了定时器节点信息后,又被注册到了定时器管理器中。
ACE_Timer_Node_Dispatch_Info_T类用来传递定时器的调度信息。
(4)expire函数
expire函数是定时器事件的调度函数,每次调用expire函数,都会查找其中超时的定时器。如果有超时的定时器,那么调用事件处理器的handle_timeout函数,对定时器事件进行处理。
(5)cancel函数
cancel函数用于删除一个定时器,由定时器时间调度算法实现。
3.8.2 定时器事件注册流程
schedule_timer是Reactor管理器提供给应用程序的接口,用于注册一个定时器。schedule_timer函数将调用转给Reactor管理器的Implementor。实际上最后的执行函数是定时器管理器的schedule函数。
注册定时器事件处理器的顺序图如图3-12所示。
图3-12 注册定时器事件处理器的顺序图
3.8.3 定时器事件调度流程的分析
在ACE_Select_Reactor_T的调度函数dispatch中,有一个函数用于定时器事件的调度,那就是dispatch_timer_handlers函数。dispatch_timer_handlers函数直接调用了定时器管理器的expire函数。
定时器事件的调度顺序如图3-13所示。
图3-13 定时器调度事件顺序图
3.8.4 定时器事件删除流程
cancel_timer用于删除一个定时器。真正的删除操作由定时器时间调度算法类的cancel函数来完成。
删除定时器处理器的顺序如图3-14所示。
图3-14 删除定时器处理器的顺序图
3.9 Notify事件调度的分析
Reactor框架除了支持上述3中常用事件外,还提供了一种更为灵活的事件处理方式,那就是Notify事件。Notify事件即可以唤醒被阻塞的系统调用,又可以用于应用程序向框架发送一个事件。它使用一个管道,应用程序在管道的写端写入事件,而框架则在管道的读端读取事件,在Reactor管理器的调度下,实现事件的调度。Notify事件的接口也使用ACE_Event_Handler类。
Notify事件的管理器的总体结构如图3-15所示。
图3-15 Notify事件的总体结构图
其中,ACE_Reactor_Notify是一个接口,而ACE_Select_Reactor_Notify类是一种Notify事件管理的实现,称之为Notify事件管理器,用于Select Reactor管理器处理Notify事件。
Reactor管理器有一个ACE_Reactor_Notify类型的数据成员,用于保存ACE_Reactor_Notify对象。Reactor管理器初始化时,在open函数中创建ACE_Select_Reactor_Notify对象。
3.9.1 Notify事件管理器的分析
ACE_Select_Reactor_Notify类是Notify管理器的实现,Reactor管理器使用它来管理Notify事件。
(1)open函数
open函数是ACE_Select_Reactor_Notify类的初始化函数,它的功能主要有两部分:①首先初始化管道,包括对管道描述符属性的设置;②然后将ACE_Select_Reactor_Notify对象注册到Reactor框架中,让其接受Reactor框架的调度。
open函数调用Reactor框架的register_handler函数将ACE_Select_Reactor_Notify对象本身注册到Reactor框架中,注册的事件是读事件,描述符是管道的读描述符。
由于管道的读文件描述符一直注册在框架中,所以框架的每次调度都会检查读描述符是否有事件发生。而应用程序可以通过写描述符写入事件,这样Reactor管理器就能调度应用程序写入的事件。
(2)notify函数
notify函数被Reactor管理器调用,想Reactor框架发送Notify事件。
(3)dispatch_notifications函数
dispatch_notifications函数是由Reactor管理器调用的事件调度函数。管道的I/O事件已经通过select函数分离出,该函数的作用是找到事件处理器,调用事件处理函数。
在open函数中,ACE_Select_Reactor_Notify对象在注册到Reactor框架中时使用的事件是读事件,描述符是管道的读描述符,所以调度函数使用的描述符集是读描述符集,事件的处理函数是handle_input函数。
(4)handle_input函数
调用handle_input函数,表示管道中已经有了数据,现在可以从中取出数据进行处理了。
(5)dispatch_notify函数
dispatch_notify函数把事件的处理交给应用程序的处理函数,具体由哪一个函数来处理由注册事件时的马赛克标志来决定。默认使用ACE_Event_Handler::EXCEPT_MASK,处理函数是handle_exception。
(6)close函数
close函数用于关闭Notify事件管理器。
3.9.2 Notify事件注册流程的分析
在Reactor管理器中,Notify事件是通过notify函数注册到框架中。notify函数将调用转发给Notify事件管理器的notify函数。
注册Notify事件处理器的顺序图如图3-16所示。
图3-16 注册Notify事件处理器的顺序图
3.9.3 Notify事件调度流程的分析
在Reactor管理器中,对Notify事件的调度是在dispatch函数中完成的。dispatch_notification_handlers函数直接使用了dispatch_notifications函数。
Notify事件的调度顺序如图3-17所示。
图3-17 调度Notify事件处理器的顺序图
Notify事件和其他事件的一个区别在于:Notify事件是通过管道从应用程序端传递到框架中的,所以Notify事件一旦提交,便不能删除,这也是框架没有提供删除Notify事件的接口的原因。
参考文献:
[1] ACE技术内幕:深入解析ACE架构设计与实现原理