timeout-new.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #include "../config-host.h"
  2. /* SPDX-License-Identifier: MIT */
  3. /*
  4. * Description: tests for getevents timeout
  5. *
  6. */
  7. #include <stdio.h>
  8. #include <sys/time.h>
  9. #include <unistd.h>
  10. #include <pthread.h>
  11. #include "liburing.h"
  12. #include "helpers.h"
  13. #define TIMEOUT_MSEC 200
  14. #define TIMEOUT_SEC 10
  15. static int thread_ret0, thread_ret1;
  16. static int cnt = 0;
  17. static pthread_mutex_t mutex;
  18. static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
  19. {
  20. ts->tv_sec = msec / 1000;
  21. ts->tv_nsec = (msec % 1000) * 1000000;
  22. }
  23. static int test_return_before_timeout(struct io_uring *ring)
  24. {
  25. struct io_uring_cqe *cqe;
  26. struct io_uring_sqe *sqe;
  27. int ret;
  28. bool retried = false;
  29. struct __kernel_timespec ts;
  30. msec_to_ts(&ts, TIMEOUT_MSEC);
  31. sqe = io_uring_get_sqe(ring);
  32. io_uring_prep_nop(sqe);
  33. ret = io_uring_submit(ring);
  34. if (ret <= 0) {
  35. fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
  36. return 1;
  37. }
  38. again:
  39. ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
  40. if (ret == -ETIME && (ring->flags & IORING_SETUP_SQPOLL) && !retried) {
  41. /*
  42. * there is a small chance SQPOLL hasn't been waked up yet,
  43. * give it one more try.
  44. */
  45. printf("warning: funky SQPOLL timing\n");
  46. sleep(1);
  47. retried = true;
  48. goto again;
  49. } else if (ret < 0) {
  50. fprintf(stderr, "%s: timeout error: %d\n", __FUNCTION__, ret);
  51. return 1;
  52. }
  53. io_uring_cqe_seen(ring, cqe);
  54. return 0;
  55. }
  56. static int test_return_after_timeout(struct io_uring *ring)
  57. {
  58. struct io_uring_cqe *cqe;
  59. int ret;
  60. struct __kernel_timespec ts;
  61. struct timeval tv;
  62. unsigned long long exp;
  63. msec_to_ts(&ts, TIMEOUT_MSEC);
  64. gettimeofday(&tv, NULL);
  65. ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
  66. exp = mtime_since_now(&tv);
  67. if (ret != -ETIME) {
  68. fprintf(stderr, "%s: timeout error: %d\n", __FUNCTION__, ret);
  69. return 1;
  70. }
  71. if (exp < TIMEOUT_MSEC / 2 || exp > (TIMEOUT_MSEC * 3) / 2) {
  72. fprintf(stderr, "%s: Timeout seems wonky (got %llu)\n", __FUNCTION__, exp);
  73. return 1;
  74. }
  75. return 0;
  76. }
  77. static int __reap_thread_fn(void *data)
  78. {
  79. struct io_uring *ring = (struct io_uring *)data;
  80. struct io_uring_cqe *cqe;
  81. struct __kernel_timespec ts;
  82. msec_to_ts(&ts, TIMEOUT_SEC);
  83. pthread_mutex_lock(&mutex);
  84. cnt++;
  85. pthread_mutex_unlock(&mutex);
  86. return io_uring_wait_cqe_timeout(ring, &cqe, &ts);
  87. }
  88. static void *reap_thread_fn0(void *data)
  89. {
  90. thread_ret0 = __reap_thread_fn(data);
  91. return NULL;
  92. }
  93. static void *reap_thread_fn1(void *data)
  94. {
  95. thread_ret1 = __reap_thread_fn(data);
  96. return NULL;
  97. }
  98. /*
  99. * This is to test issuing a sqe in main thread and reaping it in two child-thread
  100. * at the same time. To see if timeout feature works or not.
  101. */
  102. static int test_multi_threads_timeout(void)
  103. {
  104. struct io_uring ring;
  105. int ret;
  106. bool both_wait = false;
  107. pthread_t reap_thread0, reap_thread1;
  108. struct io_uring_sqe *sqe;
  109. ret = io_uring_queue_init(8, &ring, 0);
  110. if (ret) {
  111. fprintf(stderr, "%s: ring setup failed: %d\n", __FUNCTION__, ret);
  112. return 1;
  113. }
  114. pthread_create(&reap_thread0, NULL, reap_thread_fn0, &ring);
  115. pthread_create(&reap_thread1, NULL, reap_thread_fn1, &ring);
  116. /*
  117. * make two threads both enter io_uring_wait_cqe_timeout() before issuing the sqe
  118. * as possible as we can. So that there are two threads in the ctx->wait queue.
  119. * In this way, we can test if a cqe wakes up two threads at the same time.
  120. */
  121. while(!both_wait) {
  122. pthread_mutex_lock(&mutex);
  123. if (cnt == 2)
  124. both_wait = true;
  125. pthread_mutex_unlock(&mutex);
  126. sleep(1);
  127. }
  128. sqe = io_uring_get_sqe(&ring);
  129. if (!sqe) {
  130. fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
  131. goto err;
  132. }
  133. io_uring_prep_nop(sqe);
  134. ret = io_uring_submit(&ring);
  135. if (ret <= 0) {
  136. fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
  137. goto err;
  138. }
  139. pthread_join(reap_thread0, NULL);
  140. pthread_join(reap_thread1, NULL);
  141. if ((thread_ret0 && thread_ret0 != -ETIME) || (thread_ret1 && thread_ret1 != -ETIME)) {
  142. fprintf(stderr, "%s: thread wait cqe timeout failed: %d %d\n",
  143. __FUNCTION__, thread_ret0, thread_ret1);
  144. goto err;
  145. }
  146. return 0;
  147. err:
  148. return 1;
  149. }
  150. int main(int argc, char *argv[])
  151. {
  152. struct io_uring ring_normal, ring_sq;
  153. int ret;
  154. if (argc > 1)
  155. return 0;
  156. ret = io_uring_queue_init(8, &ring_normal, 0);
  157. if (ret) {
  158. fprintf(stderr, "ring_normal setup failed: %d\n", ret);
  159. return 1;
  160. }
  161. if (!(ring_normal.features & IORING_FEAT_EXT_ARG)) {
  162. fprintf(stderr, "feature IORING_FEAT_EXT_ARG not supported, skipping.\n");
  163. return 0;
  164. }
  165. ret = test_return_before_timeout(&ring_normal);
  166. if (ret) {
  167. fprintf(stderr, "ring_normal: test_return_before_timeout failed\n");
  168. return ret;
  169. }
  170. ret = test_return_after_timeout(&ring_normal);
  171. if (ret) {
  172. fprintf(stderr, "ring_normal: test_return_after_timeout failed\n");
  173. return ret;
  174. }
  175. ret = io_uring_queue_init(8, &ring_sq, IORING_SETUP_SQPOLL);
  176. if (ret) {
  177. fprintf(stderr, "ring_sq setup failed: %d\n", ret);
  178. return 1;
  179. }
  180. ret = test_return_before_timeout(&ring_sq);
  181. if (ret) {
  182. fprintf(stderr, "ring_sq: test_return_before_timeout failed\n");
  183. return ret;
  184. }
  185. ret = test_return_after_timeout(&ring_sq);
  186. if (ret) {
  187. fprintf(stderr, "ring_sq: test_return_after_timeout failed\n");
  188. return ret;
  189. }
  190. ret = test_multi_threads_timeout();
  191. if (ret) {
  192. fprintf(stderr, "test_multi_threads_timeout failed\n");
  193. return ret;
  194. }
  195. return 0;
  196. }