0% found this document useful (0 votes)
5 views6 pages

Thread-Safe Error Handling System

The document implements a thread-safe error handling system in C, featuring a circular buffer for error records, error statistics, and a callback mechanism for error notifications. It includes functions for initializing the error handler, logging errors, retrieving the latest error, and managing error statistics. The system uses a mutex for thread safety and provides functionality to clear error records and check for critical errors.

Uploaded by

Jose Maca
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views6 pages

Thread-Safe Error Handling System

The document implements a thread-safe error handling system in C, featuring a circular buffer for error records, error statistics, and a callback mechanism for error notifications. It includes functions for initializing the error handler, logging errors, retrieving the latest error, and managing error statistics. The system uses a mutex for thread safety and provides functionality to clear error records and check for critical errors.

Uploaded by

Jose Maca
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

/**

* @file error_handler.c
* @brief Implementation of the thread-safe error handling system
*/

#include "error_handler.h"
#include <string.h>
#include <stdio.h>
#include "task.h"

/**
* @brief Internal structure for error handling system state
*/
typedef struct {
ErrorRecord_t records[ERROR_MAX_RECORDS]; /**< Circular buffer of error
records */
uint32_t write_index; /**< Current write position in
buffer */
ErrorStats_t stats; /**< Error statistics */
ErrorCallback_t callback; /**< Error notification callback */
SemaphoreHandle_t mutex; /**< Mutex for thread safety */
StaticSemaphore_t mutex_buffer; /**< Static buffer for mutex */
bool initialized; /**< Initialization flag */
} ErrorHandler_t;

/* Static instance of error handler */


static ErrorHandler_t error_handler;

/* Private function prototypes */


static void ErrorHandler_TruncateFilename(const char* src, char* dst, size_t
max_len);
static void ErrorHandler_UpdateStats(ErrorSeverity_t severity, ErrorCategory_t
category);
static bool ErrorHandler_AcquireMutex(void);
static void ErrorHandler_ReleaseMutex(void);

BaseType_t ErrorHandler_Init(void) {
/* Prevent multiple initialization */
if (error_handler.initialized) {
return pdFAIL;
}

/* Clear all data */


memset(&error_handler, 0, sizeof(ErrorHandler_t));

/* Create mutex using static buffer */


error_handler.mutex = xSemaphoreCreateMutexStatic(&error_handler.mutex_buffer);
if (error_handler.mutex == NULL) {
return pdFAIL;
}

error_handler.initialized = true;
return pdPASS;
}

bool ErrorHandler_Log(
ErrorSeverity_t severity,
ErrorCategory_t category,
uint32_t error_code,
const char* file,
uint32_t line,
const char* message,
uint32_t data
) {
if (!error_handler.initialized || !file || !message) {
return false;
}

/* Validate parameters */
if (severity > ERROR_SEVERITY_CRITICAL ||
category >= ERROR_CAT_COUNT) {
return false;
}

if (!ErrorHandler_AcquireMutex()) {
error_handler.stats.mutex_timeouts++;
return false;
}

/* Get pointer to current record */


ErrorRecord_t* record = &error_handler.records[error_handler.write_index];

/* Fill record */
record->timestamp = xTaskGetTickCount();
record->severity = severity;
record->category = category;
record->error_code = error_code;
record->line = line;
record->data = data;

/* Safely copy strings */


ErrorHandler_TruncateFilename(file, record->file, ERROR_FILE_NAME_LEN);
strncpy(record->message, message, ERROR_MAX_MSG_LEN - 1);
record->message[ERROR_MAX_MSG_LEN - 1] = '\0';

/* Update indices and statistics */


error_handler.write_index = (error_handler.write_index + 1) %
ERROR_MAX_RECORDS;
ErrorHandler_UpdateStats(severity, category);

/* Notify callback if registered and error is severe enough */


if (error_handler.callback && severity >= ERROR_SEVERITY_ERROR) {
error_handler.callback(record);
}

ErrorHandler_ReleaseMutex();
return true;
}

bool ErrorHandler_RegisterCallback(ErrorCallback_t callback) {


if (!error_handler.initialized) {
return false;
}

if (!ErrorHandler_AcquireMutex()) {
return false;
}
bool has_critical = false;
for (uint32_t i = 0; i < ERROR_MAX_RECORDS; i++) {
if (error_handler.records[i].severity == ERROR_SEVERITY_CRITICAL) {
has_critical = true;
break;
}
}

ErrorHandler_ReleaseMutex();
return has_critical;
}

/**
* @brief Truncate filename to remove path
*
* @param src Source filename with path
* @param dst Destination buffer
* @param max_len Maximum length of destination buffer
*/
static void ErrorHandler_TruncateFilename(const char* src, char* dst, size_t
max_len) {
size_t len = strlen(src);
const char* start = src;

/* Find last slash or backslash */


for (const char* p = src + len - 1; p >= src; p--) {
if (*p == '/' || *p == '\\') {
start = p + 1;
break;
}
}

strncpy(dst, start, max_len - 1);


dst[max_len - 1] = '\0';
}

/**
* @brief Update error statistics
*
* @param severity Error severity level
* @param category Error category
*/
static void ErrorHandler_UpdateStats(ErrorSeverity_t severity, ErrorCategory_t
category) {
error_handler.stats.total_errors++;
error_handler.stats.errors_by_severity[severity]++;
error_handler.stats.errors_by_category[category]++;

/* Check for buffer overrun */


if (error_handler.stats.total_errors > ERROR_MAX_RECORDS) {
error_handler.stats.buffer_overruns++;
}
}

/**
* @brief Acquire the error handler mutex with timeout
*
* @return true if mutex was acquired, false if timeout
*/
static bool ErrorHandler_AcquireMutex(void) {
if (xSemaphoreTake(error_handler.mutex,
pdMS_TO_TICKS(ERROR_MUTEX_TIMEOUT_MS)) != pdTRUE) {
return false;
}
return true;
}

/**
* @brief Release the error handler mutex
*/
static void ErrorHandler_ReleaseMutex(void) {
xSemaphoreGive(error_handler.mutex);
}
return false;
}

if (!ErrorHandler_AcquireMutex()) {
return false;
}

error_handler.callback = callback;
ErrorHandler_ReleaseMutex();

return true;
}

const ErrorRecord_t* ErrorHandler_GetLatest(void) {


if (!error_handler.initialized || error_handler.stats.total_errors == 0) {
return NULL;
}

if (!ErrorHandler_AcquireMutex()) {
return NULL;
}

uint32_t latest_index = (error_handler.write_index - 1) % ERROR_MAX_RECORDS;


const ErrorRecord_t* latest = &error_handler.records[latest_index];

ErrorHandler_ReleaseMutex();
return latest;
}

bool ErrorHandler_GetStats(ErrorStats_t* stats) {


if (!error_handler.initialized || !stats) {
return false;
}

if (!ErrorHandler_AcquireMutex()) {
return false;
}

*stats = error_handler.stats;
ErrorHandler_ReleaseMutex();

return true;
}

uint32_t ErrorHandler_GetLogString(char* buffer, uint32_t max_len, uint32_t


num_errors) {
if (!error_handler.initialized || !buffer || max_len == 0) {
return 0;
}

uint32_t written = 0;
uint32_t available = max_len;

/* Header */
written += snprintf(buffer + written, available,
"Error Log (Last %lu of %lu errors):\r\n",
num_errors, error_handler.stats.total_errors);
available = max_len - written;

if (!ErrorHandler_AcquireMutex()) {
return written;
}

/* Calculate start index */


uint32_t count = 0;
uint32_t index = error_handler.write_index;

while (count < num_errors &&


count < error_handler.stats.total_errors &&
available > 0) {
if (index == 0) {
index = ERROR_MAX_RECORDS - 1;
} else {
index--;
}

const ErrorRecord_t* record = &error_handler.records[index];

/* Format timestamp as [Link] */


uint32_t ms_per_tick = portTICK_PERIOD_MS;
uint32_t timestamp_s = (record->timestamp * ms_per_tick) / 1000;
uint32_t timestamp_ms = (record->timestamp * ms_per_tick) % 1000;

written += snprintf(buffer + written, available,


"%lu.%03lu - [%s:%lu] %s: %s (0x%lX)\r\n",
timestamp_s, timestamp_ms,
record->file, record->line,
record->severity == ERROR_SEVERITY_CRITICAL ? "CRIT" :
record->severity == ERROR_SEVERITY_ERROR ? "ERROR" :
record->severity == ERROR_SEVERITY_WARNING ? "WARN" : "INFO",
record->message,
record->data);

available = max_len - written;


count++;
}

ErrorHandler_ReleaseMutex();
return written;
}

bool ErrorHandler_Clear(void) {
if (!error_handler.initialized) {
return false;
}

if (!ErrorHandler_AcquireMutex()) {
return false;
}

memset(error_handler.records, 0, sizeof(error_handler.records));
memset(&error_handler.stats, 0, sizeof(error_handler.stats));
error_handler.write_index = 0;

ErrorHandler_ReleaseMutex();
return true;
}

bool ErrorHandler_HasCritical(void) {
if (!error_handler.initialized) {

You might also like