thread.c 18 KB

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