-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Description
Lots and lots and lots of zephyr code uses semaphores as a blocking mutex when irq_lock() would be a latency problem, e.g.:
static K_SEM_DEFINE(sem, 1, 1);
...
{
k_sem_take(&sem);
/* critical section */
k_sem_give(&sem);
}
This works well, and is a simple idiom that is easily understood.
Nonetheless the semantics of a proper mutex and a semaphore are a little different: semaphores allow for multiple gives, and thus for nested calls that only deadlock "sometimes". The initial count of a mutex must always be 1, while semaphores naturally like to start at zero. Semaphores may be used from multiple threads where a mutex must always be locked and unlocked out of the same thread. All of these represent "mistake points" that a simpler mutex API would not have.
Also, there's a practical problem discovered in #8368: there are flash drivers that use a sempahore lock like this, and thus they refuse to work when CONFIG_MULTITHREADING=n because the semaphore implementation requires the threading APIs. But in that particular case, there are no other user threads and so the a proper lock API (but not a semaphore API) would be able to degrade to a noop and thus preserve the driver semantics without worry.
Zephyr should have a simple mutex lock API that is a wrapper around k_sem. It should be typesafe (i.e. not interoperable with the semaphore calls) and have assertions included to validate the appropriate rules. But at runtime it should produce the same code.
(Note: the existing k_mutex abstraction is not a candidate. That is a priority-inheriting mutex with significant code complexity and performance overhead.)