ACE_Select_Reactor的Notify功能实现浅析

来至:http://www.jiazi.cn/web/?9240/viewspace-304
作者: ydogg
1.     前言
本文关注平台为Win32, 但在其它的类Unix 平台,就实现 框架而言并没有太多变 化 ,惟一不同的是用于底层 机制的ACE_Pipe 的实现 。
但是,为了解决某些特别的原因,如大量的通知存储、规避悬空处理器指针等, ACE 也提供了一种有别于 Pipe 的解决方案,其采用消息排队的方式工作。当采取这种方式时,需定义 ACE_HAS_REACTOR_NOTIFICATION_QUEUE 宏并重生成 ACE

2.     Notify能力、风险和风险

Notify机制最重要的能力是:
1.    
让反应器拥有了处理无限处理器的能力
2.    
其次是提供了必要时解除反应器事件检查阻塞的能力。

Reactornotify()让用户直接提供给Reactor待通知反应器的指针,而这些处理器无需注册到反应器上,从而提供了无限的扩展能力。但是,在使用ACE_Pipe的实现中,如果使用不当,可能会造成严重的后果。

潜在的风险:
1.    
处理器被销毁后,排队等候的对应通知才被分派
2.    
ACE_Pipe的数据缓冲是有限的,大量通知到来可能会造成阻塞甚至死锁

因为通知的信息(包括处理器指针和掩码)以流数据的方式被写入ACE_Pipe,不能进行遍历查找,所以当对应处理器被销毁后,如果其在CE_Pipe的数据区中还有存储有通知,则ACE_Reactor将会在分派中使用悬空指针,造成未知的后果。另外,在ACE_Pipe的数据缓冲已满情况下,在处理器的回调中依然发送通知,就会因阻塞发生死锁。

在采用ACE_PipeNotify实现中,以上两个问题的规避只能依靠用户自己的应用机制。

不过,在采用队列的Notify实现中,ACE给提供了问题的解决方法。
1.    
采用队列方式可无限扩展Notify的数量
2.    
采用队列方式为通知的查询提供的方便,因此ACE_Reactor提供puerge_pending_notifications()方法来移除指定的通知,当处理器销毁时,可以使用该方法移除当前队列中所有和自己相关的通知,避免了使用悬空指针。

在采用ACE_Pipe方式中,puerge_pending_notifications()被实现为空方法。


3.     ACE_Select_Reactor类图
可以看出,ACE_Select_Reactor_T 是具体的实现负责 者。

4.     参与Notify机制的类
参与Notify 机制的类 有:
ACE_Select_Reactor_Impl 、ACE_Select_Reactor_Notify 和ACE_Pipe 。从ACE_Select_Reactor 开始,采用自上而向下的顺 序进 行分析。
 

 
5.     实现原理
各类 型Reactor 的实现 方法有所不同,比如Select_Reactor 是通过 管道来实现notify 功能,而WFMO则 通过ACE_Event类 型 的的事件加通知队列来实现 。
 
Reactor 在初始化时 ,会创 建 一个pipe 。然后将该pipe 的句柄注册到Reactor 。这样 ,这 个Pipe 就处 于Reactor 的事件分派句柄集中,像其他普通句柄一样 ,被Reactor 所监测 和分派。当用户调 用notify()时 ,notify() 会把参数信息写入这 个 管道,通过事件监测,从而Reactor 就能得知有新notify 到来,从管道读 取 相关数据后,就可以进 行 相应 的分派处 理 。从而实现 了通知和事件分派的序列化。
 
详细 的来说 ,ACE为 所有的Reactor 都定义 了专为 它们 自己服务 的Notify类 ,如
ACE_Select_Reactor_Notify 和ACE_WFO_Reactor_Notify 。
 
在类Unix 平台上,支持管道并且select 可用于监测 管道事件。但是在Win32 平台并不支持管道功能,而且Winsock 的select 只能支持对socket 句柄的事件监测 。因此,ACE 在Win32 平台用一个本地连 接 来模拟Pipe 的功能,其中接受连 接 的socket 句柄作为Pipe读 句柄,而发 起连 接 的socket 句柄作为Pipe 的写句柄。
 
