Here is my problem:
I'm using FreeRTOS with the Cortex-M3 MCU. I have an ISR called EXTI15_10, which
is trigger during a rising edge on the pins EXTI_Line10, EXTI_Line11..etc.
Within the ISR I set the variable "status" to some specified value and before leaving the ISR
I put the variable status in a queue and send it to my thread. That works fine.
The thread is periodically called and calls the function
xQueuePeek() to get the item from the queue. Note xQueuePeek()
gets the item from the queue without removing it from the queue.
This works fine too, but I only receive the correct value once.
That means, the interrupt is generated, status is set to a value and put in the
queue, threads reads the correct value. But all the next interrupts
set status correctly(I checked it before putting the item in the queue), but my
thread reads always the old value. Do I have here a problem with compiler
optimization and volatile? xQueueSendFromISR(), expects as second argument
a const void * and not volatile void *. What I'm doing wrong here?
The ISR:
void EXTI15_10_IRQHandler()
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
int status;
if (EXTI_GetITStatus(EXTI_Line10))
status = 100;
else if (EXTI_GetITStatus(EXTI_Line11))
status = 200
else if (EXTI_GetITStatus(EXTI_Line12))
status = 300;
else if (EXTI_GetITStatus(EXTI_Line13))
status = 400;
else if (EXTI_GetITStatus(EXTI_Line14))
status = 500;
else if (EXTI_GetITStatus(EXTI_Line15))
status = 600;
// Clear the pending interrupt bits
EXTI_ClearITPendingBit(EXTI_Lines15_10);
xQueueSendFromISR(queue, &status, &xHigherPriorityTaskWoken);
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
The thread:
static void thread_xy()
{
portTickType xLastExecutionTime;
xLastExecutionTime = xTaskGetTickCount();
int status_from_queue = 0;
for (;;) {
xLastExecutionTime = xTaskGetTickCount();
vTaskDelayUntil(&xLastExecutionTime, CHECK_AND_ENABLE_LEDS_DELAY);
if (queue != 0) {
if (xQueuePeek(queue, &status_from_queue, portMAX_DELAY))
// Received item from queue...
print("Status from queue = %d\n", status_from_queue);
}
}
}
If you're only ever peeking at the queue then, yes, you'll always get the same value, simply because you're leaving it there. A queue is a first-in-first-out data structure but, in order to get at the second value in the queue, you generally have to remove the first.
Consider the following sequence:
initial state queue = { }
interrupt adds 7 queue = { 7 }
application peeks (7) queue = { 7 }
interrupt adds 42 queue = { 7, 42 }
application peeks (7) queue = { 7, 42 }
interrupt adds 314159 queue = { 7, 42, 314159 }
application peeks (7) queue = { 7, 42, 314159 }
If you want to extract a value from the queue and use it, you need to get it instead of peeking at it. Without that, the queue will also eventually fill up.
I should also mention that there may be a race condition here that may well cause you problems. If your application is halfway to modifying the queue (by getting a value out of it) when an interrupt arrives, you're going to end up in all sorts of troubles since the queue will not be in a consistent state.
You need a way to stop (or, better, delay) the interrupt while the queue may be in an inconsistent state. That may be as simple as disabling interrupts while your application is modifying it (or it may require far more complex code if, for example, you're on a multi-processor system).
Of course, the xQueuePeek() (and other associated) code may already do that since you have an xQueueSendFromISR() which seems to indicate that such protection would already be part of the queueing implementation. I'm just raising that as something to watch out for.
Related
i have to write a function that, by calling it only a single time, have to:
turn on an output pin
the pin stays high for 200mS
at the end of the timer the pin need to be low again.
the pin stays low for 200mS
at the end of the timer the function can be called again.
to turn on and off an output pin I already have wrote and tested the funcions:
outOn(pin_id);
outOff(pin_id);
now, i am trying to write the function that does the above mentioned actions and this is what l've come out with so far:
void outOnT02(enum e_outs ou){
outOn(ou);
gu_RegTim.BTime[BTIM_FUNCT].Timer = O_SEC01*2;
if(gu_RegTim.BTime[BTIM_FUNCT].b.Stato == O_EndTimer) {
outOff(ou);
}
}
the function is named outOnT02 because:
it is an output;
after calling it, the pin became high;
T02 because the pin stays high for 0.2 Seconds.
outOn(ou); makes the pin go high,
outOff(ou); makes the pin go low,
gu_RegTim.BTime[BTIM_FUNCT].Timer = O_SEC01*2;
starts a 200mS timer,
and gu_RegTim.BTime[BTIM_FUNCT].b.Stato == O_EndTimer is true when the timer has run out.
it works but, as you can tell, I have to put it in a cycle otherwise gu_RegTim.BTime[BTIM_FUNCT].b.Stato == O_EndTimer will never be true and so,the pin will stay high forever.
this is where i am stuck. i can't use a SLEEP(200); because i can't interrupt the execution of the code.
the language is C, the ide is MPLAB X IDE v6.00, the compiler is XC8 v2.31 and the cpu is a PIC16F15355.
This post is a little old but it is worth to answer since it is both a good question and a common problem. Now this problem is very common in embedded world when we develop applications that has to run on only one CPU. Hence there is no real parallelism in the workflow. Also since the application will not run on top of any OS, there will be no scheduler, no timers, no threads etc. Especially in small scaled microcontrollers there is no way to run many of the true RTOSs.
But this shouldn't be an obstacle for developing applications that runs tasks concurrently. We can develop an application using some tricks so that it runs the tasks concurrently and behave as a small OS. Running concurrently means that no task blocks the CPU using busy waiting checks or something alike but we block a task that needs to wait some event to occur.
When we block a task, the specific data and the next execution point on that task must be preserved so that it can continue from where it should in the next execution. Knowing what we need to preserve helps us to create a thread-like structures that executes until it has to wait some event to occur (eg. time delay). When it has to wait (means that it will be blocked) the next state of it must be preserved and it exits to give the control to the CPU so that it executes other tasks.
When we need to deal with periodic tasks as in the question, it is relatively easier to implement without blocking the CPU execution and meanwhile handle other tasks. Moreover no interrupt usage needed for this type of tasks unless the tasks are extremely time sensitive.
Well, enough with the story part, let's get into it. I will base the examples on the OP's output flashing problem. However the same techniques can be applied for other situations like I/O events, hardware events etc.
Let's sum up the requirement briefly, we have a task that runs atomically. That is, when it is called it must run to completion so that it can be called again (this is what I understand from the OP's requirement):
Turns on an output pin for 200ms
Then turns off the pin for 200ms
Once turned off and 200ms time has elapsed it can be executed again.
Note Some functions in this example are not implemented since they can be application or microcontroller specific.
Task-like Functions
Let's assume we want to schedule the following two task-like functions each of which keeps track of its execution continuation points.
The static cp variables are declared in each function so that they remember where to continue whenever they are called. The content of cp variable will not be destroyed by the compiler when the function returns since we declare it as static. The cp needs to be updated upon the expected events occur in order to proceed to the next step whenever it is called.
Note that in outputTask, the call source must be known to control its atomic behaviour. Since the requirement for this task is that once it triggered or called, it must run to completion. So we have to know where the task is called from, in order it to decide what to do on each call. If it has been triggered from another task, it can't be triggered anymore until it completes its flashing prosess. If it is called from the scheduler (main loop) it knows it is a periodic call and will keep track of the time. This control is achieved using a parameter called periodic. When it is called from the scheduler this parameter must be set to 1, and 0 for the calls other than the scheduler.
/*
* This task-like function performs what the OP wants to achieve
*/
void outputTask(unsigned char periodic) {
static unsigned char cp = 0; // Continuation Point holder
static unsigned char currentMillis;
/*
* Check whether it is a periodic call or a new output signal call.
* If it is a periodic call and signalling has been initialized,
* proceed for time keeping.
* If it is a new signalling call and the task hasn't completed yet,
* simply ignore and return.
*/
if(!periodic && cp != 0) {
return;
}
switch(cp) {
case 0:
outOn(pin_id); // Turn on the output
cp = 1; // Next execution point
currentMillis = 200; // Load the 200ms counter for time keeping
break;
case 1:
currentMillis--;
if(currentMillis == 0) {
// 200ms time for output high has elapsed, proceed to next step
outOff(pin_id); // Turn off the output
currentMillis = 200; // Reload the counter value
cp = 2; // Proceed to the next step
}
break;
case 2:
currentMillis--;
if(currentMillis == 0) {
// 200ms time for output low has elapsed, proceed to next step
cp = 0; // Last step is done, reset the state for new calls
}
break;
default:
// For anything else, reset the task state to the initials
cp = 0 // Reset the task state to zero so that it accepts new calls
}
}
/*
* Let's say this task will wait for a button press event and will
* trigger the outputTask upon the event occurs
*/
void outputTriggerTask() {
static unsigned char cp = 0;
static unsigned char currentMillis;
switch(cp) {
case 0:
if(isButtonPressed()) { // Platform specific function
// A button press has been detected, debounce first
currentMillis = 50;
cp = 1; // Next step, check for the elapsed time
}
else {
break;
}
case 1:
currentMillis--;
if(currentMillis == 0) {
// Check whether the button press is consistent
if(isButtonPressed()) {
// Yes still consistent, handle the button press by triggering the output task
outputTask(0); // Not a periodic call
cp = 2; // Next step is to check whether button is released
}
else {
cp = 0; // Reset the task state
}
}
break;
case 2:
if(isButtonReleased()) { // Platform specific function
currentMillis = 50; // Reload the time counter
cp = 3;
}
else {
break;
}
case 3:
currentMillis--;
if(currentMillis == 0) {
// Check whether the button release is consistent
if(isButtonReleased()) {
// Yes still consistent, handle the button release if needed
cp = 0; // Reset the task to its initial state
}
}
break;
default:
cp = 0; // Reset to initials
}
}
Scheduling Approches
The following approches are for non RTOS small embedded systems. They are suitable for wide range of 8-bit microcontrollers.
Approach 1 - Create Delay Based Timebase to Schedule Tasks
Scheduling using CPU blocking delay is suitable for hobby and educational purposes while it is not suitable for real projects. This example uses a platform specific delay_ms function (or can be a macro) to create a 1ms heartbeat for the application so that the tasks can keep track of time.
void main(void) {
systemInit(); // Platform specific function
// maybe some more init functions go here
// Application's infinite scheduler loop
while(1) {
// The first thing we do is to create a 1ms timebase using delay.
// This is the heartbeat for the application
delay_ms(1000); // Platform specific function
// 1ms has elapsed check the tasks
outputTriggerTask(); // Check whether any button press event has occured
outputTask(1); // It is a periodic call for the output task
// Maybe more tasks go here...
}
}
Approach 2 - Create Hardware Timer Based Timebase
void main(void) {
systemInit(); // Platform specific function
// Setup a hardware timer for 1ms overflow without interrupt
initTimerForOneMs(); // Platform specific function
// maybe some more init functions go here
// Application's infinite scheduler loop
while(1) {
// Wait for the timer to overflow
while(!isTimerOverflow()) // Platform specific function
;
// Timer has overflowed, reload and check tasks
reloadTimer(); // Platform specific function
// 1ms has elapsed check the tasks
outputTriggerTask(); // Check whether any button press event has occured
outputTask(1); // It is a periodic call for the output task
// Maybe more tasks go here...
}
}
Approach 3 Put the Processor to Sleep for 1ms Timebase
void main(void) {
systemInit(); // Platform specific function
// maybe some more init functions go here
// Application's infinite scheduler loop
while(1) {
// Put the Processor to sleep along with a watchdog timer to wake it up
clearWatchdogTimer(); // Platform specific function
sleep(); // Platform specific function
// CPU slept for 1ms and woke up, handle the periodic tasks
outputTriggerTask(); // Check whether any button press event has occured
clearWatchdogTimer(); // Platform specific function
outputTask(1); // It is a periodic call for the output task
clearWatchdogTimer(); // Platform specific function
// Maybe more tasks go here...
}
}
And Last But not Least Time Checking Approach
In this approach the tasks will be keeping the time by checking better say comparing the elapsed time to the desired time to delay tasks without blocking the CPU. For this, we will need to use a free running timer. This will be like the millis function of the Arduino API.
Rewriting the Tasks for the Time Checking Approach
/*
* This task-like function performs what the OP wants to achieve
*/
void outputTask(unsigned char periodic) {
static unsigned char cp = 0; // Continuation Point holder
static unsigned short currentMillis; // 16 bit millisecond holder
/*
* Check whether it is a periodic call or a new output signal call.
* If it is a periodic call and signalling has been initialized,
* proceed for time keeping.
* If it is a new signalling call and the task hasn't completed yet,
* simply ignore and return.
*/
if(!periodic && cp != 0) {
return;
}
switch(cp) {
case 0:
outOn(pin_id); // Turn on the output
cp = 1; // Next execution point
currentMillis = getCurrentMillis(); // Platform specific function
break;
case 1:
if(getCurrentMillis() - currentMillis >= 200) {
// 200ms time for output high has elapsed, proceed to next step
outOff(pin_id); // Turn off the output
currentMillis = getCurrentMillis(); // Reload the counter value
cp = 2; // Proceed to the next step
}
break;
case 2:
if(getCurrentMillis() - currentMillis >= 200) {
// 200ms time for output low has elapsed, proceed to next step
cp = 0; // Last step is done, reset the state for new calls
}
break;
default:
// For anything else, reset the task state to the initials
cp = 0 // Reset the task state to zero so that it accepts new calls
}
}
/*
* Let's say this task will wait for a button press event and will
* trigger the outputTask upon the event occurs
*/
void outputTriggerTask() {
static unsigned char cp = 0;
static unsigned short currentMillis;
switch(cp) {
case 0:
if(isButtonPressed()) { // Platform specific function
// A button press has been detected, debounce first
currentMillis = getCurrentMillis(); // Platform specific function
cp = 1; // Next step, check for the elapsed time
}
else {
break;
}
case 1:
if(getCurrentMillis() - currentMillis >= 50) {
// Check whether the button press is consistent
if(isButtonPressed()) {
// Yes still consistent, handle the button press by triggering the output task
outputTask(0); // Not a periodic call
cp = 2; // Next step is to check whether button is released
}
else {
cp = 0; // Reset the task state
}
}
break;
case 2:
if(isButtonReleased()) { // Platform specific function
currentMillis = getCurrentMillis();
cp = 3;
}
else {
break;
}
case 3:
if(getCurrentMillis() - currentMillis >= 50) {
// Check whether the button release is consistent
if(isButtonReleased()) {
// Yes still consistent, handle the button release if needed
cp = 0; // Reset the task to its initial state
}
}
break;
default:
cp = 0; // Reset to initials
}
}
Scheduler for Time Checking Approach
void main(void) {
systemInit(); // Platform specific function
initMillisTimerWithInterrupt(); // Platform specific function
// maybe some more init functions go here
// Application's infinite scheduler loop
while(1) {
// Now that we use a free running millis timer no need to block the CPU to create a timebase
// Just call tasks sequentially. Each task will know what to do individually
outputTriggerTask(); // Check whether any button press event has occured
outputTask(1); // It is a periodic call for the output task
// Maybe more tasks go here...
}
}
Consider this code:
struct sembuf s_op[2];
s_op[0].sem_num = old;
s_op[0].sem_op = 1;
s_op[0].sem_flg = 0;
s_op[1].sem_num = new;
s_op[1].sem_op = -1;
s_op[1].sem_flg = 0;
semop(semid, s_op, 2);
Now, if "new" cannot be decremented immediately (because it is already at 0), will there be a time interval in which the "old" semaphore has been increased and the "new" one has not?
I mean, can this always happen atomically?
Here are some implementation details in Linux 5.4. The source code managing the sysV semaphores is in ipc/sem.c.
The semop() system call entry point calls the common internal do_semtimedop() function with the timeout parameter set to NULL:
SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops,
unsigned, nsops)
{
return do_semtimedop(semid, tsops, nsops, NULL);
}
After some checks on the parameters and permissions, loosely speaking, do_semtimedop() locks the whole semaphore set and calls another internal routine named perform_atomic_semop() which makes the requested "sem operations". This function scans the semaphore set twice:
First scan loop determines if any of the operation leads to a negative value for the semaphores in the set. If yes, it returns -EAGAIN if IPC_NOWAIT is set in the flags or 1 if it is not set. It may also return -ERANGE if one of the operations results in a value bigger than the maximum (defined as SEMVMX = 32767 in include/uapi/linux/sem.h).
Second scan loop makes the requested operations as the first loop determined that this will not result into negative values for the semaphores. 0 is returned.
static int perform_atomic_semop(struct sem_array *sma, struct sem_queue *q)
[...]
/*
* We scan the semaphore set twice, first to ensure that the entire
* operation can succeed, therefore avoiding any pointless writes
* to shared memory and having to undo such changes in order to block
* until the operations can go through.
*/
for (sop = sops; sop < sops + nsops; sop++) {
int idx = array_index_nospec(sop->sem_num, sma->sem_nsems);
curr = &sma->sems[idx];
sem_op = sop->sem_op;
result = curr->semval;
if (!sem_op && result)
goto would_block; /* wait-for-zero */
result += sem_op;
if (result < 0)
goto would_block;
if (result > SEMVMX)
return -ERANGE;
if (sop->sem_flg & SEM_UNDO) {
int undo = un->semadj[sop->sem_num] - sem_op;
/* Exceeding the undo range is an error. */
if (undo < (-SEMAEM - 1) || undo > SEMAEM)
return -ERANGE;
}
}
for (sop = sops; sop < sops + nsops; sop++) {
curr = &sma->sems[sop->sem_num];
sem_op = sop->sem_op;
result = curr->semval;
if (sop->sem_flg & SEM_UNDO) {
int undo = un->semadj[sop->sem_num] - sem_op;
un->semadj[sop->sem_num] = undo;
}
curr->semval += sem_op;
ipc_update_pid(&curr->sempid, q->pid);
}
return 0;
would_block:
q->blocking = sop;
return sop->sem_flg & IPC_NOWAIT ? -EAGAIN : 1;
}
Back in do_semtimedop(), the return from perform_atomic_semop() is checked:
If the returned value is 0, any waiting tasks on the semaphore set are woken up to run their pending operations before being woken up (i.e. do_semtimedop() is recalled for each waiting task). The global lock is released and 0 is returned
If the return value is negative (-EAGAIN or any other detected error like -ERANGE), the global lock is released and the errno is returned
If the return value is > 0 (actually 1), the calling task is put to sleep: it is added in the wait queue of the semaphore set. The call is blocked until the task is woken up.
So, to answer the question, there is no intermediate time period where one semaphore operation is done while the other is pending on the same set. There are all done at once or the calling task is put to sleep until it can do it.
will there be a time interval in which the "old" semaphore has been increased and the "new" one has not?
This is an implementation detail. Implementation may check if the operation is possible, then do the operations, or do operations one after another and if one operation cannot be done revert the operations already done. Or implementation may not do anything at all, up until the next sem* operation and query the operations then, etc. The bottom part is that either way only the observable state is what matters, and such state where one has increased and the other not cannot be observed by any process, as the operation is done "atomically".
can this always happen atomically?
Yes, where "atomically" means that no process can observe the state in the middle of operation. It doesn't mean that such state "doesn't exists", as the kernel has to query one operation after another. It means that such state can't be observed from a process that uses POSIX api.
I'm trying to set up a hardware interrupt handler in protected mode, using djgpp-2 for compiling in dosbox-0.74. Here's the smallest code possible (timer interrupt), I guess:
#include <dpmi.h>
#include <go32.h>
#include <stdio.h>
unsigned int counter = 0;
void handler(void) {
++counter;
}
void endHandler(void) {}
int main(void) {
_go32_dpmi_seginfo oldInfo, newInfo;
_go32_dpmi_lock_data(&counter, sizeof(counter));
_go32_dpmi_lock_code(handler, endHandler - handler);
_go32_dpmi_get_protected_mode_interrupt_vector(8, &oldInfo);
newInfo.pm_offset = (int) handler;
newInfo.pm_selector = _go32_my_cs();
_go32_dpmi_allocate_iret_wrapper(&newInfo);
_go32_dpmi_set_protected_mode_interrupt_vector(8, &newInfo);
while (counter < 3) {
printf("%u\n", counter);
}
_go32_dpmi_set_protected_mode_interrupt_vector(8, &oldInfo);
_go32_dpmi_free_iret_wrapper(&newInfo);
return 0;
}
Note that I'm not chaining my handler but replacing it. The counter won't increase beyond 1 (therefore never stopping the main loop) making me guess that the handler doesn't return correctly or is called only once. Chaining on the other hand works fine (remove the wrapper-lines and replace set_protected_mode with chain_protected_mode).
Am I missing a line?
You need to chain the old interrupt handler, like in the example Jonathon Reinhart linked to in the documentation, as the old handler will tell the interrupt controller to stop asserting the interrupt. It will also have the added benefit of keeping the BIOS clock ticking, so it doesn't lose a few seconds each time you run the program. Otherwise when your interrupt handler returns the CPU will immediately call the handler again and your program will get stuck in an infinite loop.
Also there's no guarantee that GCC will place endHandler after handler. I'd recommend just simply locking both the page handler starts on and the next page in case it straddles a page:
_go32_dpmi_lock_code((void *) handler, 4096);
Note the cast is required here, as there's no automatic conversion from pointer to a function types to pointer to void.
I have a handler for SysTick exception which counts ticks and calls other functions (f1, f2, f3) whose execution time can be longer than SysTick period. These functions set and clear their active status (global variables) so if a SysTick exception occurs it can detect an overload and return to interrupted function.
I have assigned fixed priority to SysTick exception (let's say 16). I want to somehow make possible for SysTick to generate an exception regardless of it's prior active status, go to SysTickHandler, increase tick counter and return to interrupted function.
One solution which may be useful is to use BASEPRI. It can be set to priority lower than SysTick so it would enable that exception. Unfortunately, using BASEPRI got me nowhere because nothing happened (I set it to max value). BASEPRI value was 0 inside SysTickHandler before I changed it. Should that value be equal to SysTick priority when processor enters handler function? Is exception priority loaded automatically in BASEPRI?
I have also considered for NVIC to have an issue with preempting already active exception but found nothing regarding that in ARM documentation.
Also, return from handler when oveload is detected could set the processor state to thread mode. Let's ignore that for now.
void SysTickHandler(void) {
ticks++;
//set_BASEPRI(max_value);
if (f1_act || f2_act || f3_act) return;
else {
f1();
f2();
f3();
}
}
A simpler example for this problem (without return) would be to increase tick counter when having an infinite loop inside handler.
void SysTickHandler(void) {
ticks++;
set_BASEPRI(max_value);
while(1);
}
If the interrupt becomes pending while its handler is already running, the handler will run to completion and immediately re-enter. Your tick will be aperiodic, and if the functions consistently take longer that one tick period, you may never leave the interrupt context.
It may be possible I suppose to increase the priority of the interrupt in the handler so that it will preempt itself, but even if that were to work, I would hesitate to recommend it.
It sounds that what you actually need is an RTOS.
Sorry to disappoint you, but it seems a overall design problem to me...
Why won't you just set some flag in SysTick and read it somewhere else?
Like:
#include <stdbool.h>
volatile bool flag = false;
//Consider any form of atomicity here
//atomic_bool or LDREX/STREX instructions here. Bitbanding will also work
void sysTickHandler(void) {
ticks++;
if (f1_act || f2_act || f3_act) return;
else {
flag = true; //or increment some counter if you want to keep track of the amount of executions
}
And somewhere else:
int main() {
// some init code
//main loop
for(;;) {
foo();//do sth
bar(x); //do sth else
if (flag) {
f1();
f2();
f3();
flag = false;
}
}
}
Or if we assume that every interrupt wakes the microcontroller and power-down mode is needed, then sth. like this might work:
if (flag) {
f1();
f2();
f3();
flag = false;
}
goToSleep(powerDownModeX); //whatever;
I need to be able to start multiple timers simultaneously and know specifically if a timer has stopped or is still going.
#define RESEND_TIMEOUT 5
void timerCreate();
void timer_start(timer_t * timer, uint32 timeout);
bool timer_complete(timer_t * timer);
int main() {
timer_t resend_timer = timerCreate();
timer_start(&resend_timer, RESEND_TIMEOUT);
while(1) {
if (timer_complete(&resend_timer))
break;
}
}
void timer_start(timer_t * timer, uint32_t timeout)
{
printf("timer starting\n");
struct itimerspec it_val;
it_val.it_value.tv_sec = timeout;
it_val.it_value.tv_nsec = 0;
// timer expires once
it_val.it_interval.tv_sec = 0;
it_val.it_interval.tv_nsec = 0;
if (timer_settime(*timer, 0, &it_val, NULL) == -1) {
errExit("Could not set timeout");
}
}
// return true if timer ended
bool timer_complete(timer_t * timer)
{
if(timer_getoverrun(*timer) == 0)
return false;
else
return true;
}
I never break out of the loop. Why can't I get the overrun of the timer (it always returns 0, which means the timer has not passed its expiration)? Yet when I add a signal handler, I know that the timer expires.
I want to try timer_gettime(timer_t timerid, struct itimerspec *curr_value) inside of my timer_complete function to see if the remaining time is 0, but how can I pass the curr_value argument without having a global variable?
Last but not least, I have tried with the TIMER_ABSTIME flag when arming the timer with timer_settime. From the manpage of int timer_settime(timer_t timerid, int flags,
const struct itimerspec *new_value,
struct itimerspec * old_value):
By default, the initial expiration time specified in
new_value->it_value is interpreted relative to the current time on
the timer's clock at the time of the call. This can be modified by
specifying TIMER_ABSTIME in flags, in which case new_value->it_value
is interpreted as an absolute value as measured on the timer's clock;
that is, the timer will expire when the clock value reaches the value
specified by new_value->it_value. If the specified absolute time has
already passed, then the timer expires immediately, and the overrun
count (see timer_getoverrun(2)) will be set correctly.
I never break out of the loop. Why can't I get the overrun of the timer (it always returns 0, which means the timer has not passed its expiration)?
No, it means you had no overruns.
The OS is not going to queue timer signals even if you specify realtime signals. Overun tells you how many signals would have been queued if the wood chuck didn't chuck signals.
So consider you set a timer to go off once every second. But for some reason you didn't handle the signal. Say you had it blocked for 5 seconds. The overrun count is going to be 4 - the signal you will/are processing and the 4 you missed.
In your case you set a one-time timer to go off after "timeout" seconds. The signal was delivered. There will be no more signals hence overrun is always going to be 0, as it should be.