GRPC浅析-completion_queue
- 前言
- cq_vtable
- cq_poller_vtable
上一节传送门
前言
completion_queue(以下简称cq)有三中类型,分别是GRPC_CQ_NEXT,GRPC_CQ_PLUCK和GRPC_CQ_CALLBACK。
cq_vtable
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
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事件的行为。