如何终止对sigwaitinfo的呼叫?

时间:2018-08-04 16:55:27

标签: c++ linux signals

背景

我的目标是在专用线程上处理某些信号,而不是在信号发出时在我的进程中正在运行的任何线程上处理它们。

我正在执行以下操作(在此示例中,仅适用于信号16):

在主线程上,在启动任何其他线程之前(忽略错误处理)

sigset_t sigset;
sigaddset(&sigset, 16);
sigprocmask(SIG_BLOCK, &sigset, nullptr);

然后,我创建一个线程来等待这些信号(在此示例中仅为16):

std::thread _thread = std::thread([&]()
{
   int ret = sigwaitinfo(&sigset, nullptr);
   if (ret == 16)
   {
       // handle signal 16
   }
});

这很好。

问题

但是,我希望能够在需要时取消对sigwaitinfo的调用。

两个不足的解决方案

我尝试了两种解决方案,但都不足够:

1。轮询

(有效的)一个选项不是使用sigwaitinfo,而是使用sigtimedwait,它接受超时参数。 这使我可以使用轮询,并在下次通话返回并设置一些取消标志时取消。 线程中的代码如下:

std::atomic<bool> _cancel (false);
std::thread _thread = std::thread([&]()
{
    timespec _timespec {0, 1}; // 1 second
    int ret = sigtimedwait(&sigset, nullptr, _timespec);
    if (_cancel) 
    {
        return;
    }
    if (ret == 16)
    {
        // handle signal 16
    }
});

为了取消,我只需要在主线程中设置_cancel标志。 这种解决方案的问题在于,轮询会导致(取消)响应性和检查取消标志所完成的工作量之间的典型权衡。

2。提高()/ sigqueue()/杀死()

在此解决方案中,我通过以下调用将专用信号添加到信号掩码,例如SIGUSR1:

sigset_t sigset;
sigaddset(&sigset, 16);
sigaddset(&sigset, SIGUSR1); // <-- added call here
sigprocmask(SIG_BLOCK, &sigset, nullptr);

然后,当我需要取消对sigwaitinfo的调用时,我设置了一个取消标志并调用raise(SIGUSR1)

线程中的代码如下:

std::atomic<bool> _cancel (false);
std::thread _thread = std::thread([&]()
{
   int ret = sigwaitinfo(&sigset, nullptr);
   if (_cancel) // <-- now check _cancel flag before handling signal
   {
       return;
   }
   if (ret == 16)
   {
       // handle signal 16
   }
});

取消操作如下:

_cancel = true; // <-- set the flag before raising the signal
raise(SIGUSR1);

此解决方案的问题是它不起作用,因为对raise()的调用不会导致sigwaitinfo在专用线程中返回。我相信根据文档,它只会在执行线程本身中发出信号。 sigqueue()和kill()也不起作用。

摘要

有没有一种方法可以使sigwaitinfo过早返回,而无需循环调用sigtimedwait并超时?

2 个答案:

答案 0 :(得分:0)

使用pthread_kill将信号发送到特定线程。

例如,代替raise(SIGUSR1);

if(int rc = ::pthread_kill(_thread.native_handle(), SIGUSR1))
    // Handle pthread_kill error.

答案 1 :(得分:0)

这是我找到的解决方案。

  1. 与其使用sigtimedwait等待,不如使用signalfd获取代表要处理的信号的文件描述符。 (与问题中提出的解决方案一样,首先需要调用sigprocmask或类似内容。)
  2. 调用eventfd返回“事件”文件描述符。
  3. 在两个文件描述符上调用轮询等待。这块。循环执行此操作。
  4. 通过在不同线程上写入事件文件描述符来取消信号。
  5. 轮询返回时,通过检查revents字段来检查发信号的文件描述符。
  6. 如果通知了事件文件描述符从循环中中断。
  7. 否则(signalfd信号已被信号通知)读取信号描述并通过调用处理程序来处理信号。然后再次循环调用民意调查。

我已验证此解决方案是可靠的。 可以在以下文档中找到更多详细信息: signalfdeventfdpoll