Reputation: 457
I am creating linux timer using timer_create with SIGEV_THREAD parameter.
Sometimes callback is called after I disarm timer and delete it. This causes segfault, because it tries to access deleted resources.
http://man7.org/linux/man-pages/man2/timer_delete.2.html
Linux manual says
timer_delete() deletes the timer whose ID is given in timerid. If the timer was armed at the time of this call, it is disarmed before being deleted. The treatment of any pending signal generated by the deleted timer is unspecified.
That basically means that I don't really know whether the callback will be called or not and I don't have a way neither to cancel it nor to force pending signal to deliver before I clean up the resources.
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_);
}
};
If timer_wrapper goes out of scope I expect callback will not get called anymore, however it's called sometimes and according to man this is expected behavior.
What is the suggested way to resolve this issue?
Upvotes: 5
Views: 1390
Reputation: 39346
The simplest approach to avoiding the event generated by a timer when deleting a timer created using timer_create()
, is to not avoid it at all. Instead, use a volatile sig_atomic_t disarmed = 0;
flag, and have the event function test the flag before it does anything else, and return immediately if disarmed
is nonzero.
This way, you first set disarmed
, then delete the timer.
(It would be even better to use atomic built-ins, either old-style __sync_fetch_and_add(&disarmed, 0)
and __sync_fetch_and_and(&disarmed, 0)
, or __atomic_load_n(&disarmed, __ATOMIC_SEQ_CST)
and __atomic_exchange_n(&disarmed, 0, __ATOMIC_SEQ_CST)
, to access the flag instead, to ensure the proper ordering.)
For SIGEV_SIGNAL
, you can block the signal first (using pthread_sigmask()
), delete the timer, check if the signal was raised during timer deletion using sigtimedwait()
with a zero timeout, and finally reinstate the old signal mask.
(I personally use a single POSIX realtime signal (SIGRTMIN+0
to SIGRTMAX-0
, defined at compile time), and a min-heap keyed on the event time (with each heap slot containing the time and a reference to the custom timeout/event structure), to handle a large number of events, with a dedicated thread.)
Upvotes: 2