可以看出,notify 功能的实现机制类似于ACE_Reactor 的标 准 用法,并 且也有一个类 似于ACE_Event_Handler 的类 存在,只不过 是被包含在Reactor 中。在逻辑 意义 上,就是一个本地连 接 ,用于和进 程 内的其他部分的通信(略有差别的地方在于事件分派的方式) 。
 
对 于ACE_Select_Reactor, ACE_Pipe 充当了ACE_Sock_Stream 的角色,而ACE_Select_Reactor_Notify则 充当了ACE_Event_Handler 的角色。ACE_Select_Reactor_Notify 派生自ACE_Reactor_Notify, 而ACE_Reactor_Notify 正是派生自ACE_Event_Handler.

6.     相关变量

ACE_Select_Reactor_Impl类中,定义了类型为ACE_Reactor_Notify*的成员变量
 

/ Callback object that unblocks the ACE_Select_Reactor if it's
/// sleeping.
ACE_Reactor_Notify *notify_handler_;


 

7.     实现分析

7.1     初始化
ACE_Select_Reactor_T 的open 函数定义 如下:
 virtual int open (size_t max_number_of_handles = DEFAULT_SIZE,
                    
int restart = 0,
                    ACE_Sig_Handler 
* = 0,
                    ACE_Timer_Queue 
* = 0,
                    
int disable_notify_pipe = ACE_DISABLE_NOTIFY_PIPE_DEFAULT,
                    ACE_Reactor_Notify 
* = 0);

其中ACE_DISABLE_NOTIFY_PIPE_DEFAULT值为0,表示默要使用Notify功能。
传递给ACE_Select_Reactor_Notify的构造函数。open()实现中:

   if  (result  !=   - 1   &&   this -> notify_handler_  ==   0 )
    
{
      ACE_NEW_RETURN (
this->notify_handler_,
                      ACE_Select_Reactor_Notify,
                      
-1);

      
if (this->notify_handler_ == 0)
        result 
= -1;
      
else
        
this->delete_notify_handler_ = true;
    }


  
if  (result  !=   - 1   &&   this -> handler_rep_.open (size)  ==   - 1 )
    result 
=   - 1 ;
  
else   if  ( this -> notify_handler_ -> open ( this ,
                                        
0 ,
                                        disable_notify_pipe) 
==   - 1 )
    
{
      ACE_ERROR ((LM_ERROR,
                  ACE_TEXT (
"%p "),
                  ACE_TEXT (
"notification pipe open failed")));
      result 
= -1;
    }


 

如果没有使用外部的notifyACE_Select_Reactor_T将会notify_handler指向一个new出来的ACE_Select_Reactor_Notify型的象,并用它的open()方法行初始化。

ACE_Select_Reactor_Notifyopen()实现如下(了非关):

   if  (disable_notify_pipe  ==   0 )
    {
      
this -> select_reactor_  =
        dynamic_cast
< ACE_Select_Reactor_Impl  *>  (r);

      
if  (select_reactor_  ==   0 )
        {
          errno 
=  EINVAL;
          
return   - 1 ;
        }

      
if  ( this -> notification_pipe_.open ()  ==   - 1 )
        
return   - 1 ;

      
if  (ACE::set_flags ( this -> notification_pipe_.read_handle (),
                          ACE_NONBLOCK) 
==   - 1 )
        
return   - 1 ;
      
else
        
return   this -> select_reactor_ -> register_handler
          (
this -> notification_pipe_.read_handle (),
           
this ,
           ACE_Event_Handler::READ_MASK);
    }
  
else
    {
      
this -> select_reactor_  =   0 ;
      
return   0 ;
    }

 

注意,里的disable_notify_pipe就是ACE_Select_Reactor_Topen()方法中传递的参数,通它,可以置不使用notify功能。
可以看出,
ACE_Select_Reactor_Notifyopen()的主要功能是初始化自己的ACE_Pipe型成员对notification_pipe_,并把它注册到Reactor中,监测read事件。

ACE_Pipeopen()实现如下(Win32平台并除了非关):

  ACE_INET_Addr my_addr;
  ACE_SOCK_Acceptor acceptor;
  ACE_SOCK_Connector connector;
  ACE_SOCK_Stream reader;
  ACE_SOCK_Stream writer;
  
