C++ Linux定时器挂起信号
我正在使用timer\u create和SIGEV\u线程参数创建linux计时器 有时在我解除计时器并删除它之后调用回调。这会导致segfault,因为它试图访问已删除的资源C++ Linux定时器挂起信号,c++,c,linux,timer,C++,C,Linux,Timer,我正在使用timer\u create和SIGEV\u线程参数创建linux计时器 有时在我解除计时器并删除它之后调用回调。这会导致segfault,因为它试图访问已删除的资源 class timer_wrapper { private: std::function<void()> callback_; timer_t timer_; static void timer_callback(sigval_t val) { static_
class timer_wrapper
{
private:
std::function<void()> callback_;
timer_t timer_;
static void timer_callback(sigval_t val)
{
static_cast<timer_wrapper*>(val.sival_ptr)->callback_();
}
public:
timer_wrapper(std::function<void()> callback, uint32_t interval_sec)
: callback_(std::move(callback))
{
struct sigevent ev;
ev.sigev_notify = SIGEV_THREAD;
ev.sigev_signo = 0;
ev.sigev_value.sival_ptr = this;
ev.sigev_notify_function = &timer_wrapper::timer_callback;
ev.sigev_notify_attributes = 0;
timer_create(CLOCK_REALTIME, &ev, &timer_);
struct itimerspec spec = {{0, 0}, {interval_sec, 0}};
timer_settime(timer_, 0, &spec, nullptr);
}
~timer_wrapper()
{
timer_delete(timer_);
}
};
Linux手册上说
timer_delete()删除timerid中给定ID的计时器。如果
计时器在本次呼叫时已解除防护,在之前已解除防护
正在删除处理任何由
未指定已删除的计时器。
这基本上意味着,我不知道回调是否会被调用,在清理资源之前,我既无法取消回调,也无法强制传递挂起的信号
class timer_wrapper
{
private:
std::function<void()> callback_;
timer_t timer_;
static void timer_callback(sigval_t val)
{
static_cast<timer_wrapper*>(val.sival_ptr)->callback_();
}
public:
timer_wrapper(std::function<void()> callback, uint32_t interval_sec)
: callback_(std::move(callback))
{
struct sigevent ev;
ev.sigev_notify = SIGEV_THREAD;
ev.sigev_signo = 0;
ev.sigev_value.sival_ptr = this;
ev.sigev_notify_function = &timer_wrapper::timer_callback;
ev.sigev_notify_attributes = 0;
timer_create(CLOCK_REALTIME, &ev, &timer_);
struct itimerspec spec = {{0, 0}, {interval_sec, 0}};
timer_settime(timer_, 0, &spec, nullptr);
}
~timer_wrapper()
{
timer_delete(timer_);
}
};
类计时器包装器
{
私人:
std::函数回调;
定时器t定时器;
静态无效计时器回调(sigval\U t val)
{
静态_cast(val.sival_ptr)->回调_();
}
公众:
计时器包装(std::函数回调,uint32\u t间隔\u秒)
:回调(std::移动(回调))
{
结构sigev;
ev.sigev_notify=sigev_线程;
ev.sigev_signo=0;
ev.sigev_value.sival_ptr=该值;
ev.sigev_notify_函数=&timer_包装器::timer_回调;
ev.sigev_notify_属性=0;
计时器创建(时钟实时、ev和计时器);
struct itimerspec spec={{0,0},{interval_sec,0};
定时器设置时间(定时器、0和规格、空PTR);
}
~timer_wrapper()
{
定时器_删除(定时器_);
}
};
若计时器包装器超出范围,我预计回调将不再被调用,但它有时会被调用,根据man的说法,这是预期的行为
解决此问题的建议方法是什么?删除使用创建的计时器时,避免计时器生成事件的最简单方法是根本不避免它。相反,使用
volatile sig_atomic_t disarmed=0代码>标志,并让事件函数在执行任何其他操作之前测试标志,如果解除防护
为非零,则立即返回
通过这种方式,您首先设置了解除报警
,然后删除计时器
(使用原子内置会更好,要么是旧式的\uuuu sync\u fetch\u和+u add(&disarmed,0)
和\uu sync\u fetch\u和+u and(&disarmed,0)
,要么是原子加载(&disarmed,\uu atomic\u SEQ\u CST)
和\uu原子交换(&disarmed,0,\uu原子SEQ\CST)
,以访问该标志,以确保正确订购。)
对于SIGEV_信号
,您可以先阻止信号(使用),删除计时器,使用零超时检查计时器删除期间是否发出信号,最后恢复旧的信号掩码
(我个人使用一个POSIX实时信号(
SIGRTMIN+0
到SIGRTMAX-0
,在编译时定义),和一个在事件时间上键入的最小堆(每个堆槽包含时间和对自定义超时/事件结构的引用),用专用线程处理大量事件。)您是否正在检查sigev处理程序中资源的状态?也许您可以设置一个标志来检查正在删除的资源的状态?否,我如何检查?我有一个从sigval_t得到的指针,我不知道它是否被删除。这不容易:)我需要一个存储区来存储flag。如果我清理资源,我还需要清理flag的存储。我可以在回调中清理它,但我不知道它是否会被调用。所以要么是segfault,要么是内存泄漏?为什么不把你的东西发布到MCVE表单中呢?这很简单,但对于单实例计时器来说是可行的。如果我有多个实例,我应该在哪里存储这个原子标志?它应该是成员标志(然后我们遇到同样的问题,标志作为实例的一部分被删除),或者是一种带有原子标志的容器,需要互斥保护,这使得这种方法不那么简单。对于SIGEV_信号,我也可以检查挂起的信号,但我正在尝试为SIGEV_线程找到解决方案。我将考虑更改计时器以防这种情况不起作用。@ NoCuito:删除计时器(使用<代码> TimeReleDeleEe()/Cuff>)必须是一个单独的操作,它删除了C++中的一个代表定时器的对象。使用SIGEV_-THREAD
时,需要一个宽限期(CPU和挂钟时间),以确保C库在删除计时器之前没有创建新线程,但新线程没有获得运行时间。由于没有确定的方法关闭此竞态窗口,正确的解决方案是使用专用线程和SIGEV_信号
(信号在所有其他线程中被阻止)。@incognito:注意,您可以使用自己的专用线程处理SIGEV_信号
轻松地“模拟”SIGEV_线程
的行为,完全避免比赛窗口。专用计时器线程的唯一问题/困难是将“新计时器”和“取消计时器”处理与超时结合起来。实际上,在请求互斥锁和条件变量上使用pthread\u cond\u timedwait()
(否则将休眠到下一个超时)而不是一个信号,往往更为健壮。模拟可能可行,但它似乎比标准解决方案更为繁重。无论如何,谢谢。我期待着这个标准的解决方案,但似乎没有解决方案,只有黑客。