lwp_futex.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * Copyright (c) 2006-2023, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2021/01/02 bernard the first version
  9. * 2023-07-25 Shell Remove usage of rt_hw_interrupt API in the lwp
  10. * Coding style: remove multiple `return` in a routine
  11. * 2023-08-08 Shell Fix return value of futex(wait); Fix ops that only
  12. * FUTEX_PRIVATE is supported currently
  13. */
  14. #define DBG_TAG "lwp.futex"
  15. #define DBG_LVL DBG_INFO
  16. #include <rtdbg.h>
  17. #include "lwp_internal.h"
  18. #include "lwp_pid.h"
  19. #include <rtthread.h>
  20. #include <lwp.h>
  21. #ifdef ARCH_MM_MMU
  22. #include <lwp_user_mm.h>
  23. #endif
  24. #include "sys/time.h"
  25. struct rt_futex
  26. {
  27. int *uaddr;
  28. rt_list_t waiting_thread;
  29. struct lwp_avl_struct node;
  30. struct rt_object *custom_obj;
  31. };
  32. /* must have futex address_search_head taken */
  33. static rt_err_t _futex_destroy_locked(void *data)
  34. {
  35. rt_err_t ret = -1;
  36. struct rt_futex *futex = (struct rt_futex *)data;
  37. if (futex)
  38. {
  39. /**
  40. * Brief: Delete the futex from lwp address_search_head
  41. *
  42. * Note: Critical Section
  43. * - the lwp (READ. share by thread)
  44. * - the lwp address_search_head (RW. protected by caller. for destroy
  45. * routine, it's always safe because it has already taken a write lock
  46. * to the lwp.)
  47. */
  48. lwp_avl_remove(&futex->node, (struct lwp_avl_struct **)futex->node.data);
  49. /* release object */
  50. rt_free(futex);
  51. ret = 0;
  52. }
  53. return ret;
  54. }
  55. /* must have futex address_search_head taken */
  56. static struct rt_futex *_futex_create_locked(int *uaddr, struct rt_lwp *lwp)
  57. {
  58. struct rt_futex *futex = RT_NULL;
  59. struct rt_object *obj = RT_NULL;
  60. /**
  61. * Brief: Create a futex under current lwp
  62. *
  63. * Note: Critical Section
  64. * - lwp (READ; share with thread)
  65. */
  66. if (lwp)
  67. {
  68. futex = (struct rt_futex *)rt_malloc(sizeof(struct rt_futex));
  69. if (futex)
  70. {
  71. obj = rt_custom_object_create("futex", (void *)futex, _futex_destroy_locked);
  72. if (!obj)
  73. {
  74. rt_free(futex);
  75. futex = RT_NULL;
  76. }
  77. else
  78. {
  79. /**
  80. * Brief: Add futex to user object tree for resource recycling
  81. *
  82. * Note: Critical Section
  83. * - lwp user object tree (RW; protected by API)
  84. * - futex (if the adding is successful, others can find the
  85. * unready futex. However, only the lwp_free will do this,
  86. * and this is protected by the ref taken by the lwp thread
  87. * that the lwp_free will never execute at the same time)
  88. */
  89. if (lwp_user_object_add(lwp, obj))
  90. {
  91. rt_object_delete(obj);
  92. rt_free(futex);
  93. futex = RT_NULL;
  94. }
  95. else
  96. {
  97. futex->uaddr = uaddr;
  98. futex->node.avl_key = (avl_key_t)uaddr;
  99. futex->node.data = &lwp->address_search_head;
  100. futex->custom_obj = obj;
  101. rt_list_init(&(futex->waiting_thread));
  102. /**
  103. * Brief: Insert into futex head
  104. *
  105. * Note: Critical Section
  106. * - lwp address_search_head (RW; protected by caller)
  107. */
  108. lwp_avl_insert(&futex->node, &lwp->address_search_head);
  109. }
  110. }
  111. }
  112. }
  113. return futex;
  114. }
  115. /* must have futex address_search_head taken */
  116. static struct rt_futex *_futex_get_locked(void *uaddr, struct rt_lwp *lwp)
  117. {
  118. struct rt_futex *futex = RT_NULL;
  119. struct lwp_avl_struct *node = RT_NULL;
  120. /**
  121. * Note: Critical Section
  122. * protect lwp address_search_head (READ)
  123. */
  124. node = lwp_avl_find((avl_key_t)uaddr, lwp->address_search_head);
  125. if (!node)
  126. {
  127. return RT_NULL;
  128. }
  129. futex = rt_container_of(node, struct rt_futex, node);
  130. return futex;
  131. }
  132. static int _futex_wait(struct rt_futex *futex, struct rt_lwp *lwp, int value, const struct timespec *timeout)
  133. {
  134. rt_thread_t thread;
  135. rt_err_t ret = -RT_EINTR;
  136. /**
  137. * Brief: Remove current thread from scheduler, besides appends it to
  138. * the waiting thread list of the futex. If the timeout is specified
  139. * a timer will be setup for current thread
  140. *
  141. * Note: Critical Section
  142. * - futex (RW; Protected by lwp_lock)
  143. * - the local cpu
  144. */
  145. LWP_LOCK(lwp);
  146. if (*(futex->uaddr) == value)
  147. {
  148. thread = rt_thread_self();
  149. rt_enter_critical();
  150. ret = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE);
  151. if (ret == RT_EOK)
  152. {
  153. /**
  154. * Brief: Add current thread into futex waiting thread list
  155. *
  156. * Note: Critical Section
  157. * - the futex waiting_thread list (RW)
  158. */
  159. rt_list_insert_before(&(futex->waiting_thread), &(thread->tlist));
  160. if (timeout)
  161. {
  162. /* start the timer of thread */
  163. rt_int32_t time = timeout->tv_sec * RT_TICK_PER_SECOND + timeout->tv_nsec * RT_TICK_PER_SECOND / NANOSECOND_PER_SECOND;
  164. if (time < 0)
  165. {
  166. time = 0;
  167. }
  168. rt_timer_control(&(thread->thread_timer),
  169. RT_TIMER_CTRL_SET_TIME,
  170. &time);
  171. rt_timer_start(&(thread->thread_timer));
  172. }
  173. }
  174. else
  175. {
  176. ret = EINTR;
  177. }
  178. LWP_UNLOCK(lwp);
  179. rt_exit_critical();
  180. if (ret == RT_EOK)
  181. {
  182. /* do schedule */
  183. rt_schedule();
  184. /* check errno */
  185. ret = rt_get_errno();
  186. }
  187. ret = ret > 0 ? -ret : ret;
  188. switch (ret)
  189. {
  190. case RT_EOK:
  191. ret = 0;
  192. break;
  193. case -RT_EINTR:
  194. ret = -EINTR;
  195. break;
  196. default:
  197. ret = -EAGAIN;
  198. break;
  199. }
  200. }
  201. else
  202. {
  203. LWP_UNLOCK(lwp);
  204. ret = -EAGAIN;
  205. rt_set_errno(EAGAIN);
  206. }
  207. return ret;
  208. }
  209. static long _futex_wake(struct rt_futex *futex, struct rt_lwp *lwp, int number)
  210. {
  211. long woken_cnt = 0;
  212. int is_empty = 0;
  213. rt_thread_t thread;
  214. /**
  215. * Brief: Wakeup a suspended thread on the futex waiting thread list
  216. *
  217. * Note: Critical Section
  218. * - the futex waiting_thread list (RW)
  219. */
  220. while (number && !is_empty)
  221. {
  222. LWP_LOCK(lwp);
  223. is_empty = rt_list_isempty(&(futex->waiting_thread));
  224. if (!is_empty)
  225. {
  226. thread = rt_list_entry(futex->waiting_thread.next, struct rt_thread, tlist);
  227. /* remove from waiting list */
  228. rt_list_remove(&(thread->tlist));
  229. thread->error = RT_EOK;
  230. /* resume the suspended thread */
  231. rt_thread_resume(thread);
  232. number--;
  233. woken_cnt++;
  234. }
  235. LWP_UNLOCK(lwp);
  236. }
  237. /* do schedule */
  238. rt_schedule();
  239. return woken_cnt;
  240. }
  241. #include <syscall_generic.h>
  242. rt_inline rt_bool_t _timeout_ignored(int op)
  243. {
  244. /**
  245. * if (op & (FUTEX_WAKE|FUTEX_FD|FUTEX_WAKE_BITSET|FUTEX_TRYLOCK_PI|FUTEX_UNLOCK_PI)) was TRUE
  246. * `timeout` should be ignored by implementation, according to POSIX futex(2) manual.
  247. * since only FUTEX_WAKE is implemented in rt-smart, only FUTEX_WAKE was omitted currently
  248. */
  249. return (op & (FUTEX_WAKE));
  250. }
  251. sysret_t sys_futex(int *uaddr, int op, int val, const struct timespec *timeout,
  252. int *uaddr2, int val3)
  253. {
  254. struct rt_lwp *lwp = RT_NULL;
  255. struct rt_futex *futex = RT_NULL;
  256. sysret_t ret = 0;
  257. if (!lwp_user_accessable(uaddr, sizeof(int)))
  258. {
  259. ret = -EINVAL;
  260. }
  261. else if (timeout && !_timeout_ignored(op) && !lwp_user_accessable((void *)timeout, sizeof(struct timespec)))
  262. {
  263. ret = -EINVAL;
  264. }
  265. else
  266. {
  267. lwp = lwp_self();
  268. ret = lwp_futex(lwp, futex, uaddr, op, val, timeout);
  269. }
  270. return ret;
  271. }
  272. rt_err_t lwp_futex(struct rt_lwp *lwp, struct rt_futex *futex, int *uaddr, int op, int val, const struct timespec *timeout)
  273. {
  274. rt_err_t rc = 0;
  275. /**
  276. * Brief: Check if the futex exist, otherwise create a new one
  277. *
  278. * Note: Critical Section
  279. * - lwp address_search_head (READ)
  280. */
  281. LWP_LOCK(lwp);
  282. futex = _futex_get_locked(uaddr, lwp);
  283. if (futex == RT_NULL)
  284. {
  285. /* create a futex according to this uaddr */
  286. futex = _futex_create_locked(uaddr, lwp);
  287. if (futex == RT_NULL)
  288. {
  289. rc = -ENOMEM;
  290. }
  291. }
  292. LWP_UNLOCK(lwp);
  293. if (!rc)
  294. {
  295. if (!(op & FUTEX_PRIVATE))
  296. rc = -ENOSYS;
  297. else
  298. {
  299. op &= ~FUTEX_PRIVATE;
  300. switch (op)
  301. {
  302. case FUTEX_WAIT:
  303. rc = _futex_wait(futex, lwp, val, timeout);
  304. break;
  305. case FUTEX_WAKE:
  306. rc = _futex_wake(futex, lwp, val);
  307. break;
  308. default:
  309. LOG_W("User require op=%d which is not implemented", op);
  310. rc = -ENOSYS;
  311. break;
  312. }
  313. }
  314. }
  315. return rc;
  316. }