Incorrect mutex behavior?

vonnieda
Posts: 145
Joined: Tue Nov 07, 2017 3:42 pm

Incorrect mutex behavior?

Postby vonnieda » Tue Dec 24, 2019 3:15 pm

I'm trying to track down a potential concurrency issue in a large codebase (which I can't share) and I'm checking my assumptions on FreeRTOS mutexes and semaphores. I've identified what appears to be incorrect behavior in the mutex, at least according to the docs.

The FreeRTOS docs ( https://www.freertos.org/CreateMutex.html ) say:
The priority of a task that ‘takes’ a mutex will be temporarily raised if another task of higher priority attempts to obtain the same mutex. The task that owns the mutex ‘inherits’ the priority of the task attempting to ‘take’ the same mutex. This means the mutex must always be ‘given’ back – otherwise the higher priority task will never be able to obtain the mutex, and the lower priority task will never ‘disinherit’ the priority.
Lots of other pages on the Internet say that the primary difference between a mutex and a binary semaphore is that a mutex must be released by the owning thread, and it CAN NOT be released by another thread. This is what I'm seeing that I think may be incorrect. It appears that I can xSemaphoreGive(mutex) from another task just fine, breaking this contract.

Here's my test program:

Code: Select all

#include <stdio.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"

static SemaphoreHandle_t sem;

static void take(void* pv) {
	while (true) {
		printf("take %d\n", xSemaphoreTake(sem, portMAX_DELAY) == pdTRUE);
		vTaskDelay(pdMS_TO_TICKS(1000));
	}
}

static void give(void* pv) {
	while (true) {
		printf("give %d\n", xSemaphoreGive(sem) == pdTRUE);
		vTaskDelay(pdMS_TO_TICKS(1000));
	}
}

void app_main() {
	sem = xSemaphoreCreateMutex();
	xTaskCreate(take, "take", 7 * 1024, NULL, tskIDLE_PRIORITY, NULL);
	xTaskCreate(give, "give", 7 * 1024, NULL, tskIDLE_PRIORITY + 1, NULL);
}
And the output:

Code: Select all

I (237) cpu_start: Starting scheduler on PRO CPU.
give 0
take 1
give 1
take 1
give 1
take 1
give 1
According to the docs, the "give" task should not be able to give the mutex because the "take" task is the owner, but it works fine. I ran this test for 8+ hours and it never failed.

Can someone help me understand what is happening here?

Thanks,
Jason

WiFive
Posts: 3529
Joined: Tue Dec 01, 2015 7:35 am

Re: Incorrect mutex behavior?

Postby WiFive » Tue Dec 24, 2019 9:28 pm


vonnieda
Posts: 145
Joined: Tue Nov 07, 2017 3:42 pm

Re: Incorrect mutex behavior?

Postby vonnieda » Tue Dec 24, 2019 11:24 pm

Thanks WiFive, but could you expand a little? This seems to be a change to the pthread abstraction layer, but I'm not using pthreads. Are you suggesting that I do, or just showing the error, or something else?

Thanks,
Jason

ESP_Sprite
Posts: 9730
Joined: Thu Nov 26, 2015 4:08 am

Re: Incorrect mutex behavior?

Postby ESP_Sprite » Wed Dec 25, 2019 12:59 am

I think the 'contract' is more a logical result of the way you should use mutexes rather than something that is hardcoded. Mutexes are intended to protect resource access, so normally, a task would take a mutex, do the resource access, then give it back. As a give is always preceded by a take, and by definition two tasks cannot be in the code between a give and take at the same time, the give is always executed on the same task as the take.

vonnieda
Posts: 145
Joined: Tue Nov 07, 2017 3:42 pm

Re: Incorrect mutex behavior?

Postby vonnieda » Wed Dec 25, 2019 5:08 am

ESP_Sprite wrote:
Wed Dec 25, 2019 12:59 am
I think the 'contract' is more a logical result of the way you should use mutexes rather than something that is hardcoded. Mutexes are intended to protect resource access, so normally, a task would take a mutex, do the resource access, then give it back. As a give is always preceded by a take, and by definition two tasks cannot be in the code between a give and take at the same time, the give is always executed on the same task as the take.

Thanks ESP_Sprite - I get the concept, but this came up because I had implemented a readers-writer lock based on the Wiki article ( https://en.wikipedia.org/wiki/Readers%E ... riter_lock ). I had used two mutexes. In the field I've noticed very rare instances where one of the mutexes gets stuck, so I started investigating and I came across a note in another implementation ( https://github.com/michaelbecker/freert ... ock.c#L161 ) that says:

ResourceLock CANNOT be a mutex. In FreeRTOS, as in most OS's, a thread is not allowed to unlock another thread's mutex. But the very nature of a Reader Lock allows an arbitrary ordering of unlocks when multiple threads hold the reader lock. Semaphores are not subject to this constraint.

So that's why I wrote the test and wanted to confirm the behavior. If this isn't true, then it's probably not the root cause of the issue I'm trying to solve, so just changing to a binary semaphore probably won't fix it. And based on the test I wrote, and having read through the FreeRTOS code today, it seems there is no difference between a binary semaphore and a mutex for tasks with the same priority.

Thanks,
Jason

Who is online

Users browsing this forum: Bing [Bot] and 92 guests