thread.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. #include "thread.h"
  2. #include "kernel.h"
  3. #include "memmgr.h"
  4. #include "memmgr_heap.h"
  5. #include "check.h"
  6. #include "common_defines.h"
  7. #include "mutex.h"
  8. #include "string.h"
  9. #include <task.h>
  10. #include "log.h"
  11. #include <furi_hal_rtc.h>
  12. #include <furi_hal_console.h>
  13. #define TAG "FuriThread"
  14. #define THREAD_NOTIFY_INDEX 1 // Index 0 is used for stream buffers
  15. typedef struct FuriThreadStdout FuriThreadStdout;
  16. struct FuriThreadStdout {
  17. FuriThreadStdoutWriteCallback write_callback;
  18. FuriString* buffer;
  19. };
  20. struct FuriThread {
  21. FuriThreadState state;
  22. int32_t ret;
  23. FuriThreadCallback callback;
  24. void* context;
  25. FuriThreadStateCallback state_callback;
  26. void* state_context;
  27. char* name;
  28. char* appid;
  29. FuriThreadPriority priority;
  30. TaskHandle_t task_handle;
  31. size_t heap_size;
  32. FuriThreadStdout output;
  33. // Keep all non-alignable byte types in one place,
  34. // this ensures that the size of this structure is minimal
  35. bool is_service;
  36. bool heap_trace_enabled;
  37. configSTACK_DEPTH_TYPE stack_size;
  38. };
  39. static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size);
  40. static int32_t __furi_thread_stdout_flush(FuriThread* thread);
  41. /** Catch threads that are trying to exit wrong way */
  42. __attribute__((__noreturn__)) void furi_thread_catch() { //-V1082
  43. asm volatile("nop"); // extra magic
  44. furi_crash("You are doing it wrong"); //-V779
  45. __builtin_unreachable();
  46. }
  47. static void furi_thread_set_state(FuriThread* thread, FuriThreadState state) {
  48. furi_assert(thread);
  49. thread->state = state;
  50. if(thread->state_callback) {
  51. thread->state_callback(state, thread->state_context);
  52. }
  53. }
  54. static void furi_thread_body(void* context) {
  55. furi_assert(context);
  56. FuriThread* thread = context;
  57. // store thread instance to thread local storage
  58. furi_assert(pvTaskGetThreadLocalStoragePointer(NULL, 0) == NULL);
  59. vTaskSetThreadLocalStoragePointer(NULL, 0, thread);
  60. furi_assert(thread->state == FuriThreadStateStarting);
  61. furi_thread_set_state(thread, FuriThreadStateRunning);
  62. TaskHandle_t task_handle = xTaskGetCurrentTaskHandle();
  63. if(thread->heap_trace_enabled == true) {
  64. memmgr_heap_enable_thread_trace((FuriThreadId)task_handle);
  65. }
  66. thread->ret = thread->callback(thread->context);
  67. if(thread->heap_trace_enabled == true) {
  68. furi_delay_ms(33);
  69. thread->heap_size = memmgr_heap_get_thread_memory((FuriThreadId)task_handle);
  70. furi_log_print_format( //-V576
  71. thread->heap_size ? FuriLogLevelError : FuriLogLevelInfo,
  72. TAG,
  73. "%s allocation balance: %u",
  74. thread->name ? thread->name : "Thread",
  75. thread->heap_size);
  76. memmgr_heap_disable_thread_trace((FuriThreadId)task_handle);
  77. }
  78. furi_assert(thread->state == FuriThreadStateRunning);
  79. if(thread->is_service) {
  80. FURI_LOG_W(
  81. TAG,
  82. "%s service thread TCB memory will not be reclaimed",
  83. thread->name ? thread->name : "<unknown service>");
  84. }
  85. // flush stdout
  86. __furi_thread_stdout_flush(thread);
  87. furi_thread_set_state(thread, FuriThreadStateStopped);
  88. vTaskDelete(NULL);
  89. furi_thread_catch();
  90. }
  91. FuriThread* furi_thread_alloc() {
  92. FuriThread* thread = malloc(sizeof(FuriThread));
  93. thread->output.buffer = furi_string_alloc();
  94. thread->is_service = false;
  95. FuriThread* parent = NULL;
  96. if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
  97. // TLS is not available, if we called not from thread context
  98. parent = pvTaskGetThreadLocalStoragePointer(NULL, 0);
  99. if(parent && parent->appid) {
  100. furi_thread_set_appid(thread, parent->appid);
  101. } else {
  102. furi_thread_set_appid(thread, "unknown");
  103. }
  104. } else {
  105. // if scheduler is not started, we are starting driver thread
  106. furi_thread_set_appid(thread, "driver");
  107. }
  108. FuriHalRtcHeapTrackMode mode = furi_hal_rtc_get_heap_track_mode();
  109. if(mode == FuriHalRtcHeapTrackModeAll) {
  110. thread->heap_trace_enabled = true;
  111. } else if(mode == FuriHalRtcHeapTrackModeTree && furi_thread_get_current_id()) {
  112. if(parent) thread->heap_trace_enabled = parent->heap_trace_enabled;
  113. } else {
  114. thread->heap_trace_enabled = false;
  115. }
  116. return thread;
  117. }
  118. FuriThread* furi_thread_alloc_ex(
  119. const char* name,
  120. uint32_t stack_size,
  121. FuriThreadCallback callback,
  122. void* context) {
  123. FuriThread* thread = furi_thread_alloc();
  124. furi_thread_set_name(thread, name);
  125. furi_thread_set_stack_size(thread, stack_size);
  126. furi_thread_set_callback(thread, callback);
  127. furi_thread_set_context(thread, context);
  128. return thread;
  129. }
  130. void furi_thread_free(FuriThread* thread) {
  131. furi_assert(thread);
  132. furi_assert(thread->state == FuriThreadStateStopped);
  133. if(thread->name) free((void*)thread->name);
  134. if(thread->appid) free((void*)thread->appid);
  135. furi_string_free(thread->output.buffer);
  136. free(thread);
  137. }
  138. void furi_thread_set_name(FuriThread* thread, const char* name) {
  139. furi_assert(thread);
  140. furi_assert(thread->state == FuriThreadStateStopped);
  141. if(thread->name) free((void*)thread->name);
  142. thread->name = name ? strdup(name) : NULL;
  143. }
  144. void furi_thread_set_appid(FuriThread* thread, const char* appid) {
  145. furi_assert(thread);
  146. furi_assert(thread->state == FuriThreadStateStopped);
  147. if(thread->appid) free((void*)thread->appid);
  148. thread->appid = appid ? strdup(appid) : NULL;
  149. }
  150. void furi_thread_mark_as_service(FuriThread* thread) {
  151. thread->is_service = true;
  152. }
  153. void furi_thread_set_stack_size(FuriThread* thread, size_t stack_size) {
  154. furi_assert(thread);
  155. furi_assert(thread->state == FuriThreadStateStopped);
  156. furi_assert(stack_size % 4 == 0);
  157. thread->stack_size = stack_size;
  158. }
  159. void furi_thread_set_callback(FuriThread* thread, FuriThreadCallback callback) {
  160. furi_assert(thread);
  161. furi_assert(thread->state == FuriThreadStateStopped);
  162. thread->callback = callback;
  163. }
  164. void furi_thread_set_context(FuriThread* thread, void* context) {
  165. furi_assert(thread);
  166. furi_assert(thread->state == FuriThreadStateStopped);
  167. thread->context = context;
  168. }
  169. void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority) {
  170. furi_assert(thread);
  171. furi_assert(thread->state == FuriThreadStateStopped);
  172. furi_assert(priority >= FuriThreadPriorityIdle && priority <= FuriThreadPriorityIsr);
  173. thread->priority = priority;
  174. }
  175. void furi_thread_set_current_priority(FuriThreadPriority priority) {
  176. UBaseType_t new_priority = priority ? priority : FuriThreadPriorityNormal;
  177. vTaskPrioritySet(NULL, new_priority);
  178. }
  179. FuriThreadPriority furi_thread_get_current_priority() {
  180. return (FuriThreadPriority)uxTaskPriorityGet(NULL);
  181. }
  182. void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) {
  183. furi_assert(thread);
  184. furi_assert(thread->state == FuriThreadStateStopped);
  185. thread->state_callback = callback;
  186. }
  187. void furi_thread_set_state_context(FuriThread* thread, void* context) {
  188. furi_assert(thread);
  189. furi_assert(thread->state == FuriThreadStateStopped);
  190. thread->state_context = context;
  191. }
  192. FuriThreadState furi_thread_get_state(FuriThread* thread) {
  193. furi_assert(thread);
  194. return thread->state;
  195. }
  196. void furi_thread_start(FuriThread* thread) {
  197. furi_assert(thread);
  198. furi_assert(thread->callback);
  199. furi_assert(thread->state == FuriThreadStateStopped);
  200. furi_assert(thread->stack_size > 0 && thread->stack_size < (UINT16_MAX * sizeof(StackType_t)));
  201. furi_thread_set_state(thread, FuriThreadStateStarting);
  202. uint32_t stack = thread->stack_size / sizeof(StackType_t);
  203. UBaseType_t priority = thread->priority ? thread->priority : FuriThreadPriorityNormal;
  204. if(thread->is_service) {
  205. thread->task_handle = xTaskCreateStatic(
  206. furi_thread_body,
  207. thread->name,
  208. stack,
  209. thread,
  210. priority,
  211. memmgr_alloc_from_pool(sizeof(StackType_t) * stack),
  212. memmgr_alloc_from_pool(sizeof(StaticTask_t)));
  213. } else {
  214. BaseType_t ret = xTaskCreate(
  215. furi_thread_body, thread->name, stack, thread, priority, &thread->task_handle);
  216. furi_check(ret == pdPASS);
  217. }
  218. furi_check(thread->task_handle);
  219. }
  220. void furi_thread_cleanup_tcb_event(TaskHandle_t task) {
  221. FuriThread* thread = pvTaskGetThreadLocalStoragePointer(task, 0);
  222. if(thread) {
  223. // clear thread local storage
  224. vTaskSetThreadLocalStoragePointer(task, 0, NULL);
  225. thread->task_handle = NULL;
  226. }
  227. }
  228. bool furi_thread_join(FuriThread* thread) {
  229. furi_assert(thread);
  230. furi_check(furi_thread_get_current() != thread);
  231. // !!! IMPORTANT NOTICE !!!
  232. //
  233. // If your thread exited, but your app stuck here: some other thread uses
  234. // all cpu time, which delays kernel from releasing task handle
  235. while(thread->task_handle) {
  236. furi_delay_ms(10);
  237. }
  238. return true;
  239. }
  240. FuriThreadId furi_thread_get_id(FuriThread* thread) {
  241. furi_assert(thread);
  242. return thread->task_handle;
  243. }
  244. void furi_thread_enable_heap_trace(FuriThread* thread) {
  245. furi_assert(thread);
  246. furi_assert(thread->state == FuriThreadStateStopped);
  247. thread->heap_trace_enabled = true;
  248. }
  249. void furi_thread_disable_heap_trace(FuriThread* thread) {
  250. furi_assert(thread);
  251. furi_assert(thread->state == FuriThreadStateStopped);
  252. thread->heap_trace_enabled = false;
  253. }
  254. size_t furi_thread_get_heap_size(FuriThread* thread) {
  255. furi_assert(thread);
  256. furi_assert(thread->heap_trace_enabled == true);
  257. return thread->heap_size;
  258. }
  259. int32_t furi_thread_get_return_code(FuriThread* thread) {
  260. furi_assert(thread);
  261. furi_assert(thread->state == FuriThreadStateStopped);
  262. return thread->ret;
  263. }
  264. FuriThreadId furi_thread_get_current_id() {
  265. return xTaskGetCurrentTaskHandle();
  266. }
  267. FuriThread* furi_thread_get_current() {
  268. FuriThread* thread = pvTaskGetThreadLocalStoragePointer(NULL, 0);
  269. furi_assert(thread != NULL);
  270. return thread;
  271. }
  272. void furi_thread_yield() {
  273. furi_assert(!FURI_IS_IRQ_MODE());
  274. taskYIELD();
  275. }
  276. /* Limits */
  277. #define MAX_BITS_TASK_NOTIFY 31U
  278. #define MAX_BITS_EVENT_GROUPS 24U
  279. #define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U))
  280. #define EVENT_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U))
  281. uint32_t furi_thread_flags_set(FuriThreadId thread_id, uint32_t flags) {
  282. TaskHandle_t hTask = (TaskHandle_t)thread_id;
  283. uint32_t rflags;
  284. BaseType_t yield;
  285. if((hTask == NULL) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) {
  286. rflags = (uint32_t)FuriStatusErrorParameter;
  287. } else {
  288. rflags = (uint32_t)FuriStatusError;
  289. if(FURI_IS_IRQ_MODE()) {
  290. yield = pdFALSE;
  291. (void)xTaskNotifyIndexedFromISR(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits, &yield);
  292. (void)xTaskNotifyAndQueryIndexedFromISR(
  293. hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags, NULL);
  294. portYIELD_FROM_ISR(yield);
  295. } else {
  296. (void)xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, flags, eSetBits);
  297. (void)xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags);
  298. }
  299. }
  300. /* Return flags after setting */
  301. return (rflags);
  302. }
  303. uint32_t furi_thread_flags_clear(uint32_t flags) {
  304. TaskHandle_t hTask;
  305. uint32_t rflags, cflags;
  306. if(FURI_IS_IRQ_MODE()) {
  307. rflags = (uint32_t)FuriStatusErrorISR;
  308. } else if((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
  309. rflags = (uint32_t)FuriStatusErrorParameter;
  310. } else {
  311. hTask = xTaskGetCurrentTaskHandle();
  312. if(xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &cflags) ==
  313. pdPASS) {
  314. rflags = cflags;
  315. cflags &= ~flags;
  316. if(xTaskNotifyIndexed(hTask, THREAD_NOTIFY_INDEX, cflags, eSetValueWithOverwrite) !=
  317. pdPASS) {
  318. rflags = (uint32_t)FuriStatusError;
  319. }
  320. } else {
  321. rflags = (uint32_t)FuriStatusError;
  322. }
  323. }
  324. /* Return flags before clearing */
  325. return (rflags);
  326. }
  327. uint32_t furi_thread_flags_get(void) {
  328. TaskHandle_t hTask;
  329. uint32_t rflags;
  330. if(FURI_IS_IRQ_MODE()) {
  331. rflags = (uint32_t)FuriStatusErrorISR;
  332. } else {
  333. hTask = xTaskGetCurrentTaskHandle();
  334. if(xTaskNotifyAndQueryIndexed(hTask, THREAD_NOTIFY_INDEX, 0, eNoAction, &rflags) !=
  335. pdPASS) {
  336. rflags = (uint32_t)FuriStatusError;
  337. }
  338. }
  339. return (rflags);
  340. }
  341. uint32_t furi_thread_flags_wait(uint32_t flags, uint32_t options, uint32_t timeout) {
  342. uint32_t rflags, nval;
  343. uint32_t clear;
  344. TickType_t t0, td, tout;
  345. BaseType_t rval;
  346. if(FURI_IS_IRQ_MODE()) {
  347. rflags = (uint32_t)FuriStatusErrorISR;
  348. } else if((flags & THREAD_FLAGS_INVALID_BITS) != 0U) {
  349. rflags = (uint32_t)FuriStatusErrorParameter;
  350. } else {
  351. if((options & FuriFlagNoClear) == FuriFlagNoClear) {
  352. clear = 0U;
  353. } else {
  354. clear = flags;
  355. }
  356. rflags = 0U;
  357. tout = timeout;
  358. t0 = xTaskGetTickCount();
  359. do {
  360. rval = xTaskNotifyWaitIndexed(THREAD_NOTIFY_INDEX, 0, clear, &nval, tout);
  361. if(rval == pdPASS) {
  362. rflags &= flags;
  363. rflags |= nval;
  364. if((options & FuriFlagWaitAll) == FuriFlagWaitAll) {
  365. if((flags & rflags) == flags) {
  366. break;
  367. } else {
  368. if(timeout == 0U) {
  369. rflags = (uint32_t)FuriStatusErrorResource;
  370. break;
  371. }
  372. }
  373. } else {
  374. if((flags & rflags) != 0) {
  375. break;
  376. } else {
  377. if(timeout == 0U) {
  378. rflags = (uint32_t)FuriStatusErrorResource;
  379. break;
  380. }
  381. }
  382. }
  383. /* Update timeout */
  384. td = xTaskGetTickCount() - t0;
  385. if(td > tout) {
  386. tout = 0;
  387. } else {
  388. tout -= td;
  389. }
  390. } else {
  391. if(timeout == 0) {
  392. rflags = (uint32_t)FuriStatusErrorResource;
  393. } else {
  394. rflags = (uint32_t)FuriStatusErrorTimeout;
  395. }
  396. }
  397. } while(rval != pdFAIL);
  398. }
  399. /* Return flags before clearing */
  400. return (rflags);
  401. }
  402. uint32_t furi_thread_enumerate(FuriThreadId* thread_array, uint32_t array_items) {
  403. uint32_t i, count;
  404. TaskStatus_t* task;
  405. if(FURI_IS_IRQ_MODE() || (thread_array == NULL) || (array_items == 0U)) {
  406. count = 0U;
  407. } else {
  408. vTaskSuspendAll();
  409. count = uxTaskGetNumberOfTasks();
  410. task = pvPortMalloc(count * sizeof(TaskStatus_t));
  411. if(task != NULL) {
  412. count = uxTaskGetSystemState(task, count, NULL);
  413. for(i = 0U; (i < count) && (i < array_items); i++) {
  414. thread_array[i] = (FuriThreadId)task[i].xHandle;
  415. }
  416. count = i;
  417. }
  418. (void)xTaskResumeAll();
  419. vPortFree(task);
  420. }
  421. return (count);
  422. }
  423. const char* furi_thread_get_name(FuriThreadId thread_id) {
  424. TaskHandle_t hTask = (TaskHandle_t)thread_id;
  425. const char* name;
  426. if(FURI_IS_IRQ_MODE() || (hTask == NULL)) {
  427. name = NULL;
  428. } else {
  429. name = pcTaskGetName(hTask);
  430. }
  431. return (name);
  432. }
  433. const char* furi_thread_get_appid(FuriThreadId thread_id) {
  434. TaskHandle_t hTask = (TaskHandle_t)thread_id;
  435. const char* appid = "system";
  436. if(!FURI_IS_IRQ_MODE() && (hTask != NULL)) {
  437. FuriThread* thread = (FuriThread*)pvTaskGetThreadLocalStoragePointer(hTask, 0);
  438. if(thread) {
  439. appid = thread->appid;
  440. }
  441. }
  442. return (appid);
  443. }
  444. uint32_t furi_thread_get_stack_space(FuriThreadId thread_id) {
  445. TaskHandle_t hTask = (TaskHandle_t)thread_id;
  446. uint32_t sz;
  447. if(FURI_IS_IRQ_MODE() || (hTask == NULL)) {
  448. sz = 0U;
  449. } else {
  450. sz = (uint32_t)(uxTaskGetStackHighWaterMark(hTask) * sizeof(StackType_t));
  451. }
  452. return (sz);
  453. }
  454. static size_t __furi_thread_stdout_write(FuriThread* thread, const char* data, size_t size) {
  455. if(thread->output.write_callback != NULL) {
  456. thread->output.write_callback(data, size);
  457. } else {
  458. furi_hal_console_tx((const uint8_t*)data, size);
  459. }
  460. return size;
  461. }
  462. static int32_t __furi_thread_stdout_flush(FuriThread* thread) {
  463. FuriString* buffer = thread->output.buffer;
  464. size_t size = furi_string_size(buffer);
  465. if(size > 0) {
  466. __furi_thread_stdout_write(thread, furi_string_get_cstr(buffer), size);
  467. furi_string_reset(buffer);
  468. }
  469. return 0;
  470. }
  471. bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) {
  472. FuriThread* thread = furi_thread_get_current();
  473. __furi_thread_stdout_flush(thread);
  474. thread->output.write_callback = callback;
  475. return true;
  476. }
  477. FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback() {
  478. FuriThread* thread = furi_thread_get_current();
  479. return thread->output.write_callback;
  480. }
  481. size_t furi_thread_stdout_write(const char* data, size_t size) {
  482. FuriThread* thread = furi_thread_get_current();
  483. if(size == 0 || data == NULL) {
  484. return __furi_thread_stdout_flush(thread);
  485. } else {
  486. if(data[size - 1] == '\n') {
  487. // if the last character is a newline, we can flush buffer and write data as is, wo buffers
  488. __furi_thread_stdout_flush(thread);
  489. __furi_thread_stdout_write(thread, data, size);
  490. } else {
  491. // string_cat doesn't work here because we need to write the exact size data
  492. for(size_t i = 0; i < size; i++) {
  493. furi_string_push_back(thread->output.buffer, data[i]);
  494. if(data[i] == '\n') {
  495. __furi_thread_stdout_flush(thread);
  496. }
  497. }
  498. }
  499. }
  500. return size;
  501. }
  502. int32_t furi_thread_stdout_flush() {
  503. return __furi_thread_stdout_flush(furi_thread_get_current());
  504. }
  505. void furi_thread_suspend(FuriThreadId thread_id) {
  506. TaskHandle_t hTask = (TaskHandle_t)thread_id;
  507. vTaskSuspend(hTask);
  508. }
  509. void furi_thread_resume(FuriThreadId thread_id) {
  510. TaskHandle_t hTask = (TaskHandle_t)thread_id;
  511. if(FURI_IS_IRQ_MODE()) {
  512. xTaskResumeFromISR(hTask);
  513. } else {
  514. vTaskResume(hTask);
  515. }
  516. }
  517. bool furi_thread_is_suspended(FuriThreadId thread_id) {
  518. TaskHandle_t hTask = (TaskHandle_t)thread_id;
  519. return eTaskGetState(hTask) == eSuspended;
  520. }