Skip to content

Commit 56ed755

Browse files
authored
Fix crash caused by race condition in xSemaphoreTake (#173)
* Fix race condition in xSemaphoreTake This race condition would make infinisim eventually crash with the message "Mutex released without being held" on my machine. The function `xSemaphoreTake` waits for the semaphore to be available to take by looping + sleeping until there is an element in the `pxQueue->queue`. Then it locks the `pxQueue->mutex` and checks the queuesize again. Since the first check is not done under the mutex, some other thread could remove the element from the queue before the mutex is locked. Then the second check in `xSemaphoreTake` will return false. The method `DateTimeController::CurrentDateTime()` uses a semaphore as a mutex to guard calls to `UpdateTime()`. But it (and the other methods from `DateTimeController`) do not check if `xSemaphoreTake` actually gave them the semaphore. They will try to give it back even if they failed to take it, which leads to the crash in `xSemaphoreGive`. The other thread in this crash was calling the watchface refresh method, which also calls `CurrentDateTime()`. To fix the race condition, lock the mutex guarding `pxQeue->queue` before checking the queue size and unlock before sleeping. When implemented like this, `xSemaphoreTake` from InfiniSim follows the API from FreeRTOS more closely, which waits the full `xTicksToWait` while InfiniSim would abort prematurely in some cases. With the assumption that `UpdateTime()` will finish within some finite amount of time, this make the methods in `DateTimerController` work even without error handling since they have such long wait times. * Change to constexpr and use the appropriate type for DELAY_BETWEEN_ATTEMPTS Since xTicksToWait is TickType_t (uint32) and SDL_Delay takes uint32 as well, we should avoid any chance of funny business due to differences in signedness.
1 parent 9893cb7 commit 56ed755

File tree

1 file changed

+19
-13
lines changed

1 file changed

+19
-13
lines changed

sim/semphr.cpp

+19-13
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,33 @@
55
QueueHandle_t xSemaphoreCreateMutex() {
66
SemaphoreHandle_t xSemaphore = xQueueCreate(1, 1);
77
Queue_t *pxQueue = (Queue_t *)xSemaphore;
8+
// Queue full represents taken semaphore/locked mutex
89
pxQueue->queue.push_back(0);
910
return xSemaphore;
10-
};
11+
}
1112

1213
BaseType_t xSemaphoreTake(SemaphoreHandle_t xSemaphore,
1314
TickType_t xTicksToWait) {
1415
Queue_t *pxQueue = (Queue_t *)xSemaphore;
15-
while (!pxQueue->queue.empty()) {
16-
if (xTicksToWait <= 25) {
17-
return false;
16+
constexpr TickType_t DELAY_BETWEEN_ATTEMPTS = 25;
17+
do {
18+
if (pxQueue->mutex.try_lock()) {
19+
std::lock_guard<std::mutex> lock(pxQueue->mutex, std::adopt_lock);
20+
if (pxQueue->queue.empty()) {
21+
pxQueue->queue.push_back(0);
22+
return true;
23+
}
1824
}
19-
SDL_Delay(25);
20-
xTicksToWait -= 25;
21-
}
22-
std::lock_guard<std::mutex> guard(pxQueue->mutex);
23-
if (!pxQueue->queue.empty()) {
24-
return false;
25-
}
26-
pxQueue->queue.push_back(0);
27-
return true;
25+
// Prevent underflow
26+
if (xTicksToWait >= DELAY_BETWEEN_ATTEMPTS) {
27+
// Someone else is modifying queue, wait for them to finish
28+
SDL_Delay(DELAY_BETWEEN_ATTEMPTS);
29+
xTicksToWait -= DELAY_BETWEEN_ATTEMPTS;
30+
}
31+
} while (xTicksToWait >= DELAY_BETWEEN_ATTEMPTS);
32+
return false;
2833
}
34+
2935
BaseType_t xSemaphoreGive(SemaphoreHandle_t xSemaphore) {
3036
Queue_t *pxQueue = (Queue_t *)xSemaphore;
3137
std::lock_guard<std::mutex> guard(pxQueue->mutex);

0 commit comments

Comments
 (0)