GRPC浅析-completion_queue

GRPC浅析-completion_queue

前言 cq_vtable cq_poller_vtable
上一节传送门

前言

completion_queue(以下简称cq)有三中类型,分别是GRPC_CQ_NEXT,GRPC_CQ_PLUCK和GRPC_CQ_CALLBACK。

cq_vtable

/*cq的操作接口如下*/
struct cq_vtable {
  grpc_cq_completion_type cq_completion_type;
  size_t data_size;
  void (*init)(void* data,
               grpc_experimental_completion_queue_functor* shutdown_callback);
  void (*shutdown)(grpc_completion_queue* cq);
  void (*destroy)(void* data);
  bool (*begin_op)(grpc_completion_queue* cq, void* tag);
  void (*end_op)(grpc_completion_queue* cq, void* tag, grpc_error* error,
                 void (*done)(void* done_arg, grpc_cq_completion* storage),
                 void* done_arg, grpc_cq_completion* storage, bool internal);
  grpc_event (*next)(grpc_completion_queue* cq, gpr_timespec deadline,
                     void* reserved);
  grpc_event (*pluck)(grpc_completion_queue* cq, void* tag,
                      gpr_timespec deadline, void* reserved);
};

为cq分配内存时,会分配额外的cq_vtable::data_size + cq_poller_vtable::size()的内存,用于cq_data和poller。poller也就是上一节中提到的pollset。

cq_data有3种,分别是cq_next_data,cq_pluck_data和cq_callback_data,与3种类型的cq分别对应。

cq_next_data内部维护一个完成事件队列(队列的push基于CSA,pop基于Spinlock+CSA),work接口会从队列中消费完成事件。
cq_pluck_data内部维护一个完成事件链表,与cq_next_data不同的是,事件不一定是按照FIFO来消费的。并且维护了一个pluckers,当线程即将进入(也许会)阻塞情况时,将线程对应的woker和tag添加到pluckers里,当有这个tag类型完成事件时,唤醒线程消费完成事件。
cq_callback_data没有维护完成事件的数据结构。

接下来描述cq_vtable 里的接口。
init——在初始化cq_data,参数shutdown_callback只有callback类型的cq才使用到。

shutdown——关闭一个cq。callback类型的cq会调用init时传入的shutdown_callback。

destroy——调用cq_data析构函数。

begin_op——开始一个完成操作。

end_op——产生一次完成事件。不同的cq,end_op有不同的行为。

    对于next类型cq。
    将完成事件添加到cq_data的队列里。如果添加到队列里的事件是队列首个元素,则kick一次worker,这保证线程调用cq_next时不会处于有完成事件却阻塞起来的情况。 对于pluck类型cq。
    将tag完成事件添加到链表里,如果pluckers有此tag对应的worker,则kick此woker。 对于callback类型cq。
    直接消费完成事件,并调度一个callback。

next——只有next类型cq实现此接口。如果有完成事件,则消费返回。否则,进入pollset_work等待I/O事件。

pluck——只有pluck类型cq实现此接口。如果存在对应tag完成事件,则消费返回,否则等待对应的tag完成事件发生。

cq_poller_vtable

/*poller相关操作也就是上一节所说的pollset*/
struct cq_poller_vtable {
  bool can_get_pollset;
  bool can_listen;
  size_t (*size)(void);
  void (*init)(grpc_pollset* pollset, gpr_mu** mu);
  grpc_error* (*kick)(grpc_pollset* pollset,
                      grpc_pollset_worker* specific_worker);
  grpc_error* (*work)(grpc_pollset* pollset, grpc_pollset_worker** worker,
                      grpc_millis deadline);
  void (*shutdown)(grpc_pollset* pollset, grpc_closure* closure);
  void (*destroy)(grpc_pollset* pollset);
};

有3种类型的poller,分别是GRPC_CQ_DEFAULT_POLLING,GRPC_CQ_NON_LISTENING和GRPC_CQ_NON_POLLING。第一种类型poller可以监听所有fd,有正常pollset的操作,而第二种出了listening fd不可监听外,接口行为与第一种完全一样。第三种,与pollset没有关联,也就没有等待I/O事件的行为。

栏目
728_90 cn stocks