Questions about obtaining the implementation code of the mutex function OSMutexPend() in the uCOS official mutex function (stm32F4)

OSMutexPend() : Get mutex function

{

Function idea : The task has exclusive ownership of the mutex. The mutex can only be held by one task at any time. If the mutex is in the unlocked state, then the task that acquires the mutex will successfully acquire the mutex. , and has the right to use the mutex; if the mutex is in a locked state, the task that obtains the mutex will not be able to obtain the mutex, and the task will be suspended. Priority inheritance will be performed before the task is suspended . , if the current task priority is higher than the priority of the task holding the mutex, then the priority of the task holding the mutex will be temporarily increased .

Five entry parameters : pointer to the mutex; timeout; options; timestamp when released (0 means the mutex has not been acquired and released after it was created; or the mutex is occupied by other tasks, and the task Failed to obtain mutex even after timeout); return error type

Function process : ① Security check, parameter option check, etc. There are only two options: OS_OPT_PEND_BLOCKING and OS_OPT_PEND_NON_BLOCKING, which are whether the task should block if the mutex is held by other tasks. ② Entering the critical section, if the mutex is available (unlocked state) (OwnerNestingCtr value is 0), then modify the value of the mutex element OwnerTCBPtr and assign the current TCB to it; then increase OwnerNestingCtr by 1, indicating that the mutex is locked status; and assign the returned timestamp to the value of the timestamp element in the mutex; call OS_MutexGrpAdd() to add the mutex to the linked list of the mutex-held task, exit the critical section; return an error-free type, and return return. ③ If the current task acquires the mutex again, then it is necessary to modify the OwnerNestingCtr element of the mutex to increase its value by 1; return the timestamp; exit the critical section; return the error type "OS_ERR_MUTEX_OWNER" and return. ④ If the mutex is occupied by other tasks and the option type is OS_OPT_PEND_NON_BLOCKING, then the error "OS_ERR_PEND_WOULD_BLOCK" will be returned and return. ( Under the condition that the task selects "non-blocking", you can use this return type to determine whether the mutex is occupied by other tasks ). ⑤ If the mutex is occupied by other tasks, the option type is "OS_OPT_PEND_BLOCKING", first check whether the scheduler is locked ( because the task needs to be added to the waiting list, if the scheduler is locked, no task operations can be performed); if it is not locked, call OS_CRITICAL_ENTER_CPU_EXIT() to lock the scheduler but enable interrupts; if the priority of the current task is greater than the priority of the task that obtains the mutex, call OS_TaskChangePrio() to modify the priority of the task that obtains the mutex; Call OS_Pend() to add the task to the mutex's waiting list; call OS_CRITICAL_EXIT_NO_SCHED() to unlock the scheduler and not switch; then call OSSched() to switch (because the task here has entered the mutex's waiting list and is blocked , so switch). ⑥ The context is saved after switching. If it is executed here again, it means that the task must have recovered from the blocking state before it can continue to be scheduled. Then it should be judged according to the task status whether to wait until the mutex is restored or timeout recovery, etc. If the task status is "OS_STATUS_PEND_OK", it means we got the mutex, returns the timestamp and no error; if the task status is "OS_STATUS_PEND_ABORT", returns the timestamp of waiting for termination and the error of "waiting for termination"; if it is "OS_STATUS_PEND_TIMEOUT" , the return timestamp is 0, indicating that the mutex has not been obtained, and a "waiting timeout" error is returned; if it is "OS_STATUS_PEND_DEL", an error that the mutex has been deleted is returned; other states unrelated to waiting are invalid.

- Call OS_TaskChangePrio() :

