How to Use Task Callbacks

By Ralph Moore · December 27, 2025 · SecureSMX, SMX

Task callbacks add powerful tools to the embedded programmers toolbox. Each task can have its own callback function, or no callback function, as follows:

cbfunA is the callback function for taskA. If enabled, it is called automatically by the scheduler and by certain task services. Task callback functions can be used in many ways, some of which are as follows.

Saving Extended Task Contexts

Most RTOSs provide task callbacks to save and restore extended task contexts. For example, when a task switch occurs, Cortex-M automatically saves the first 16 floating-point registers. The remaining floating-point registers, if used, must be saved by the application when the task is suspended and they must be restored when the task is resumed. Task callback functions make this easier to do:

   cbfun(EXIT);

is called by the scheduler when the current task is being suspended and

   cbfun(ENTER);

is called by the scheduler when it is being resumed.

These cases can be used to save additional floating point registers. On the other hand, if only a few floating point registers are actually being used, it may be desirable to turn off the Cortex-M automatic floating-point state preservation and use the EXIT and ENTER callbacks to save and restore just the floating-point registers actually being used. This reduces task switching times and memory usage.

In addition to the floating-point unit, other coprocessors may need their registers to be saved and restored during task switches. Also, task-specific variables may need to be saved and restored.

Managing Task Resources

When a task first starts, it may need to be initialized and to acquire system resources, such semaphores, mutexes, and memory. This can be done with:

   cbfun(INIT);

If it becomes necessary to delete the task due to its being breached by malware or due to something else, the resources can be automatically released with:

   cbfun(DELETE);

The advantage of this approach is that the DELETE case is immediately below the INIT case in cbfun(). This helps to ensure that every resource acquired during task initialization is released during task deletion. Being able to shut down a task without resource leaks is important for disabling malware that has breached a task, before it can cause harm. cbfun(INIT) is called from TaskStart(), which normally is called during initialization. cbfun(DELETE) is called from TaskDelete(), which normally is called during task or system exit. So, neither adds to task switching time.

An alternate approach is for cbfun(DELETE) to send a message to an exchange where a recovery task waits to release the deleted task’s resources and to recover normal operation. The message would identify the deleted task and the recovery task would call:

   cbfunA(RECOVER);

where cbfunA is the callback function for taskA and RECOVER is a case to release taskA’s resources, report the event causing taskA to be stopped, then attempt to restart taskA or start a replacement task. This is a good technique to rid a breached task of malware, if possible, or to shut it down until a fix is available, if not. In the latter case, the replacement task might be a stub that enables the system to continue running and performing its main functions.

For resources that may be acquired during run time, special handling is needed. For example, if semA may or may not have been acquired:

   case DELETE:
      if (semA)
         SemDelete(semA);

Preserving One-Shot Task Contexts

The STOP and START cases are unique to SMX one-shot tasks. They serve the same purposes as the EXIT and ENTER cases do for normal tasks. Note: the first time a task is started, cbfun() is not called.

Controlling Task Callbacks

Not all tasks require callbacks and, for some tasks a callback may be necessary only in part of its code. This can be controlled as follows:

   TaskSet(ct, SMX_ST_CBFUN, cbfun, 1); // callback is active and all
                                        // cases are enabled

   TaskSet(ct, SMX_ST_CBFUN, cbfun, 0); // callback is active and only
                                        // INIT and DELETE are enabled

   TaskSet(ct, SMX_ST_CBFUN, NULL, 0);  // callback is inactive

The INIT and DELETE cases may be needed for security, as previously discussed.

Callback functions can also be enabled and disabled while a task is running:

   smx_TaskSet(ct, SMX_ST_CBFUN, (u32)fpfunA, 1); // do floating-point
                                                  // operations

   smx_TaskSet(ct, SMX_ST_CBFUN, cbfunA, 1); // return to normal

Here fpfunA(), which preserves floating-point registers and extended task context, in the event of task suspension, is being enabled before a section of code that uses floating-point. After the floating-point section, cbfunA(), which preserves only extended task context, is enabled.

Conclusion

Task callbacks provide methods to deal with difficult problems. Callback code can be found in xtask.c and xsched.c at www.github.com/Micro-Digital/SecureSMX.

My LinkedIn Profile

Search

Categories

Recent posts

October 15, 2026
Tokens Improve IoT Security
September 28, 2026
Task Runtime Limits
August 22, 2026
RTOS Pitfalls
August 16, 2026
Interrupt and Task RTOS Latencies
August 1, 2026
An Event Friendly RTOS