Sigevent 计时器无法重新启动,也无法执行回调

Sigevent timer can't be restarted nor it executes a callback

我有一个 t_timer 设置,使用 struct sigevent 和 struct itimerspec 为 6 秒。

如果某个事件发生,我的程序应该刷新这个计时器,否则,在计时器到期时,应该执行回调,最后程序应该停止

然而,不仅回调被完全忽略,而且我无法重新启动所述计时器。

以下是一些前奏代码,其中使用了我class中的变量。我尽量缩短和简化代码。

typedef struct itimerspec TTL;
typedef struct sigevent handler;

struct RtnodeSpvSession : public rtnode::SessionInterface {
   boost::asio::io_service             iSvc;
   timer_t                             tTimer;
   TTL                                 tTimerSetter;
   void OnStart();
   void EnqueueRead();
   void Thread_TTL();
   void ResetTTL;
}

void RtnodeSpvSession::OnStart()
{
   printf("Starting RtnodeSpvSession...\n");
   TTL_Thread();
   iSvc.post(boost::bind(&RtnodeSpvSession::EnqueueRead, this));
}

EnqueueRead 每 x 秒调用一次,if 语句检查确定是否必须重置计时器的条件

void RtnodeSpvSession::EnqueueRead()
{
   if (1<2)
   {
      ResetTTL();
   }
   else
   {
      //do nothing
   }
}

sigevent.At 计时器过期的回调方法此字符串从未被打印,也似乎根本没有调用该方法

static void catch_alarm(union sigval sv)
{
   std::cout << "Imma dead\n\n\n\n";
}

最后创建计时器

void RtnodeSpvSession::TTL_Thread()
/**
 * Starts the timeout timer
 * When tTimerSetter expires, tTimeoutHandler executes the callback on method catch_alarm
 * and triggers the process kill
 */
{
   handler tTimeoutHandler;
   tTimeoutHandler.sigev_notify = SIGEV_THREAD_ID;            /* Notify via thread */
   tTimeoutHandler.sigev_notify_function = catch_alarm;     /* Thread start function */
   tTimeoutHandler._sigev_un._tid = syscall(SYS_gettid);
   tTimeoutHandler.sigev_signo = SIGHUP;
   tTimeoutHandler.sigev_notify_attributes = NULL;

   if (timer_create(CLOCK_REALTIME, &tTimeoutHandler, &tTimer) == -1) {
      perror("timer_create");
      exit(1);
   }

   memset(&tTimerSetter,0,sizeof(tTimerSetter));
   tTimerSetter.it_value.tv_nsec = 0;
   tTimerSetter.it_value.tv_nsec = 0;
   tTimerSetter.it_value.tv_sec = 6;
   tTimerSetter.it_interval.tv_nsec = 0;
   tTimerSetter.it_interval.tv_sec = 0;
   if (timer_settime(tTimer, 0, &tTimerSetter, NULL) == -1) {
      perror("timer_settime");
      exit(1);
   }
}

这是我明显有缺陷的重置:我认为从头开始删除并重新创建计时器是我的下一个最佳选择,因为我无法直接操作现有计时器。但是没用。

void RtnodeSpvSession::ResetTTL()
/// Resets the process's TTL
{
   std::cout << "BEGINNING RESET\n\n\n";
   timer_delete(tTimer);
   TTL_Thread();
}

我找到了一些关于这个的东西。如果使用SIGEV_THREAD_ID,定时器会向线程id发送一个信号。所以你必须在线程中安装一个信号处理程序,函数 catch_alarm 不会被调用,只是信号处理程序,但在线程中。

如果您使用 SIGEV_THREAD,那么函数 catch_alarm 将被调用,但是您不能设置线程 ID,因为它会以您的名义创建。

以下代码如您所愿:

typedef struct itimerspec TTL;
typedef struct sigevent handler;

void catch_alarm(union sigval sv)
{
    //std::cout << "Imma dead\n\n\n\n";
    printf ("\nSignaled in %d = %d\n\n", syscall(SYS_gettid), sv.sival_int);
    for (int i = 0; i < 5; ++i) {
        printf ("Process in thread %d\n", i);
        sleep(1);
    }
}

int main ()
{
    timer_t tTimer;
    TTL tTimerSetter;
    handler tTimeoutHandler;

    printf ("My pid: %d %d\n", getpid(), syscall(SYS_gettid));
    
    memset(&tTimeoutHandler,0,sizeof(tTimeoutHandler));
    tTimeoutHandler.sigev_notify = SIGEV_THREAD;            /* Notify via thread */
    tTimeoutHandler.sigev_notify_function = catch_alarm;     /* Thread start function */
    tTimeoutHandler.sigev_value.sival_int = 9999;
    tTimeoutHandler.sigev_notify_attributes = NULL;

    printf ("Creating timer\n");
    if (timer_create(CLOCK_REALTIME, &tTimeoutHandler, &tTimer) == -1) {
        perror("timer_create");
        exit(1);
    }

    memset(&tTimerSetter,0,sizeof(tTimerSetter));
    tTimerSetter.it_value.tv_nsec = 0;
    tTimerSetter.it_value.tv_nsec = 0;
    tTimerSetter.it_value.tv_sec = 5;
    tTimerSetter.it_interval.tv_nsec = 0;
    tTimerSetter.it_interval.tv_sec = 0;
    if (timer_settime(tTimer, 0, &tTimerSetter, NULL) == -1) {
        perror("timer_settime");
        exit(1);
    }

    printf ("To wait\n");

    for (int i = 0; i < 10; ++i) {
        printf ("Waiting %d\n", i);
        sleep(1);
    }

    printf ("Finished\n");
    return 0;
}

输出:

My pid: 27473 27473
Creating timer
To wait
Waiting 0
Waiting 1
Waiting 2
Waiting 3
Waiting 4

Signaled in 27477 = 9999

Process in thread 0
Waiting 5
Process in thread 1
Waiting 6
Process in thread 2
Waiting 7
Process in thread 3
Waiting 8
Process in thread 4
Waiting 9
Waiting 10
Waiting 11
Waiting 12
Waiting 13
Waiting 14
Finished

SIGEV_THREAD_ID (Linux-specific) As for SIGEV_SIGNAL, but the signal is targeted at the thread whose ID is given in sigev_notify_thread_id

关键:至于SIGEV_SIGNAL.

如前所述,如果您使用 SIGEV_THREAD_ID,则必须创建线程,并且 信号 将发送到该线程。