pthread.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. // Copyright 2017 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. //
  14. // This module implements pthread API on top of FreeRTOS. API is implemented to the level allowing
  15. // libstdcxx threading framework to operate correctly. So not all original pthread routines are supported.
  16. // Moreover some implemened functions do not provide full functionality, e.g. pthread_create does not support
  17. // thread's attributes customization (prio, stack size and so on). So if you are not satisfied with default
  18. // behavior use native FreeRTOS API.
  19. //
  20. #include <errno.h>
  21. #include <pthread.h>
  22. #include <string.h>
  23. #include "esp_err.h"
  24. #include "esp_attr.h"
  25. #include "rom/queue.h"
  26. #include "freertos/FreeRTOS.h"
  27. #include "freertos/task.h"
  28. #include "freertos/semphr.h"
  29. #include "pthread_internal.h"
  30. #define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL
  31. #include "esp_log.h"
  32. const static char *TAG = "pthread";
  33. /** task state */
  34. enum esp_pthread_task_state {
  35. PTHREAD_TASK_STATE_RUN,
  36. PTHREAD_TASK_STATE_EXIT
  37. };
  38. /** pthread thread FreeRTOS wrapper */
  39. typedef struct esp_pthread_entry {
  40. SLIST_ENTRY(esp_pthread_entry) list_node; ///< Tasks list node struct.
  41. TaskHandle_t handle; ///< FreeRTOS task handle
  42. TaskHandle_t join_task; ///< Handle of the task waiting to join
  43. enum esp_pthread_task_state state; ///< pthread task state
  44. bool detached; ///< True if pthread is detached
  45. } esp_pthread_t;
  46. /** pthread wrapper task arg */
  47. typedef struct {
  48. void *(*func)(void *); ///< user task entry
  49. void *arg; ///< user task argument
  50. } esp_pthread_task_arg_t;
  51. /** pthread mutex FreeRTOS wrapper */
  52. typedef struct {
  53. SemaphoreHandle_t sem; ///< Handle of the task waiting to join
  54. int type; ///< Mutex type. Currently supported PTHREAD_MUTEX_NORMAL and PTHREAD_MUTEX_RECURSIVE
  55. } esp_pthread_mutex_t;
  56. static SemaphoreHandle_t s_threads_mux = NULL;
  57. static portMUX_TYPE s_mutex_init_lock = portMUX_INITIALIZER_UNLOCKED;
  58. static SLIST_HEAD(esp_thread_list_head, esp_pthread_entry) s_threads_list
  59. = SLIST_HEAD_INITIALIZER(s_threads_list);
  60. static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo);
  61. esp_err_t esp_pthread_init(void)
  62. {
  63. s_threads_mux = xSemaphoreCreateMutex();
  64. if (s_threads_mux == NULL) {
  65. return ESP_ERR_NO_MEM;
  66. }
  67. return ESP_OK;
  68. }
  69. static void *pthread_list_find_item(void *(*item_check)(esp_pthread_t *, void *arg), void *check_arg)
  70. {
  71. esp_pthread_t *it;
  72. SLIST_FOREACH(it, &s_threads_list, list_node) {
  73. void *val = item_check(it, check_arg);
  74. if (val) {
  75. return val;
  76. }
  77. }
  78. return NULL;
  79. }
  80. static void *pthread_get_handle_by_desc(esp_pthread_t *item, void *desc)
  81. {
  82. if (item == desc) {
  83. return item->handle;
  84. }
  85. return NULL;
  86. }
  87. static void *pthread_get_desc_by_handle(esp_pthread_t *item, void *hnd)
  88. {
  89. if (hnd == item->handle) {
  90. return item;
  91. }
  92. return NULL;
  93. }
  94. static inline TaskHandle_t pthread_find_handle(pthread_t thread)
  95. {
  96. return pthread_list_find_item(pthread_get_handle_by_desc, (void *)thread);
  97. }
  98. static esp_pthread_t *pthread_find(TaskHandle_t task_handle)
  99. {
  100. return pthread_list_find_item(pthread_get_desc_by_handle, task_handle);
  101. }
  102. static void pthread_delete(esp_pthread_t *pthread)
  103. {
  104. SLIST_REMOVE(&s_threads_list, pthread, esp_pthread_entry, list_node);
  105. free(pthread);
  106. }
  107. static void pthread_task_func(void *arg)
  108. {
  109. esp_pthread_task_arg_t *task_arg = (esp_pthread_task_arg_t *)arg;
  110. ESP_LOGV(TAG, "%s ENTER %p", __FUNCTION__, task_arg->func);
  111. // wait for start
  112. xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
  113. ESP_LOGV(TAG, "%s START %p", __FUNCTION__, task_arg->func);
  114. task_arg->func(task_arg->arg);
  115. ESP_LOGV(TAG, "%s END %p", __FUNCTION__, task_arg->func);
  116. free(task_arg);
  117. /* preemptively clean up thread local storage, rather than
  118. waiting for the idle task to clean up the thread */
  119. pthread_internal_local_storage_destructor_callback();
  120. if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
  121. assert(false && "Failed to lock threads list!");
  122. }
  123. esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle());
  124. if (!pthread) {
  125. assert(false && "Failed to find pthread for current task!");
  126. }
  127. if (pthread->detached) {
  128. // auto-free for detached threads
  129. pthread_delete(pthread);
  130. } else {
  131. // Remove from list, it indicates that task has exited
  132. if (pthread->join_task) {
  133. // notify join
  134. xTaskNotify(pthread->join_task, 0, eNoAction);
  135. } else {
  136. pthread->state = PTHREAD_TASK_STATE_EXIT;
  137. }
  138. }
  139. xSemaphoreGive(s_threads_mux);
  140. ESP_LOGD(TAG, "Task stk_wm = %d", uxTaskGetStackHighWaterMark(NULL));
  141. vTaskDelete(NULL);
  142. ESP_LOGV(TAG, "%s EXIT", __FUNCTION__);
  143. }
  144. int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
  145. void *(*start_routine) (void *), void *arg)
  146. {
  147. TaskHandle_t xHandle = NULL;
  148. ESP_LOGV(TAG, "%s", __FUNCTION__);
  149. if (attr) {
  150. ESP_LOGE(TAG, "%s: attrs not supported!", __FUNCTION__);
  151. return ENOSYS;
  152. }
  153. esp_pthread_task_arg_t *task_arg = malloc(sizeof(esp_pthread_task_arg_t));
  154. if (task_arg == NULL) {
  155. ESP_LOGE(TAG, "Failed to allocate task args!");
  156. return ENOMEM;
  157. }
  158. memset(task_arg, 0, sizeof(esp_pthread_task_arg_t));
  159. esp_pthread_t *pthread = malloc(sizeof(esp_pthread_t));
  160. if (pthread == NULL) {
  161. ESP_LOGE(TAG, "Failed to allocate pthread data!");
  162. free(task_arg);
  163. return ENOMEM;
  164. }
  165. memset(pthread, 0, sizeof(esp_pthread_t));
  166. task_arg->func = start_routine;
  167. task_arg->arg = arg;
  168. BaseType_t res = xTaskCreate(&pthread_task_func, "pthread", CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT,
  169. task_arg, CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT, &xHandle);
  170. if(res != pdPASS) {
  171. ESP_LOGE(TAG, "Failed to create task!");
  172. free(pthread);
  173. free(task_arg);
  174. if (res == errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY) {
  175. return ENOMEM;
  176. } else {
  177. return EAGAIN;
  178. }
  179. }
  180. pthread->handle = xHandle;
  181. if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
  182. assert(false && "Failed to lock threads list!");
  183. }
  184. SLIST_INSERT_HEAD(&s_threads_list, pthread, list_node);
  185. xSemaphoreGive(s_threads_mux);
  186. // start task
  187. xTaskNotify(xHandle, 0, eNoAction);
  188. *thread = (pthread_t)pthread; // pointer value fit into pthread_t (uint32_t)
  189. ESP_LOGV(TAG, "Created task %x", (uint32_t)xHandle);
  190. return 0;
  191. }
  192. int pthread_join(pthread_t thread, void **retval)
  193. {
  194. esp_pthread_t *pthread = (esp_pthread_t *)thread;
  195. int ret = 0;
  196. bool wait = false;
  197. ESP_LOGV(TAG, "%s %p", __FUNCTION__, pthread);
  198. // find task
  199. if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
  200. assert(false && "Failed to lock threads list!");
  201. }
  202. TaskHandle_t handle = pthread_find_handle(thread);
  203. if (!handle) {
  204. // not found
  205. ret = ESRCH;
  206. } else if (pthread->join_task) {
  207. // already have waiting task to join
  208. ret = EINVAL;
  209. } else if (handle == xTaskGetCurrentTaskHandle()) {
  210. // join to self not allowed
  211. ret = EDEADLK;
  212. } else {
  213. esp_pthread_t *cur_pthread = pthread_find(xTaskGetCurrentTaskHandle());
  214. if (cur_pthread && cur_pthread->join_task == handle) {
  215. // join to each other not allowed
  216. ret = EDEADLK;
  217. } else {
  218. if (pthread->state == PTHREAD_TASK_STATE_RUN) {
  219. pthread->join_task = xTaskGetCurrentTaskHandle();
  220. wait = true;
  221. } else {
  222. pthread_delete(pthread);
  223. }
  224. }
  225. }
  226. xSemaphoreGive(s_threads_mux);
  227. if (ret == 0 && wait) {
  228. xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);
  229. if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
  230. assert(false && "Failed to lock threads list!");
  231. }
  232. pthread_delete(pthread);
  233. xSemaphoreGive(s_threads_mux);
  234. }
  235. if (retval) {
  236. *retval = 0; // no exit code in FreeRTOS
  237. }
  238. ESP_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret);
  239. return ret;
  240. }
  241. int pthread_detach(pthread_t thread)
  242. {
  243. esp_pthread_t *pthread = (esp_pthread_t *)thread;
  244. int ret = 0;
  245. if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
  246. assert(false && "Failed to lock threads list!");
  247. }
  248. TaskHandle_t handle = pthread_find_handle(thread);
  249. if (!handle) {
  250. ret = ESRCH;
  251. } else {
  252. pthread->detached = true;
  253. }
  254. xSemaphoreGive(s_threads_mux);
  255. ESP_LOGV(TAG, "%s %p EXIT %d", __FUNCTION__, pthread, ret);
  256. return ret;
  257. }
  258. int pthread_cancel(pthread_t thread)
  259. {
  260. ESP_LOGE(TAG, "%s: not supported!", __FUNCTION__);
  261. return ENOSYS;
  262. }
  263. int sched_yield( void )
  264. {
  265. vTaskDelay(0);
  266. return 0;
  267. }
  268. pthread_t pthread_self(void)
  269. {
  270. if (xSemaphoreTake(s_threads_mux, portMAX_DELAY) != pdTRUE) {
  271. assert(false && "Failed to lock threads list!");
  272. }
  273. esp_pthread_t *pthread = pthread_find(xTaskGetCurrentTaskHandle());
  274. if (!pthread) {
  275. assert(false && "Failed to find current thread ID!");
  276. }
  277. xSemaphoreGive(s_threads_mux);
  278. return (pthread_t)pthread;
  279. }
  280. int pthread_equal(pthread_t t1, pthread_t t2)
  281. {
  282. return t1 == t2 ? 1 : 0;
  283. }
  284. /***************** ONCE ******************/
  285. int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
  286. {
  287. if (once_control == NULL || init_routine == NULL || !once_control->is_initialized) {
  288. ESP_LOGE(TAG, "%s: Invalid args!", __FUNCTION__);
  289. return EINVAL;
  290. }
  291. uint32_t res = 1;
  292. #if defined(CONFIG_SPIRAM_SUPPORT)
  293. if (esp_ptr_external_ram(once_control)) {
  294. uxPortCompareSetExtram((uint32_t *) &once_control->init_executed, 0, &res);
  295. } else {
  296. #endif
  297. uxPortCompareSet((uint32_t *) &once_control->init_executed, 0, &res);
  298. #if defined(CONFIG_SPIRAM_SUPPORT)
  299. }
  300. #endif
  301. // Check if compare and set was successful
  302. if (res == 0) {
  303. ESP_LOGV(TAG, "%s: call init_routine %p", __FUNCTION__, once_control);
  304. init_routine();
  305. }
  306. return 0;
  307. }
  308. /***************** MUTEX ******************/
  309. static int mutexattr_check(const pthread_mutexattr_t *attr)
  310. {
  311. if (attr->type < PTHREAD_MUTEX_NORMAL || attr->type > PTHREAD_MUTEX_RECURSIVE) {
  312. return EINVAL;
  313. }
  314. return 0;
  315. }
  316. int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
  317. {
  318. int type = PTHREAD_MUTEX_NORMAL;
  319. if (!mutex) {
  320. return EINVAL;
  321. }
  322. if (attr) {
  323. if (!attr->is_initialized) {
  324. return EINVAL;
  325. }
  326. int res = mutexattr_check(attr);
  327. if (res) {
  328. return res;
  329. }
  330. type = attr->type;
  331. }
  332. esp_pthread_mutex_t *mux = (esp_pthread_mutex_t *)malloc(sizeof(esp_pthread_mutex_t));
  333. if (!mux) {
  334. return ENOMEM;
  335. }
  336. mux->type = type;
  337. if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
  338. mux->sem = xSemaphoreCreateRecursiveMutex();
  339. } else {
  340. mux->sem = xSemaphoreCreateMutex();
  341. }
  342. if (!mux->sem) {
  343. free(mux);
  344. return EAGAIN;
  345. }
  346. *mutex = (pthread_mutex_t)mux; // pointer value fit into pthread_mutex_t (uint32_t)
  347. return 0;
  348. }
  349. int pthread_mutex_destroy(pthread_mutex_t *mutex)
  350. {
  351. esp_pthread_mutex_t *mux;
  352. ESP_LOGV(TAG, "%s %p", __FUNCTION__, mutex);
  353. if (!mutex) {
  354. return EINVAL;
  355. }
  356. mux = (esp_pthread_mutex_t *)*mutex;
  357. // check if mux is busy
  358. int res = pthread_mutex_lock_internal(mux, 0);
  359. if (res == EBUSY) {
  360. return EBUSY;
  361. }
  362. vSemaphoreDelete(mux->sem);
  363. free(mux);
  364. return 0;
  365. }
  366. static int IRAM_ATTR pthread_mutex_lock_internal(esp_pthread_mutex_t *mux, TickType_t tmo)
  367. {
  368. if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
  369. if (xSemaphoreTakeRecursive(mux->sem, tmo) != pdTRUE) {
  370. return EBUSY;
  371. }
  372. } else {
  373. if (xSemaphoreTake(mux->sem, tmo) != pdTRUE) {
  374. return EBUSY;
  375. }
  376. }
  377. return 0;
  378. }
  379. static int pthread_mutex_init_if_static(pthread_mutex_t *mutex) {
  380. int res = 0;
  381. if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
  382. portENTER_CRITICAL(&s_mutex_init_lock);
  383. if ((intptr_t) *mutex == PTHREAD_MUTEX_INITIALIZER) {
  384. res = pthread_mutex_init(mutex, NULL);
  385. }
  386. portEXIT_CRITICAL(&s_mutex_init_lock);
  387. }
  388. return res;
  389. }
  390. int IRAM_ATTR pthread_mutex_lock(pthread_mutex_t *mutex)
  391. {
  392. if (!mutex) {
  393. return EINVAL;
  394. }
  395. int res = pthread_mutex_init_if_static(mutex);
  396. if (res != 0) {
  397. return res;
  398. }
  399. return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, portMAX_DELAY);
  400. }
  401. int IRAM_ATTR pthread_mutex_trylock(pthread_mutex_t *mutex)
  402. {
  403. if (!mutex) {
  404. return EINVAL;
  405. }
  406. int res = pthread_mutex_init_if_static(mutex);
  407. if (res != 0) {
  408. return res;
  409. }
  410. return pthread_mutex_lock_internal((esp_pthread_mutex_t *)*mutex, 0);
  411. }
  412. int IRAM_ATTR pthread_mutex_unlock(pthread_mutex_t *mutex)
  413. {
  414. esp_pthread_mutex_t *mux;
  415. if (!mutex) {
  416. return EINVAL;
  417. }
  418. mux = (esp_pthread_mutex_t *)*mutex;
  419. if (mux->type == PTHREAD_MUTEX_RECURSIVE) {
  420. xSemaphoreGiveRecursive(mux->sem);
  421. } else {
  422. xSemaphoreGive(mux->sem);
  423. }
  424. return 0;
  425. }
  426. int pthread_mutexattr_init(pthread_mutexattr_t *attr)
  427. {
  428. if (!attr) {
  429. return EINVAL;
  430. }
  431. attr->type = PTHREAD_MUTEX_NORMAL;
  432. attr->is_initialized = 1;
  433. return 0;
  434. }
  435. int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
  436. {
  437. if (!attr) {
  438. return EINVAL;
  439. }
  440. attr->is_initialized = 0;
  441. return 0;
  442. }
  443. int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type)
  444. {
  445. ESP_LOGE(TAG, "%s: not supported!", __FUNCTION__);
  446. return ENOSYS;
  447. }
  448. int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
  449. {
  450. if (!attr) {
  451. return EINVAL;
  452. }
  453. pthread_mutexattr_t tmp_attr = {.type = type};
  454. int res = mutexattr_check(&tmp_attr);
  455. if (!res) {
  456. attr->type = type;
  457. }
  458. return res;
  459. }