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; }
参考
打赏作者以资鼓励: