Is there a way to change the timer callback function?

Hi all,
I am using timers and wonder if there is a way to change a timers callback function. I have a lot of small jobs to be done on a timer event but only one at a time and which is predicted before the timer is started. So I think about making a callback for each purpose and make the timer point to that function, so it is called when the timer fires.
I know that I could create a seperate timer for each purpose but as I want everything static this is not an option. Also I could use timer ID and a switch inside one big timer callback function but I think the other option is more elegant and faster.
Any ideas?

Thanks a lot

Martin

There is currently no API to change the callback, although it would be simple to add one should you wish to create a PR with an implementation :wink:

Hi Richard,

thanks for the quick reply.

What is a PR and how is the process going on?

~WRD0000.jpg

Hi Martin,

it would be easy to implement a dynamic timer callback by using a dispatcher function as your callback. Be aware of race condiotions, though… iow, what if your callback changes at the same time your timer expires?

Hi RAc,

how would this dispatcher work? The timer callback function calls a function pointer which I can change exterrnally? Did you mean so? Would at least be a workaround.

Thanks for this hint

Martin

~WRD0000.jpg

Inside your generic callback function e.g. a state variable can be used to dispatch (e.g. switch/case) to different actions/functions. As RAc already mentioned setting e.g. the state or timer action variable must be done atomically to avoid race conditions.
This might not be needed if you’re using a single shot timer rearmed in the (dispatched) callback function. Means setting the next state/timer action and restart the single-shot timer is race free and doesn’t need extra protection.

Yes, that is exactly what I want. As I wrote I am aware of the timerID + switch option. But I think seperate functions would be easier, clearer and faster.

~WRD0000.jpg

Sure, the dispatch method adds 1 indirection. So feel free to add the timer feature to change the callback function and create a pull-request (PR) that it get’s merged into FreeRTOS mainline :+1:

Ok, yes, I would like to try it. I already looked at the StaticTimer_t struct and found this comment with it:

…in the hope users will recognise that it would be unwise to make direct use of the structure members

I need a little help on this, how does this hiding policy work and how should I access these structure members. Where can I read more on this?

I would start with a function declaration like this:

void vTimerSetCallbackFunction (TimerHandle_t xTimer, TimerCallbackFunction_t pxnewCallbackFunction)

I need to know how to get the pointer to the StaticTimer_t struct from the TimerHandle (and to make it complete how to do it with dynamic timers) and how to access the callback function member.

Thanks for any help

Martin

~WRD0000.jpg

Sorry, I need to raise a DR (disagreement request) on this one.

A generic API would necessarily need to address the race condition mentioned above, meaning what happens if a timer expiration and callback replacement overlap? There would need to be an extra parameter to the replacement API call reading something like “unconditionally|deferred to next expiration|within time window” which would be a nightmare to support.

Efficiency is not an issue in my opinion. Using branch tables and compiler optimizations such as tail recursive call elimination, the overhead of using dispatchers can on some platforms reduce to a very few cycles. Given that timers are not ISRs, we are not talking the need of eliminating indiviual CPU cycles for efficiency.

I’d vote for a KB code sample instead of an API to be officially supplied. There are way too many different and contradicting possible use cases to make an official API feasible. There is no “cleaner, faster and easier” in native support in this case.

Hi RAc,

ok I agree that making a new API function needs to consider all possible use cases which can be a lot of work.

Without knowing the details I have asked above I currently don’t see what kind of danger / race condition you see for this special way. My current point of view is this: the final switching of the callback function is setting a new value to a function pointer which is atomic because it is a 32 bit value (just setting, no read-modify-write), so either of the two callback functions will be called in case the timer fires while the API function runs, the old or the new one. This has to be taken into account anyway otherwise you have to stop the timer at a safe code part before changing the callback. The same applies when you do it via TimerID or an extra function pointer or whatever other way, you never know when the timer fires and if you can’t make the change in an atomic way you need a semaphore or so. Please correct me if I am wrong or have some misunderstanding, I am not into deep details of FreeRTOS.

And maybe you are right that over all it is a bit of an overkill to go for this option while there are two other options already available.

I am curious to read your opinion.

Martin

~WRD0000.jpg

Hi Martin,

the danger is not in terms of data or pointer corruption (I’ll agree that there are ways to safely eliminate these) but in terms of predictability of the relationship between setting the callback and invoking it.