int  result  =   0 ;
if  defined (ACE_WIN32)
  ACE_INET_Addr local_any  (static_cast
< u_short >  ( 0 ), ACE_LOCALHOST);
# endif
   
if  (acceptor.open (local_any)  ==   - 1
      
||  acceptor.get_local_addr (my_addr)  ==   - 1 )
    result 
=   - 1 ;
  
else
    
{
      ACE_INET_Addr sv_addr (my_addr.get_port_number (),
                             ACE_LOCALHOST);

      
// Establish a connection within the same process.
      if (connector.connect (writer, sv_addr) == -1)
        result 
= -1;
      
else if (acceptor.accept (reader) == -1)
        
{
          writer.close ();
          result 
= -1;
        }

    }


  acceptor.close ();
  
if  (result  ==   - 1 )
    
return   - 1 ;

  
this -> handles_[ 0 =  reader.get_handle ();
  
this -> handles_[ 1 =  writer.get_handle ();


可以看到,ACE_Pipe了一个自身的Tcp接来模Pipe写句柄分就是两端的socket句柄,很巧妙的Pipe移植。ACE_Piperead_handle()函数返回的是handles_[0],也就是说,注册到Reactor是读socket,handles_[1]将会被客户所使用。


7.2    方法调

使用notify相关方法ACE_Select_Reactor_T充当中介,会将将它直接交由内部的notify_handler对象理。
 ACE_Select_Reactor_Tnotify()代码:

 ssize_t  const  n  =   this -> notify_handler_ -> notify (eh, mask, imeout);
  
return  n  ==   - 1   ?   - 1  :  0 ;

ACE_Select_Reactor_Nofitynotify()函数代码:

if  ( this -> select_reactor_  ==   0 )
    
return   0 ;

  ACE_Event_Handler_var safe_handler (event_handler);

  
if  (event_handler)
    event_handler
-> add_reference ();

  ACE_Notification_Buffer buffer (event_handler, mask);
  ssize_t 
const  n  =  ACE::send ( this -> notification_pipe_.write_handle (),
                               (
char   * & buffer,
                               
sizeof  buffer,
                               timeout);
  
if  (n  ==   - 1 )
    
return   - 1 ;

  
//  No failures.
  safe_handler.release ();

将用户传递的“目的Handler“和”掩“参数,写入了管道,实则是通过socket句柄发送了数据。ACE_Notification_Buffer是一个简单的数据包裹类。

7.3    事件分派

Reactor()事件多路分离流程:
ACE_Reactor::run_reactor_event_loop() -> ACE_Select_Reactor_T::handle_events() ->
ACE_Select_Reactor_T::
handle_events_i()

ACE_Select_Reactor_T::handle_events_i()代码如下:

ACE_SEH_TRY
    {
      
//  We use the data member dispatch_set_ as the current dispatch
      
//  set.
      
//  We need to start from a clean dispatch_set
       this -> dispatch_set_.rd_mask_.reset ();
      
this -> dispatch_set_.wr_mask_.reset ();
      
this -> dispatch_set_.ex_mask_.reset ();

      
int  number_of_active_handles  =
        
this -> wait_for_multiple_events ( this -> dispatch_set_,
                                        max_wait_time);

      result 
=
        
this -> dispatch (number_of_active_handles,
                        
this -> dispatch_set_);
    }
  ACE_SEH_EXCEPT (
this -> release_token ())
    {
       …
    }

Reactor在监测事件时,不区分Noftiy用句柄和其它句柄,一视同仁。所以无需关心wait_for_multiple_events()的实现。
ACE_Select_Reactor_T:: dispatch ()的关键代码如下:

if  ( this -> dispatch_timer_handlers (other_handlers_dispatched)  ==   - 1 )
        
//  State has changed or timer queue has failed, exit loop.
         break ;
if  ( this -> dispatch_notification_handlers
               (dispatch_set,
                active_handle_count,
                other_handlers_dispatched) 
==   - 1 )
        
//  State has changed or a serious failure has occured, so exit
        
//  loop.
         break ;
this -> dispatch_io_handlers
               (dispatch_set,
                active_handle_count,
                io_handlers_dispatched) 
==   - 1 )
        
//  State has changed, so exit loop.
         break ;

可以看出,Select_Reactor分派事件时是按照:定时器、通知、IO事件的顺序进行的。
这里也许有人会疑惑,为什么不直接将notify的处理放在io的分派函数
dispatch_io_handlers()中处理呢? 这个想法合情合理,但是Select_Reactor中,规定notify事件的优先级要比普通IO事件的优先级高,以便可以处理一些优先级较高的突发事件。所以必须在IO事件分派前,主动的分派notify事件。并且notify事件分派后,需要将Pipe的句柄从dispatch_set去除,以免在IO事件分派中被重复分派。


dispatch_notification_handlers
()代码:

int   const  n  =
    
this -> notify_handler_ -> dispatch_notifications (number_of_active_handles,
                                                   dispatch_set.rd_mask_);

  
if  (n  ==   - 1 )
    
return   - 1 ;
  
else
    {
      number_of_handlers_dispatched 
+=  n;
      number_of_active_handles 
-=  n;
    }

ACE_Select_Reactor_T::dispatch_notifications()函数代码:

ACE_HANDLE  const  read_handle  =
    
this -> notification_pipe_.read_handle ();

  
if  (read_handle  !=  ACE_INVALID_HANDLE
      
&&  rd_mask.is_set (read_handle))
    
{
      
--number_of_active_handles;
      rd_mask.clr_bit (read_handle);
      
return this->handle_input (read_handle);
    }

  
else
    
return   0 ;

到了这里,handle_input出现了,下面的处理和ACE_Event_Handler相近了。
ACE_Select_Reactor_Notify::handle_input()函数代码:

int  number_dispatched  =   0 ;
  
int  result  =   0 ;
  ACE_Notification_Buffer buffer;

  
while  ((result  =   this -> read_notify_pipe (handle, buffer))  >   0 )
    {
      
if  ( this -> dispatch_notify (buffer)  >   0 )
        
++ number_dispatched;

      
if  (number_dispatched  ==   this -> max_notify_iterations_)
        
break ;
    }

 

代码逻辑很清楚,从Pipe的缓冲区中依次读取所有接受到的Notify事件信息,并且交给dispatch_notify去处理,如果达到设定值max_notify_iterations_(ACE_Reactor:: max_notify_iterations()函数的功能),就停止分派。

需要注意:由于是依次从Pipe中读取信息,如果在用户指定的Notify处理Handler中调用notify()(嵌套使用),给Pipe在此写入信息,就容易造成Reactor的死循环。
WFMO_Reactor的对应实现中,因为使用Event,所以对应的函数是handle_signal().

ACE_Select_Reactor_Notify::dispatch_notify()函数代码:

if  (buffer.eh_  !=   0 )
    {
      ACE_Event_Handler 
* event_handler  =  buffer.eh_;

      
bool   const  requires_reference_counting  =
        event_handler
-> reference_counting_policy ().value ()  ==
        ACE_Event_Handler::Reference_Counting_Policy::ENABLED;

      
switch  (buffer.mask_)
        {
        
case  ACE_Event_Handler::READ_MASK:
        
case  ACE_Event_Handler::ACCEPT_MASK:
          result 
=  event_handler -> handle_input (ACE_INVALID_HANDLE);
          
break ;
        
case  ACE_Event_Handler::WRITE_MASK:
          result 
=  event_handler -> handle_output (ACE_INVALID_HANDLE);
          
break ;
        
case  ACE_Event_Handler::EXCEPT_MASK:
          result 
=  event_handler -> handle_exception (ACE_INVALID_HANDLE);
          
break ;
        
case  ACE_Event_Handler::QOS_MASK:
          result 
=  event_handler -> handle_qos (ACE_INVALID_HANDLE);
          
break ;
        
case  ACE_Event_Handler::GROUP_QOS_MASK:
          result 
=  event_handler -> handle_group_qos (ACE_INVALID_HANDLE);
          
break ;
        
default :
          
//  Should we bail out if we get an invalid mask?
          ACE_ERROR ((LM_ERROR,
                      ACE_TEXT (
" invalid mask = %d " ),
                      buffer.mask_));
        }

      
if  (result  ==   - 1 )
        event_handler
-> handle_close (ACE_INVALID_HANDLE,
                                     ACE_Event_Handler::EXCEPT_MASK);
    }

 

这里是分派的终点,用户传递的用于处理Notify事件的Handler根据mask的类型而被调用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值