Note : ① Because as long as the task holds the mutex, it has ownership of it. Only waiting for the task to call the function that releases the mutex will make the mutex available. Therefore, the task may be in various states before calling the release mutex function , such as ready state: after creating the task, the task will be inserted into the ready list. If the insertion is successful, the task status will be modified to ready state. During the task running process, if When functions involving task status changes are called, the task status is in the ready state. For example, in the delay state: if the blocking delay function is called in it, the task will be removed from the ready list, inserted into the time base list, and the insertion will be successful and the status will be changed to the delay state; if it is called before the task has released the mutex, , the task is in a delayed state. Therefore, the priority of the task needs to be modified according to the task status . ② This function is called in the function OSMutexPend() that obtains the mutex. Before calling this function, OS_CRITICAL_ENTER_CPU_EXIT() has been called to lock the scheduler to prevent task switching and status changes (such as inserting the time base list, waiting list, etc.). ③ After modifying the priority, the task should be inserted into the ready list under the corresponding priority.

Two entry parameters : pointer to the task TCB holding the mutex; the priority to be replaced

Function process : It is a do-while loop, and the loop condition is that the TCB pointer is not empty. The body of the loop is to modify the priority of the task according to the task status and the operations performed to modify the priority. ( The purpose of the do-while loop here is to solve the problem that the task holding the mutex is blocked in the waiting list of other mutexes when accessing resources protected by other mutexes ). ① If it is in the ready state, it means that the task is still running, or it has not been switched to the task . In order to prevent the task running status from being modified when the task priority is modified, first call OS_RdyListRemove() to remove the task from the ready list ( if there are no tasks under this priority after removal, this function will clear the corresponding priority table at the same time. priority bit ); so after modifying the task priority, call OS_PrioInsert() to set 1 in the corresponding priority table position. This function uses the or algorithm to set 1, so there is no need to worry about duplication and affecting other positions; because the ready state has There are two situations: the task is running, so after modifying the priority, it should be inserted into the head of the ready list so that it can continue to run; the second situation is that it may not have been switched to it yet (but it holds the mutex and (not released), the current task is not equal to the task holding the mutex, then the task holding the mutex is inserted into the end of the ready list. Because the priority is increased, it cannot be inserted into the head to allow other high-priority tasks. The waiting time for tasks to run becomes longer. ② If the task status is delayed, suspended, delayed + suspended, only the priority of the task holding the mutex is changed, because when the task is in these states, it has been removed from the ready list and the priority is modified. When switched to ready state, they are automatically inserted into the ready list according to priority. ③ If it is waiting, timeout waiting, waiting + hanging, timeout waiting + hanging, because it is also removed from the ready list, in addition to modifying the priority like ②, you also need to access the pendon element of the task to see what the task is waiting for. . If you are not waiting for a mutex (such as waiting for a semaphore, message queue, etc.), you should call OS_PendListChangePrio() to modify the position of the task in the waiting list, because the waiting list is a one-way linked list strung together according to the task priority. Modified Task priority, of course, needs to be modified in the linked list. ④ Because a task can hold multiple mutexes and access multiple protected resources, if it is waiting for a mutex, then in addition to calling OS_PendListChangePrio() to modify the task's position in the mutex waiting list, because the task is in other mutexes In the mutex waiting list, and because the priority has been increased, it is necessary to determine whether the priority of another mutex task needs to be increased. If necessary, you need to repeat the above process, because this function is used to modify the priority of the task holding the mutex ; if not, then there is no need to repeat the above process and exit the loop . There is only one necessary situation: the priority of the task in the waiting list is greater than the priority of the task holding the mutex. However, uCOS officials did not write the judgment code as below.

Because the OS_TaskChangePrio() function can not only change the priority to a higher level, but also change the priority to a lower level , and it does not only change the priority of tasks holding mutexes, other tasks can also call this function to change the priority during running. level . It's just that the mutex function OSMutexPend() is obtained here and calls OS_TaskChangePrio() to inherit the priority and change the priority to a larger value. Therefore, uCOS officially distinguishes two situations: one is to change the priority to a smaller value, prio_cur > prio_new; the other is to keep it unchanged or change it to a larger value .

But I still think the official logic is wrong :

I'll post my logic and hope you guys can give me some advice:

Guess you like

Origin blog.csdn.net/m0_43443861/article/details/125960849