thread.c 17 KB

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