GPDB中Latch等待与唤醒实现机制
GreenPlum/PostgreSQL中有很多Latch以帮助多进程以及主线程与从线程之间协作。那么Latch是如何实现的呢?Latch可以在多进程之间以及主线程和从线程之间实现等待与唤醒,WaitEventSet是实现这项功能的关键结构。本文我们聊聊WaitEventSet的实现以及一个用法。
1、WaitEventSet的结构
1)WaitEventSet用于管理注册的事件,nevens表示注册的事件个数,nevents_space表示该Set最多可以管理事件个数,比如WaitLatchOrSocket函数中这个变量值为3,仅可以管理事件WL_LATCH_SET、WL_POSTMASTER_DEATH、WL_EXIT_ON_PM_DEATH。可以设置的事件包括以下几种:
WL_LATCH_SET:等待latch被set
WL_POSTMASTER_DEATH:等待postmaster die
WL_SOCKET_READABLE:等待socket可读
WL_SOCKET_WRITEABLE:等待socket可写
WL_EXIT_ON_PM_DEATH:postmaster死了后立即退出。AddWaitEventToSet函数中可以看到若事件WL_EXIT_ON_PM_DEATH则立即改成WL_POSTMASTER_DEATH,也就是说在postmaster进程挂了后可以探测到,并退出。
epoll_fd为epoll实例的文件描述符。
latch:如果事件有WL_LATCH_SET,则该latch为需要等待latch。通过is_set来实现等待。
epoll_ret_event[]数组:epoll_wait将发生的事件集合从内核复制到该数组中。
2)WaitEvent表示注册的事件,pos表示该事件位于events[]数组的第几个;fd为事件相关的socket fd,也就是监听这个fd上的事件;events即为需要监听的事件。
3)WaitEvent使用的函数顺序为:
CreateWaitEventSet
AddWaitEventToSet
WaitEventSetWait
FreeWaitEventSet
下面依次介绍下这几个函数是干什么的。
2、CreateWaitEventSet
WaitEventSet使用前当然需要先创建,这件事就由函数CreateWaitEventSet来完成。该函数在当前内存上下文中为n个事件(个数为函数入参)申请内存空间,并进行初始化。
GPDB中使用epoll来实现事件的等待与唤醒,该函数也会调用epoll_create来打开一个epoll文件描述符,创建一个epoll实例。
3、AddWaitEventToSet
添加事件到WaitEventSet中。并调用epoll_ctl函数将想要监听的文件描述符添加到epoll实例中。
4、WaitEventSetWait
等待注册的事件发生。调用epoll_wait函数来监听epoll上注册的事件。如果没有任何IO事件,则一直被阻塞,直到有IO事件发生。
5、FreeWaitEventSet
关闭epoll文件描述符,释放WaitEventSet。
6、以master motion接收端receiveChunksUDPIFC为例
epoll如何与管道协作,来实现WaitEventSet的等待与唤醒呢?
1)fork出一个子进程时,子进程会将父进程继承的管道关闭掉。然后自己重新创建一个管道。得到2个fd。pipefd[0]用于读取写入管道的内容,pipefd[1]用于向管道写入。通过全局变量selfpipe_readfd和selfpipe_writefd分别表示读和写fd。当然,这是用于同一个进程不同线程之间进行管道通信。
2)将管道的selfpipe_readfd及epoll的EPOLLIN事件注册到epoll实例中,当主进程进入epoll_wait等待时,一旦监听到管道的selfpipe_readfd端可读,也就是管道中被写入了东西,就唤醒,退出等待。
3)receiveChunksUDPIFC用于接收segment发来的数据,当然这是UDPIFC模式下使用。若连接上没有数据,则将WL_LATCH_SET、WL_TIMEOUT注册到epoll实例中
4)WaitLatchOrSocket用于创建epoll实例并注册监听事件。
(1)首先调用Create_WaitEventSet->epoll_create创建一个epoll实例
(2)WL_TIMEOUT时,timeout为MAIN_THREAD_COND_TIMEOUT_MS即250ms,也就是epoll_wait的超时时间是250ms。若无指定WL_TIMEOUT则timeout值为-1,表示epoll_wait阻塞等待,一直等到被唤醒。
(3)指定WL_LATCH_SET时,WaitEventSet.latch被设置成ic_control_info.latch,并通过epoll_ctl监听数据的到来
(4)本案例中,共注册3个WaitEvent,这里关注第一个,也就是selfpipe_readfd管道上的EPOLLERR、EPOLLHUP、EPOLLIN33个事件。接收数据的线程rxTreadFunc接收到数据后会向管道写1个字节数据,从而被epoll监听到,以退出epoll_wait。这样完成没数据到来时等待,有数据到来时就唤醒的功能: