pebble/src/fw/kernel/kernel_applib_state.c
Josh Soref c7851d5745 spelling: reentrancy
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2025-01-29 00:03:27 -05:00

173 lines
6.5 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 "kernel_applib_state.h"
#include "applib/ui/layer.h"
#include "mcu/interrupts.h"
#include "os/mutex.h"
#include "FreeRTOS.h"
#include "task.h"
static PebbleRecursiveMutex *s_log_state_mutex = INVALID_MUTEX_HANDLE;
static bool s_log_state_task_entered[NumPebbleTask]; // which tasks have entered
// ---------------------------------------------------------------------------------------------
CompassServiceConfig **kernel_applib_get_compass_config(void) {
static CompassServiceConfig *s_compass_config;
return &s_compass_config;
}
// --------------------------------------------------------------------------------------------
AnimationState* kernel_applib_get_animation_state(void) {
static AnimationState s_kernel_animation_state;
return &s_kernel_animation_state;
}
// Get the current task. If FreeRTOS has not been initialized yet, set to KernelMain
static PebbleTask prv_get_current_task(void) {
if (pebble_task_get_handle_for_task(PebbleTask_KernelMain) == NULL) {
return PebbleTask_KernelMain;
} else {
return pebble_task_get_current();
}
}
// --------------------------------------------------------------------------------------------
// Return a pointer to the LogState to use for kernel (non app task) code. The LogState contains
// the buffers for formatting the log message.
// Returns NULL if a kernel logging operation is already in progress
LogState *kernel_applib_get_log_state(void) {
static LogState sys_log_state;
bool use_mutex;
// Return right away if we re-entered from the same task For example, if we hit an assert while
// trying to grab the s_log_state_mutex mutex below and tried to log an error.
PebbleTask task = prv_get_current_task();
if (s_log_state_task_entered[task]) {
return NULL;
}
s_log_state_task_entered[task] = true;
// We have 3 possible phases of operation:
// 1.) Before FreeRTOS has been initialized - only 1 "task", no mutexes available
// 2.) After FreeRTOS, but before our mutex has been created (via kernel_applib_init())
// 3.) After our mutex has been created.
// In phase 1, we don't bother taking the mutex but still log
// In phase 2, we just return without logging. It is too dangerous to have
// possibly multiple tasks using logging without mutex support
// In phase 3, we log after locking the mutex only.
// Note, if we are in an ISR or critical section in any of these phases, we cannot use a mutex
if ((pebble_task_get_handle_for_task(PebbleTask_KernelMain) == NULL) || mcu_state_is_isr()
|| portIN_CRITICAL() || (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING)) {
// phase 1 || in an ISR || in a critical section
use_mutex = false;
} else if (s_log_state_mutex == INVALID_MUTEX_HANDLE) {
// phase 2
dbgserial_putstr("LOGGING DISABLED");
goto exit_fail;
} else {
// phase 3
use_mutex = true;
}
if (use_mutex) {
// Logging operations shouldn't take long to complete. Use a timeout in case we run into
// an unlikely deadlock situation (one task doing a synchronous log to flash and another task
// trying to log from flash code)
bool success = mutex_lock_recursive_with_timeout(s_log_state_mutex, 1000);
if (!success) {
dbgserial_putstr("kernel_applib_get_log_state timeout error");
goto exit_fail;
}
}
// Return if re-entered (logging while logging). This can happen for example if one task
// grabbed the context from an ISR or critical section and another grabbed it using the mutex
if (sys_log_state.in_progress) {
if (use_mutex) {
mutex_unlock_recursive(s_log_state_mutex);
}
goto exit_fail;
}
sys_log_state.in_progress = true;
return &sys_log_state;
exit_fail:
s_log_state_task_entered[task] = false;
return NULL;
}
// --------------------------------------------------------------------------------------------
// Release the LogState buffer obtained by kernel_applib_get_log_state()
void kernel_applib_release_log_state(LogState *state) {
state->in_progress = false;
// For phase 1 & when in an ISR, there is no mutex available
if (!portIN_CRITICAL() && !mcu_state_is_isr() &&
(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) &&
(s_log_state_mutex != INVALID_MUTEX_HANDLE)) {
mutex_unlock_recursive(s_log_state_mutex);
}
// Clear the reentrancy flag for this task
PebbleTask task = prv_get_current_task();
s_log_state_task_entered[task] = false;
}
// ---------------------------------------------------------------------------------------------
EventServiceInfo* kernel_applib_get_event_service_state(void) {
static EventServiceInfo s_event_service_state;
return &s_event_service_state;
}
// --------------------------------------------------------------------------------------------
TickTimerServiceState* kernel_applib_get_tick_timer_service_state(void) {
static TickTimerServiceState s_tick_timer_service_state;
return &s_tick_timer_service_state;
}
// -----------------------------------------------------------------------------------------------------------
ConnectionServiceState* kernel_applib_get_connection_service_state(void) {
static ConnectionServiceState s_connection_service_state;
return &s_connection_service_state;
}
// -----------------------------------------------------------------------------------------------------------
BatteryStateServiceState* kernel_applib_get_battery_state_service_state(void) {
static BatteryStateServiceState s_battery_state_service_state;
return &s_battery_state_service_state;
}
Layer** kernel_applib_get_layer_tree_stack(void) {
static Layer* layer_tree_stack[LAYER_TREE_STACK_SIZE];
return layer_tree_stack;
}
// -------------------------------------------------------------------------------------------------------------
void kernel_applib_init(void) {
s_log_state_mutex = mutex_create_recursive();
connection_service_state_init(kernel_applib_get_connection_service_state());
battery_state_service_state_init(kernel_applib_get_battery_state_service_state());
}