FAIRYFAR-INTERNAL
 
  FAIRYFAR-INTERNAL  |  SITEMAP  |  ABOUT-ME  |  HOME  
您的足迹: openGauss的定时器处理
openGauss的定时器处理

问题

原生PostgreSQL使用Linux的API函数setitimer设置定时器,在一个进程里,如果有多个定时器,那么SIGALRM信号处理函数无法直接区分是哪个定时器的超时响应。PG的做法是:使用一个timer list(数据结构为数组),list按照定时器超时时间排序,时间短的排前面(下标为0),这样就可以知道是哪个定时器超时了。

openGauss情况变得更加复杂,多进程模式改为了多线程模式。PG的timer list无法解决定时器标识问题。

解决方法

openGauss完全抛弃了PG的timer list。改为使用 POSIX 定时器 timer_settime()。

POSIX 定时器使用timer_create创建定时器,并返回TimerId,以唯一标识定时器。

以下是openGauss创建Timer的函数:

snippet.c
#define RES_SIGNAL SIGUSR2
int gs_signal_createtimer(void)
{
    struct sigevent sev;
 
    /*
     * Set up per thread timer.
     * Upon timeout, a SIGUSR2 will be dilivered to current thread itself.
     * The signal handler can check sival_ptr for the reason. We don't want
     * to use SIGEV_THREAD callback method, because it needs certain environment
     * set up to work correctly. Meanwhile, it creates a new thread for the
     * callback, which is inefficiently.
     */
    sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
    sev.sigev_signo = RES_SIGNAL;
    sev.sigev_value.sival_ptr = (void*)(SIGALRM + gs_thread_self());
    sev._sigev_un._tid = syscall(SYS_gettid);
 
    if (timer_create(CLOCK_REALTIME, &sev, &t_thrd.utils_cxt.sigTimerId) == -1) {
        ereport(FATAL, (errmsg("failed to create timer for thread")));
        return -1;
    }
 
    return 0;
}

sev.sigev_value.sival_ptr设置为 SIGALRM + 线程PID,注意,这里timer使用SIGUSR2作为信号,而不是SIGALRM,这样在,进程收到SIGUSR2信号时,可以区分出是哪个定时器超时。

SIGUSR2信号处理函数如下:

snippet.c
static gs_sigaction_func gs_signal_install_handler(void)
{
    struct sigaction act, oact;
 
    /*
     * It is important to set SA_NODEFER flag, so this signal is not added
     * to the signal mask of the calling process on entry to the signal handler.
     */
    sigemptyset(&act.sa_mask);
    act.sa_sigaction = gs_res_signal_handler;
    act.sa_flags = 0;
    act.sa_flags |= SA_SIGINFO;
    act.sa_flags |= SA_RESTART;
 
    if (sigaction(RES_SIGNAL, &act, &oact) < 0) {
        ereport(PANIC, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("not able to set up signal action handler")));
    }
 
    /* Return previously installed handler */
    return oact.sa_sigaction;
}
 
static void gs_res_signal_handler(int signo, siginfo_t* siginfo, void* context)
{
    void* signature = NULL;
    MemoryContext oldContext = CurrentMemoryContext;
 
    /*
     * Calculate the timer signature and mark the SIGALRM by myself. This is
     * because SIGALRM is dilivered by current thread's timer.
     */
    ThreadId thread_id = gs_thread_self();
    signature = (void*)(SIGALRM + thread_id);
 
    if (siginfo->si_value.sival_ptr == signature) {
        if (SIG_IGN != t_thrd.signal_slot->gssignal->handlerList[SIGALRM] &&
            SIG_DFL != t_thrd.signal_slot->gssignal->handlerList[SIGALRM]) {
            (t_thrd.signal_slot->gssignal->handlerList[SIGALRM])(SIGALRM);
        }
 
        CurrentMemoryContext = oldContext;
        return;
    }
 
    /* Hornour the signal */
    gs_signal_handle();
 
    CurrentMemoryContext = oldContext;
    return;
}

参考



打赏作者以资鼓励:
移动端扫码阅读: