Skip to content

Commit b670d5b

Browse files
[FL-3885] Put errno into TCB (#3893)
* feat: thread-safe errno * ci: fix pvs warning * ci: silence pvs warning * fix: 🤯 * test: convert test app into a unit test
1 parent 0428e82 commit b670d5b

File tree

3 files changed

+67
-3
lines changed

3 files changed

+67
-3
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include <furi.h>
2+
#include <errno.h>
3+
#include "../test.h" // IWYU pragma: keep
4+
5+
#define TAG "ErrnoTest"
6+
#define THREAD_CNT 16
7+
#define ITER_CNT 1000
8+
9+
static int32_t errno_fuzzer(void* context) {
10+
int start_value = (int)context;
11+
int32_t fails = 0;
12+
13+
for(int i = start_value; i < start_value + ITER_CNT; i++) {
14+
errno = i;
15+
furi_thread_yield();
16+
if(errno != i) fails++;
17+
}
18+
19+
for(int i = 0; i < ITER_CNT; i++) {
20+
errno = 0;
21+
furi_thread_yield();
22+
UNUSED(strtol("123456", NULL, 10)); // -V530
23+
furi_thread_yield();
24+
if(errno != 0) fails++;
25+
26+
errno = 0;
27+
furi_thread_yield();
28+
UNUSED(strtol("123456123456123456123456123456123456123456123456", NULL, 10)); // -V530
29+
furi_thread_yield();
30+
if(errno != ERANGE) fails++;
31+
}
32+
33+
return fails;
34+
}
35+
36+
void test_errno_saving(void) {
37+
FuriThread* threads[THREAD_CNT];
38+
39+
for(int i = 0; i < THREAD_CNT; i++) {
40+
int start_value = i * ITER_CNT;
41+
threads[i] = furi_thread_alloc_ex("ErrnoFuzzer", 1024, errno_fuzzer, (void*)start_value);
42+
furi_thread_set_priority(threads[i], FuriThreadPriorityNormal);
43+
furi_thread_start(threads[i]);
44+
}
45+
46+
for(int i = 0; i < THREAD_CNT; i++) {
47+
furi_thread_join(threads[i]);
48+
mu_assert_int_eq(0, furi_thread_get_return_code(threads[i]));
49+
furi_thread_free(threads[i]);
50+
}
51+
}

applications/debug/unit_tests/tests/furi/furi_test.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ void test_furi_concurrent_access(void);
88
void test_furi_pubsub(void);
99
void test_furi_memmgr(void);
1010
void test_furi_event_loop(void);
11+
void test_errno_saving(void);
1112

1213
static int foo = 0;
1314

@@ -42,6 +43,10 @@ MU_TEST(mu_test_furi_event_loop) {
4243
test_furi_event_loop();
4344
}
4445

46+
MU_TEST(mu_test_errno_saving) {
47+
test_errno_saving();
48+
}
49+
4550
MU_TEST_SUITE(test_suite) {
4651
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
4752
MU_RUN_TEST(test_check);
@@ -51,6 +56,7 @@ MU_TEST_SUITE(test_suite) {
5156
MU_RUN_TEST(mu_test_furi_pubsub);
5257
MU_RUN_TEST(mu_test_furi_memmgr);
5358
MU_RUN_TEST(mu_test_furi_event_loop);
59+
MU_RUN_TEST(mu_test_errno_saving);
5460
}
5561

5662
int run_minunit_test_furi(void) {

targets/f7/inc/FreeRTOSConfig.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
44
#include <stdint.h>
5+
#include <errno.h>
56
#pragma GCC diagnostic ignored "-Wredundant-decls"
67
#endif
78

@@ -26,6 +27,7 @@
2627
#define configUSE_16_BIT_TICKS 0
2728
#define configMAX_PRIORITIES (32)
2829
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
30+
#define configUSE_POSIX_ERRNO 1
2931

3032
/* Heap size determined automatically by linker */
3133
// #define configTOTAL_HEAP_SIZE ((size_t)0)
@@ -146,9 +148,14 @@ standard names. */
146148
#define configOVERRIDE_DEFAULT_TICK_CONFIGURATION \
147149
1 /* required only for Keil but does not hurt otherwise */
148150

149-
#define traceTASK_SWITCHED_IN() \
150-
extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \
151-
furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack)
151+
#define traceTASK_SWITCHED_IN() \
152+
extern void furi_hal_mpu_set_stack_protection(uint32_t* stack); \
153+
furi_hal_mpu_set_stack_protection((uint32_t*)pxCurrentTCB->pxStack); \
154+
errno = pxCurrentTCB->iTaskErrno
155+
// ^^^^^ acquire errno directly from TCB because FreeRTOS assigns its `FreeRTOS_errno' _after_ our hook is called
156+
157+
// referencing `FreeRTOS_errno' here vvvvv because FreeRTOS calls our hook _before_ copying the value into the TCB, hence a manual write to the TCB would get overwritten
158+
#define traceTASK_SWITCHED_OUT() FreeRTOS_errno = errno
152159

153160
#define portCLEAN_UP_TCB(pxTCB) \
154161
extern void furi_thread_cleanup_tcb_event(TaskHandle_t task); \

0 commit comments

Comments
 (0)