-
Notifications
You must be signed in to change notification settings - Fork 30
Description
When the task is waiting or holding a mutex, it can still be removed by another task and never handle the mutex. For example, if the task is holding the mutex and is cancelled by another task, the mutex ownership still the same as this cancelled task.
This issue causes synchronization mechanisms to become invalid.
Test
lock_task(): Acquire the mutex and yield tocancel_task(), this task will be cancelled while holding the mutex.cancel_task(): Cancel the task that holds the mutex and try to acquire the mutex.
#include <linmo.h>
static mutex_t m;
int32_t lock_task_id, cancel_task_id;
void lock_task(void)
{
mo_mutex_lock(&m);
mo_task_yield();
mo_mutex_unlock(&m);
printf("Never return here \n");
for (;;)
mo_task_wfi();
}
void cancel_task(void)
{
mo_task_cancel(lock_task_id);
printf("Owner ID is %d and lock task ID is %d\n", m.owner_tid, lock_task_id);
mo_mutex_lock(&m);
printf("Ownership inconsistent, owner id %d still the same \n", m.owner_tid);
mo_mutex_unlock(&m);
for (;;)
mo_task_wfi();
}
int32_t app_main(void)
{
mo_logger_flush();
lock_task_id = mo_task_spawn(lock_task, DEFAULT_STACK_SIZE);
cancel_task_id = mo_task_spawn(cancel_task, DEFAULT_STACK_SIZE);
mo_mutex_init(&m);
/* preemptive mode */
return 1;
}Result
$make run
Ready to launch Linmo kernel + application.
Linmo kernel is starting...
Heap initialized, 130003764 bytes available
task 1: entry=80001210 stack=80004d78 size=1024 prio_level=4 time_slice=5
Logger initialized
task 2: entry=800001d4 stack=80005234 size=8192 prio_level=4 time_slice=5
task 3: entry=8000020c stack=800072bc size=8192 prio_level=4 time_slice=5
Scheduler mode: Preemptive
Owner ID is 2 and lock task ID is 2
Ownership inconsistent, owner id 2
The cancelled task, which is the owner of the mutex, does not reset the mutex properties. As the test results show, the owner ID should be 0 when the mutex holder is cancelled. Or just rejecting cancellation when holding the mutex.
Analysis
Refer to mo_mutex_lock(), the code section below is used for updating new ownership of the mutex; however, the cancelled task while holding the mutex never releases the mutex, which will reset owner_tid = 0.
...
/* Fast path: mutex is free, acquire immediately */
if (likely(m->owner_tid == 0)) {
m->owner_tid = self_tid;
NOSCHED_LEAVE();
return ERR_OK;
}
...