Problem: I have timer(s) running, upon expiration of timer(s) certain function needs to be invoked. Output: There is a segfault inside Hndlr() function
As per man page of sigevent, it says,
SIGEV_THREAD – Notify the process by invoking sigev_notify_function “as if” it were the start function of a new thread. (Among the implement‐ tation possibilities here are that each timer notification could result in the creation of a new thread, or that a single thread is created to receive all notifications.)
The function (sigev_notify_function) is invoked with sigev_value as its sole argument
I did refer to this: UNIX/Linux signal handling: SIGEV_THREAD and it says,
sigev_value contains supplementary data that is passed to the function
So, I have written the following,
typedef struct Info { enum Status { Start = 1, Expire = 2 } TimerStatus; int data; timer_t timerId; } Info_t; void Hndlr(union sigval *sv) { //Upon expiry I want to set this value of t1.TimerStatus Expire: //t1.TimerStatus = Expire; //So I have done this: sv->sival_int = Expire; } int TimerInit(Info_t *Type) { struct sigevent sev; sev.sigev_notify = SIGEV_THREAD; sev.sigev_value.sival_ptr = Type->timerId; sev.sigev_value.sival_int = Type->TimerStatus; sev.sigev_notify_function = &Hndlr; sev.sigev_notify_attributes = 0; timer_create(CLOCK_REALTIME, &sev, &(Type->timerId)); } //other code int main(int argc, char const *argv[]) { Info_t t1; t1.TimerStatus = Start; TimerInit(&t1); //start timer //other code while (1) { if (t1.TimerStatus == Expire) { //do something, invoke a function } } return 0;
}
Warning: assignment to ‘void (*)(__sigval_t)’ {aka ‘void (*)(union sigval)’} from incompatible pointer type ‘void (*)(union sigval *)’ [-Wincompatible-pointer-types] sev.sigev_notify_function = &Hndlr;
Since, I am using union sigval *sv in Hndlr, I am receiving this warning.
Q) How to I pass enum type to Hndlr as pass-by-ptr and change it, ie., t1.TimerStatus = Expire
PS: I haven’t included the entire code involving timer_set() etc, and it also involves multiple instances of timer. So, How can I achieve this functionality (Q) ?
Advertisement
Answer
A few mistakes in the code:
- Wrong function prototype for timer expiry function
Hndlr
. - Setting all members of
union sigval
, whereas only one member must be set. - A variable modified and read in another thread must be atomic.
A working example (compiler options -std=c11 -pthread -W{all,extra}
, linker options -std=c11 -pthread -lrt
):
#include <stdatomic.h> #include <stdlib.h> #include <signal.h> #include <time.h> enum Status { Start = 1, Expire = 2 }; typedef struct { atomic_int status; int data; timer_t timerId; } Info_t; static void Hndlr(union sigval sigev_value) { Info_t* info = sigev_value.sival_ptr; atomic_store(&info->status, Expire); } void TimerInit(Info_t* info, unsigned seconds) { int r; struct sigevent sev; struct itimerspec its; sev.sigev_notify = SIGEV_THREAD; sev.sigev_value.sival_ptr = info; sev.sigev_notify_function = &Hndlr; sev.sigev_notify_attributes = 0; r = timer_create(CLOCK_REALTIME, &sev, &info->timerId); if(r) abort(); its.it_interval.tv_sec = 0; its.it_interval.tv_nsec = 0; its.it_value.tv_sec = seconds; its.it_value.tv_nsec = 0; r = timer_settime(info->timerId, 0, &its, NULL); if(r) abort(); } int main() { Info_t t1; t1.status = Start; TimerInit(&t1, 3); while(atomic_load(&t1.status) != Expire) ; return 0; }
In this particular usage, when the timer callback function just stores into a variable, there is no need to use another thread with SIGEV_THREAD
, SIGEV_SIGNAL
would work just as well (setup code changes are required), as long as blocking functions that can be interrupted with the signal handle EINTR
.