Assume that the timer is set to fire in 500 ms intervals, and in a given use case, the callback is replaced in, say, 1,4 sec intervals starting from a given expiration. The user would normally expect the current expiration callback to execute twice before the new callback is invoked. However, depending on a lot of factors (such as the relative priorities of the timer task and the taks doing the replacement and other conditions), there may be constellations in which a replacement can be deferred such that the current callback will be invoked three instead of two times. This is even more evident in scenarios in which the replacement is completly aysnchronous.

Other issues are raised in regard to issues such as what happens if two or more threads attempt to concurrently replace a callback? Extrapolating from the diligency with which certain users have recently explored all kinds of adventurous architectures in detail, I wouldn’t consider any of those scenarios as exotic.

Given that there are many easy ways to address these issues on the application level (aside from the dispatcher, you could for example register serveral timers only one of which is active at any given time), I don’t see a merit of trying to make an API for this fringe and error prone kind of thing.

I wouldn’t use a semaphore, but a critical section to update the pointer, as the operation is ‘light weight’ enough, you don’t need the overhead of a semaphore. (And only IF you need one, I can’t think of a supported platform where a function pointer change isn’t atomic)

Yes, there is a race condition, but it is essential, and goes back to the caller, so if it is important to the caller, they need to do the preventive measures.

That’s exactly the problem. In some use cases the race condition can be tolerated, in other it can’t. IF there was an API to implement a callback exchange, it would need to provide a way to realize both requirements. The OS can not stipulate or enforce a way to prevent the race in the firast place.

Because the race occurs in the calling code, the calling code needs to deal with the race!

If the caller doesn’t do anything, the timer might be activated just before or just after the subroutine, and there is NOTHING the subroutine can do about it. Thus, if the caller cares, he cares about more than just inside the subroutine, and needs to guard with a critical section or scheduler disable around the code that needs to be ‘atomic’.

If the call is within a timer callback we are already protected. If not, then likely, if they care, they should start the protection before getting the information to decide to or what to change the callback to, and end after the call returns.

Richard, please re-read my post #11 in this thread. I believe you still have a misconception about the race condition at hand here. The race is very simply between the invocation of the callback and its modification, NOT in terms of pointer corruption (again, that’s trivial to deal with) but in terms of which callback is expected to be executed. In the scenario I outlined, a different callback than expected may be executed due to the race.

Hence it’d be nice to have a state machine triggered by a single shot timer and executed synchronously in the callback if the sequence of timed actions is known up front.

The issue is that the race isn’t in the call to set the callback, but in the code deciding to change the callback.

I am assuming that this function directly changes the timer control block, and not using the timer queue to handle the operation. The timer message has no space for the call back pointer, and there isn’t the need to serialize the operation. (Yes THAT would add some issues).

You’re still missing the point, Richard. None of what you raise is relevant to the race at hand. The internal data structures of the timer representation are completly irrelevant.

When the setting of the callback and its invocation “overlap,” the trivial solution is to ensure atomicity. Yet there is still a race condition that may in one scenario change the callback before it is invoked and another one afterwards, ending up in two different callback invocation sequences.

Consider a periodic 500ms callback and another 300ms timer (possibly executing in a different task thus no serialization in the timer task) that toggles the first timer’s callback at every invocation. This leads to a fairly predictable pattern of callbacks invoked every 500ms, right? Except that at time stamps 1500,3000 etc, the callback modification falls at “the same time,” meaning that depending on a number of issues such as task priorization, starvation, ISR load etc, an unpredictable toggle occurrs.

If we want that to happen or not is up to the application, so the application would need to inform the OS how it wants the scenario to be handled. My point is that this mechanism is too error prone to be usefully implementable, also given the fact that there are several ways to implement dynmaic callbacks on the application level. Thus I’d vote against an API which would create a support nightmare just to serve a fringe condition that can much more flexibly be handled on app level.

First, ALL timer callback occur in the timer task, but you could have something else happen at a periodic rate.

Yes, if the same tick interrupt triggers alone routine wanting to change what call back happens and the call back occurring, there is some uncertainty in the order they happen, but there is NOTHING That a simple call to change the callback for a timer routine can do about that. The uncertainty happened before it got called. Your system has a fundamental race between the two operations, and it is only solvable at the application programmer level.

That is my point, by the time you call the change callback routine it is too late to fix the race, if you really wanted the change first, the timer callback may have already happened.

Unless you are saying that there needs to be some built in routine to handle arbitrarily scheduled callback changes, there is nothing FreeRTOS can do here. That is an applications problem. Maybe what you really want is the timer callback going first to something that decide which 500 ms event this should be and then goes there. That is application code.

The one thing that might be useful to add would be to allow a priority ordering of timer events that trigger at the same time.