sched_timed_mtx_tc.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /*
  2. * Copyright (c) 2006-2024, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2024-01-25 Shell init ver.
  9. */
  10. #define __RT_KERNEL_SOURCE__
  11. #include <rtthread.h>
  12. #include <stdlib.h>
  13. #include "utest.h"
  14. #define TEST_SECONDS 10
  15. #define TEST_LOOP_TICKS (TEST_SECONDS * RT_TICK_PER_SECOND)
  16. #define TEST_PROGRESS_COUNTS (36)
  17. #define TEST_PROGRESS_ON (TEST_LOOP_TICKS*2/TEST_PROGRESS_COUNTS)
  18. static struct rt_semaphore _thr_exit_sem;
  19. static struct rt_mutex _ipc_primitive;
  20. static struct rt_semaphore _cons_can_take_mtx;
  21. static struct rt_semaphore _prod_can_take_mtx;
  22. static rt_atomic_t _progress_counter;
  23. #define CONSUMER_MAGIC 0x11223344
  24. #define PRODUCER_MAGIC 0x44332211
  25. static rt_atomic_t _last_holder_flag = CONSUMER_MAGIC;
  26. static rt_base_t _timedout_failed_times = 0;
  27. /**
  28. * Test on timedout IPC with racing condition where timedout routine and producer
  29. * thread may race to wakeup sleeper.
  30. *
  31. * This test will fork 2 thread, one producer and one consumer. The producer will
  32. * looping and trigger the IPC on the edge of new tick arrives. The consumer will
  33. * wait on IPC with a timedout of 1 tick.
  34. */
  35. static void _wait_until_edge(void)
  36. {
  37. rt_tick_t entry_level, current;
  38. rt_base_t random_latency;
  39. entry_level = rt_tick_get();
  40. do
  41. {
  42. current = rt_tick_get();
  43. }
  44. while (current == entry_level);
  45. /* give a random latency for test */
  46. random_latency = rand() % 1000 * 1000;
  47. entry_level = current;
  48. for (size_t i = 0; i < random_latency; i++)
  49. {
  50. current = rt_tick_get();
  51. if (current != entry_level)
  52. break;
  53. }
  54. }
  55. static void _producer_entry(void *param)
  56. {
  57. rt_err_t error;
  58. for (size_t i = 0; i < TEST_LOOP_TICKS; i++)
  59. {
  60. /**
  61. * only try to take mutex after consumer have taken it after last
  62. * release from us.
  63. */
  64. error = rt_sem_take(&_prod_can_take_mtx, RT_WAITING_FOREVER);
  65. if (error)
  66. {
  67. uassert_true(0);
  68. break;
  69. }
  70. error = rt_mutex_take(&_ipc_primitive, RT_WAITING_FOREVER);
  71. if (error)
  72. {
  73. uassert_true(0);
  74. break;
  75. }
  76. /* ensure that mutex should be held in round-robin method */
  77. if (rt_atomic_load(&_last_holder_flag) != CONSUMER_MAGIC)
  78. {
  79. uassert_true(0);
  80. break;
  81. }
  82. else
  83. {
  84. rt_atomic_store(&_last_holder_flag, PRODUCER_MAGIC);
  85. rt_sem_release(&_cons_can_take_mtx);
  86. }
  87. _wait_until_edge();
  88. rt_mutex_release(&_ipc_primitive);
  89. if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0)
  90. uassert_true(1);
  91. }
  92. rt_sem_release(&_thr_exit_sem);
  93. return;
  94. }
  95. static void _consumer_entry(void *param)
  96. {
  97. rt_err_t error;
  98. for (size_t i = 0; i < TEST_LOOP_TICKS; i++)
  99. {
  100. /**
  101. * only try to take mutex after producer have taken it after last
  102. * release from us.
  103. */
  104. error = rt_sem_take(&_cons_can_take_mtx, RT_WAITING_FOREVER);
  105. if (error)
  106. {
  107. uassert_true(0);
  108. break;
  109. }
  110. while (1)
  111. {
  112. error = rt_mutex_take_interruptible(&_ipc_primitive, 1);
  113. if (error == -RT_ETIMEOUT)
  114. {
  115. _timedout_failed_times++;
  116. if (rt_mutex_get_owner(&_ipc_primitive) == rt_thread_self())
  117. {
  118. uassert_true(0);
  119. break;
  120. }
  121. }
  122. else
  123. {
  124. break;
  125. }
  126. }
  127. if (error != RT_EOK)
  128. {
  129. uassert_true(0);
  130. break;
  131. }
  132. /* ensure that mutex should be held in round-robin method */
  133. if (rt_atomic_load(&_last_holder_flag) != PRODUCER_MAGIC)
  134. {
  135. uassert_true(0);
  136. break;
  137. }
  138. else
  139. {
  140. rt_atomic_store(&_last_holder_flag, CONSUMER_MAGIC);
  141. rt_sem_release(&_prod_can_take_mtx);
  142. }
  143. rt_mutex_release(&_ipc_primitive);
  144. if (rt_mutex_get_owner(&_ipc_primitive) == rt_thread_self())
  145. {
  146. uassert_true(0);
  147. break;
  148. }
  149. if (rt_atomic_add(&_progress_counter, 1) % TEST_PROGRESS_ON == 0)
  150. uassert_true(1);
  151. }
  152. rt_sem_release(&_thr_exit_sem);
  153. return;
  154. }
  155. static void timed_mtx_tc(void)
  156. {
  157. rt_thread_t prod = rt_thread_create(
  158. "prod",
  159. _producer_entry,
  160. (void *)0,
  161. UTEST_THR_STACK_SIZE,
  162. UTEST_THR_PRIORITY + 1,
  163. 4);
  164. rt_thread_t cons = rt_thread_create(
  165. "cons",
  166. _consumer_entry,
  167. (void *)0,
  168. UTEST_THR_STACK_SIZE,
  169. UTEST_THR_PRIORITY + 1,
  170. 100);
  171. rt_thread_startup(prod);
  172. rt_thread_startup(cons);
  173. for (size_t i = 0; i < 2; i++)
  174. {
  175. uassert_int_equal(
  176. rt_sem_take(&_thr_exit_sem, 2 * TEST_LOOP_TICKS),
  177. RT_EOK);
  178. }
  179. /* Summary */
  180. LOG_I("Total failed times: %ld(in %d)\n", _timedout_failed_times, TEST_LOOP_TICKS);
  181. }
  182. static rt_err_t utest_tc_init(void)
  183. {
  184. _timedout_failed_times = 0;
  185. rt_mutex_init(&_ipc_primitive, "ipc", RT_IPC_FLAG_PRIO);
  186. rt_sem_init(&_cons_can_take_mtx, "test", 0, RT_IPC_FLAG_PRIO);
  187. rt_sem_init(&_prod_can_take_mtx, "test", 1, RT_IPC_FLAG_PRIO);
  188. rt_sem_init(&_thr_exit_sem, "test", 0, RT_IPC_FLAG_PRIO);
  189. return RT_EOK;
  190. }
  191. static rt_err_t utest_tc_cleanup(void)
  192. {
  193. rt_mutex_detach(&_ipc_primitive);
  194. rt_sem_detach(&_cons_can_take_mtx);
  195. rt_sem_detach(&_prod_can_take_mtx);
  196. rt_sem_detach(&_thr_exit_sem);
  197. return RT_EOK;
  198. }
  199. static void testcase(void)
  200. {
  201. UTEST_UNIT_RUN(timed_mtx_tc);
  202. }
  203. UTEST_TC_EXPORT(testcase, "testcases.kernel.scheduler.timed_mtx", utest_tc_init, utest_tc_cleanup, TEST_SECONDS * 2);