ACE_Reactor一些重要的细节
看下具体ACE_Dev_Poll_Reactor的实现,如何将一个处理集和handle关联起来,代码如下:
int ACE_Dev_Poll_Reactor ::register_handler_i(handle, event_handler,mask)
//step 1
if(this->handler_rep_.find(handle)==0)
{
//Handler not present in the repository.Bind it.
if(this->handler_rep_.bind(handle,event_handler, mask)!=0)
return -1;
}
//step2
//All but the notify handler get registered with oneshot to facilitate auto suspend before the upcall.See dispatch_io_event for more information
if(event_handler!= this->notify_handler_)
epev.events |= EPOLLONESHOT;
if(::epoll_ctl(this->poll_fd_, op, handle, &epev) == -1)
......
step1
首先就是要将handle和处理这个handle的event_handler绑定,若已有处理的event_handler,则只单纯将可能新增的mask添加进监测的epoll函数参数中。
这里的handler_rep_是一个Handler_Repository类的指针,其内部是一个map表可以对handle和event_handler进行有效关联对应和存储。
step2
从上面可以看出,如果是epoll的话,且注册了notify handler, 则epoll_wait会自动将EPOLLONESHOT标识添加上,这样在事件被监测到得分发处理时就不用再手动的去suspend。
Notify机制的好处是:
1. 让反应器拥有了处理无限处理器的能力
2. 其次是提供了必要时解除反应器事件检查阻塞的能力。
Reactor的notify()让用户直接提供给Reactor待通知反应器的指针,而这些处理器无需注册到反应器上,从而提供了无限的扩展能力。
不过在ACE中显然Notify机制有不同的实现方式。一种是采用ACE_Pipe的实现,这个属于默认的方式。另一种则是内存队列保存Notify消息。你可以通过定义ACE_HAS_REACTOR_NOTIFICATION_QUEUE的宏编译ACE,这样ACE将不使用ACE_Pipe作为Notify消息的管道,而使用一个自己的内存队列保存Notify消息。
但是需要注意的是,在使用ACE_Pipe的实现中,如果使用不当,可能会造成严重的后果。
如果你用不到Notify机制,最好在ACE_Reactor初始化的时候彻底关闭Notify机制。很多Reactor的初始化函数都提供了关闭notify pipe的方式。比如ACE_Select_Reactor_T的open函数的disable_notify_pipe参数。当其为1的时候表示关闭notify 管道。
潜在的风险:
1. 处理器被销毁后,排队等候的对应通知才被分派
2. ACE_Pipe的数据缓冲是有限的,大量通知到来可能会造成阻塞甚至死锁
因为通知的信息(包括处理器指针和掩码)以流数据的方式被写入ACE_Pipe,不能进行遍历查找,所以当对应处理器被销毁后,如果其在ACE_Pipe的数据区中还有存储有通知,则ACE_Reactor将会在分派中使用悬空指针,造成未知的后果。另外,在ACE_Pipe的数据缓冲已满情况下,在处理器的回调中依然发送通知,就会因阻塞发生死锁。
通过
int ACE_Dev_Poll_Reactor ::notify(eh, mask,timeout)
{
//pass over both the event_handler* and * the mask o allow the caller to dictate which Event_Handler method the receiver invokes.Note that this call can timeout.
n = this->notify_handle_->notify(eh, mask, timeout);
return n=-1?-1:0;
}
在源码的owner接口中,看到了这样一句注释
//There is no need to set the owner of the event loop.Multiple threads may invoke the event loop simulataneously.
说明有可能多个线程同时调用ACE_Dev_Poll_Reactor的event_loop函数,网上很多示例代码写的都比较简单,例如:
65 int main(void)
66 {
67 ACE::init();
68
69 ACE_Dev_Poll_Reactor dev_reactor(1024);
70 ACE_Reactor reactor(&dev_reactor);