pebble/src/libos/mutex_freertos.c
2025-01-27 11:38:16 -08:00

191 lines
5.6 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mcu/interrupts.h"
#include "os/assert.h"
#include "os/malloc.h"
#include "os/mutex.h"
#include "os/tick.h"
#include "FreeRTOS.h"
#include "light_mutex.h"
#include "task.h"
#include <string.h>
typedef struct {
uint32_t lr;
LightMutexHandle_t freertos_mutex;
} PebbleMutexCommon;
struct pebble_mutex_t {
PebbleMutexCommon common;
};
struct pebble_recursive_mutex_t {
PebbleMutexCommon common;
};
// macros should only be called while we hold the mutex we are logging so no
// additional locking is needed
#define LOG_LOCKED(logged_lr, new_lr) \
if (logged_lr == 0) { \
logged_lr = new_lr; \
}
#define LOG_UNLOCKED(logged_lr, nest_count) \
if (nest_count == 1) { \
logged_lr = 0; \
}
static PebbleMutexCommon *create_pebble_mutex(LightMutexHandle_t freertos_mutex) {
PebbleMutexCommon *mutex = os_malloc_check(sizeof(PebbleMutex));
*mutex = (PebbleMutexCommon) {
.freertos_mutex = freertos_mutex,
.lr = 0
};
return mutex;
}
PebbleMutex * mutex_create(void) {
LightMutexHandle_t freertos_mutex = xLightMutexCreate();
OS_ASSERT(freertos_mutex != NULL);
PebbleMutex *mutex = (PebbleMutex *)create_pebble_mutex(freertos_mutex);
return mutex;
}
void mutex_destroy(PebbleMutex * handle) {
OS_ASSERT(handle != NULL);
LightMutexHandle_t mutex = handle->common.freertos_mutex;
vLightMutexDelete(mutex);
os_free(handle);
}
void mutex_lock(PebbleMutex * handle) {
uintptr_t myLR = (uintptr_t) __builtin_return_address(0);
OS_ASSERT(!mcu_state_is_isr());
xLightMutexLock(handle->common.freertos_mutex, portMAX_DELAY);
LOG_LOCKED(handle->common.lr, myLR);
}
bool mutex_lock_with_timeout(PebbleMutex * handle, uint32_t timeout_ms) {
uintptr_t myLR = (uintptr_t) __builtin_return_address(0);
OS_ASSERT(!mcu_state_is_isr());
TickType_t timeout_ticks = milliseconds_to_ticks(timeout_ms);
LightMutexHandle_t mutex = handle->common.freertos_mutex;
if (xLightMutexLock(mutex, timeout_ticks) == pdTRUE) {
LOG_LOCKED(handle->common.lr, myLR);
return (true);
}
return (false);
}
void mutex_lock_with_lr(PebbleMutex * handle, uint32_t myLR) {
OS_ASSERT(!mcu_state_is_isr());
xLightMutexLock(handle->common.freertos_mutex, portMAX_DELAY);
LOG_LOCKED(handle->common.lr, myLR);
}
void mutex_unlock(PebbleMutex * handle) {
OS_ASSERT(!mcu_state_is_isr());
LOG_UNLOCKED(handle->common.lr, 1)
xLightMutexUnlock(handle->common.freertos_mutex);
}
static void prv_assert_held_by_curr_task(PebbleMutex * handle, bool is_held, uint32_t lr) {
LightMutexHandle_t mutex = handle->common.freertos_mutex;
OS_ASSERT_LR((xLightMutexGetHolder(mutex) == xTaskGetCurrentTaskHandle()) == is_held,
lr);
}
void mutex_assert_held_by_curr_task(PebbleMutex * handle, bool is_held) {
uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0);
prv_assert_held_by_curr_task(handle, is_held, saved_lr);
}
void mutex_assert_recursive_held_by_curr_task(PebbleRecursiveMutex * handle, bool is_held) {
uintptr_t saved_lr = (uintptr_t) __builtin_return_address(0);
prv_assert_held_by_curr_task((PebbleMutex *) handle, is_held, saved_lr);
}
PebbleRecursiveMutex * mutex_create_recursive(void) {
LightMutexHandle_t freertos_mutex = xLightMutexCreateRecursive();
OS_ASSERT(freertos_mutex != NULL);
PebbleRecursiveMutex * mutex = (PebbleRecursiveMutex *)create_pebble_mutex(freertos_mutex);
return mutex;
}
void mutex_lock_recursive(PebbleRecursiveMutex * handle) {
uintptr_t myLR = (uintptr_t) __builtin_return_address(0);
OS_ASSERT(!mcu_state_is_isr());
xLightMutexLockRecursive(handle->common.freertos_mutex, portMAX_DELAY);
LOG_LOCKED(handle->common.lr, myLR);
}
bool mutex_lock_recursive_with_timeout_and_lr(PebbleRecursiveMutex * handle,
uint32_t timeout_ms, uint32_t myLR) {
OS_ASSERT(!mcu_state_is_isr());
TickType_t timeout_ticks = milliseconds_to_ticks(timeout_ms);
LightMutexHandle_t mutex = handle->common.freertos_mutex;
if (xLightMutexLockRecursive(mutex, timeout_ticks) == pdTRUE) {
LOG_LOCKED(handle->common.lr, myLR);
return (true);
}
return (false);
}
bool mutex_lock_recursive_with_timeout(PebbleRecursiveMutex * handle, uint32_t timeout_ms) {
uintptr_t myLR = (uintptr_t) __builtin_return_address(0);
OS_ASSERT(!mcu_state_is_isr());
TickType_t timeout_ticks = milliseconds_to_ticks(timeout_ms);
LightMutexHandle_t mutex = handle->common.freertos_mutex;
if (xLightMutexLockRecursive(mutex, timeout_ticks) == pdTRUE) {
LOG_LOCKED(handle->common.lr, myLR);
return (true);
}
return (false);
}
bool mutex_is_owned_recursive(PebbleRecursiveMutex * handle) {
LightMutexHandle_t mutex = handle->common.freertos_mutex;
void* holder = xLightMutexGetHolder(mutex);
void* current = xTaskGetCurrentTaskHandle();
return (holder == current);
}
void mutex_unlock_recursive(PebbleRecursiveMutex * handle) {
OS_ASSERT(!mcu_state_is_isr());
LightMutexHandle_t mutex = handle->common.freertos_mutex;
LOG_UNLOCKED(handle->common.lr, uxLightMutexGetRecursiveCallCount(mutex));
xLightMutexUnlockRecursive(mutex);
}