Task Exception Handling Function
In this issue, we will explain the task exception handling function of T-Kernel.
From Part 2 to 5 of this series, we will discuss the task management function of T-Kernel and the functions related to synchronization and communication between tasks (event flags and semaphores). An important point in terms of the task exception handling features we’ll be dealing with is that many of these APIs are runtime situation dependent.
For example, suppose we use the semaphores we dealt with in Part 3 to perform the following acquisition of semaphore resources for semaphores.
tk_wai_sem( semid, 1, TMO_FEVR );
At this time, it is very important to know from which task the above process was executed. If resources cannot be allocated immediately, the task that executed the API above enters a state of waiting until it can acquire the resources, but if it was executed by task A, task A transitions to the waiting state and if it was executed by task B, task B transitions to the waiting state. In this way, the situation when executing the API of T-Kernel is information that greatly affects the operation of the API, and this is generally called “context”.
The task exception handling function described in this article can be described in a nutshell as a special function for exception handling that allows you to enter the context of task B from task A to perform an operation in another task. In the previous example, you can say that this function allows you to execute tk_wai_sem()
from task B to put task A in a waiting state.
Task Exception Handling Procedure
The task exception handling function of T-Kernel consists of the following APIs
tk_def_tex | Define a task exception handler |
---|---|
tk_ras_tex | Fired a task exception |
tk_dis_tex | Prohibit task exceptions |
tk_ena_tex | Prohibit task exceptions |
tk_end_tex | Terminating a Task Exception Handler |
tk_ref_tex | Task exception status reference |
The following are the basic usage procedures for generating a task exception and executing the desired task exception processing in the context of the target task.
(1) tk_def_tex (definition of task exception handler) defines the task exception handler for the target task.
Task exception handlers are the operations you want to perform in the context of the target task and their association is done here.
(2) Use tk_ena_tex (allow task exceptions) to allow a task exception to be raised to that task.
Use this API to allow task exceptions so that when a task exception occurs, the task can accept the task exception.
(3) Execute tk_ras_tex (raise a task exception) to raise a task exception.
This executes the contents of the task exception handler in the context of the target task.
In addition, T-Kernel task exceptions can be identified by the task exception code number from 0 to 31 called “task exception code,” and task exception handlers can be defined independently for each exception code.
Exception handling using the task exception handling function
The following is an example of using task exceptions with task exception code 0. For task exceptions with an exception code other than 0, longjmp()
and so on can be used to perform return processing and so on, and by using these together, flexible exception processing can be achieved, but due to their complexity, this article does not cover them.
As an example, let’s say that a task exception is used to detect an abnormality in a task using mutex exclusion control, return it to a safe state, and then terminate the task. In this example, we will assume that mtxid
is the ID of the mutex object used in the mutualex exclusion control between tasks.
First, we need to define a task exception handler. The following is an example code that defines a task exception.
ER ercd; CONST T_DTEX dtex = { .texatr = 0, .texhdr = sample_texhdr, }; /* Define a task exception handler for the task indicated by tskid */ ercd = tk_def_tex(tskid, &dtex); if ( ercd < E_OK ) { goto err_ret; } /* Enable task exceptions in task exception code 0 */ tk_ena_tex(tskid, (1U << 0));
The definition of the main body of the task exception handler referenced above is as follows
/** * Task Exception Handling */ void sample_texhdr( INT texcd ) { /* Before unlocking the mutex, you can use the Moving to a consistent state of affairs */ cancel_operation(); /* Unlock the mutex. */ tk_unl_mtx(mtxid); /* Exit the task */ tk_ext_tsk(); }
This code assumes that you have a situation where a task is prevented from functioning properly due to a bug that causes a deadlock in the program, and you want to detect the situation from another task and safely terminate the task and restart it to get it back to the correct state. Unlocking the mutex can only be done by the acquired task, so you need to use a task exception handler. Before unlocking the mutex, you will need to transition to a situation where there is no problem in terms of exclusion control. Depending on what you want to do, the above code example uses the cancel_operation()
function to do so.
Let’s assume that the task exception handler is used to detect a deadlock in another task and perform a recovery process. The following is an example code
ER ercd; /* Muhtex is trying to lock up. */ ercd = tk_loc_mtx(mtxid, MAXIMUM_LOCK_TIME); if ( ercd < E_OK ) { if ( ercd != E_TMOUT ) goto err_ret; /* When an error (E_TMOUT) due to timeout is detected The maximum lock time MAXIMUM_LOCK_TIME under normal operation is set to This means that it has been exceeded, and there is a possibility of a deadlock. For this reason, we raise a task exception (code = 0) */ tk_ras_tex(tskid, 0); /* Perform the recovery process */ restart_task(); }
In the example above, it is assumed that the maximum lock time by the mutex is MAXIMUM_LOCK_TIME
and the value is specified for the timeout time. If an error E_TMOUT
occurs here, the mutex lock has been unlocked for a longer period of time than expected in normal operation, which means that the system is not operating as expected due to a deadlock, etc. Therefore, we raise a task exception by executing tk_ras_tex()
to execute the task exception handling we defined earlier. After executing the task exception, the restart_task()
function is executed as a return process.
Caution
As you can see from the example in the previous section, the task exception handling feature is a rather special feature. The possibility that a task exception handler may be invoked anywhere in the program must be considered, and strict attention must be paid to this possibility.
For example, suppose that the following processing is performed within the target task. This is a common form of code where foo()
is exclusively controlled using a binary semaphore.
.... /* (A) */ tk_wai_sem(semid, 1, TMO_FEVR); /* (B) */ foo(); /* (C) */ tk_sig_sem(semid, 1); /* (D) */ ....
Now, what happens if you define a task exception handler as follows
void texhdr( INT texcd ) { tk_wai_sem(semid, 1, TMO_FEVR); /* (E) */ bar(); /* termination process */ tk_sig_sem(semid, 1); tk_ext_tsk(); }
If we simply allow this task exception, the following timing issues may arise
- If the task exception handler is invoked at the timing of (A) and (D), it works without any problems.
- If the task exception handler is invoked at the timing of (B) and (C), the situation will be such that binary semaphores are requested to be acquired twice from the same task, and the task will be permanently waiting.
As you can see, task exceptions are a feature that requires careful use and must be used correctly when the feature is absolutely necessary.
Relationship with Subsystems
As you can see, it is not so easy to use the task exception management feature correctly, but you can significantly reduce the burden of middleware development by combining it with subsystems.
The details are in the category of subsystem management functions, so I will not describe them here, but T-Kernel has a mechanism to safely and correctly handle exceptions to software modules created using the subsystem management function. Specifically, there is a mechanism to interrupt the processing for the task exceptions introduced in this article by the break function of each subsystem and to handle exceptions independently for each software module.
T-Kernel 2.0 Extension (T2EX) and T-Kernel Standard Extension (TKSE), which have been briefly introduced in this series, are examples of middleware that actually uses this mechanism to correctly handle exceptions.
If you are interested, please check the explanation about the break function in the subsystem management function in the T-Kernel 2.0 specification.
“もっと見る” カテゴリーなし
Mbed TLS overview and features
In this article, I'd like to discuss Mbed TLS, which I've touched on a few times in the past, Transport …
What is an “IoT device development platform”?
I started using Mbed because I wanted a microcontroller board that could connect natively to the Internet. At that time, …
Mbed OS overview and features
In this article, I would like to write about one of the components of Arm Mbed, and probably the most …