futex_wake_op.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #define _GNU_SOURCE
  2. #include <errno.h>
  3. #include <limits.h>
  4. #include <linux/futex.h>
  5. #include <pthread.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <sys/syscall.h>
  10. #include <unistd.h>
  11. static int futex(int* uaddr, int futex_op, int val, const struct timespec* timeout, int* uaddr2, int val3) {
  12. return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
  13. }
  14. static int futex_wait(int* uaddr, int val, const struct timespec* timeout) {
  15. return futex(uaddr, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, val, timeout, NULL, 0);
  16. }
  17. static int futex_wake(int* uaddr, int to_wake) {
  18. return futex(uaddr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, to_wake, NULL, NULL, 0);
  19. }
  20. static int futex_wake_op(int* uaddr1, int to_wake1, int* uaddr2, int to_wake2, int op) {
  21. return futex(uaddr1, FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG, to_wake1, (struct timespec*)(unsigned long)to_wake2, uaddr2, op);
  22. }
  23. static void fail(const char* msg, int x) {
  24. printf("%s failed with %d (%s)\n", msg, x, strerror(x));
  25. exit(1);
  26. }
  27. static void check(int x) {
  28. if (x) {
  29. fail("pthread", x);
  30. }
  31. }
  32. static void store(int* ptr, int val) {
  33. __atomic_store_n(ptr, val, __ATOMIC_SEQ_CST);
  34. }
  35. static int load(int* ptr) {
  36. return __atomic_load_n(ptr, __ATOMIC_SEQ_CST);
  37. }
  38. static int wakeop_arg_extend(int x) {
  39. if (x >= 0x800) {
  40. return 0xfffff000 | x;
  41. }
  42. return x;
  43. }
  44. static int futex1 = 0;
  45. static int futex2 = 0;
  46. #define THREADS1 4
  47. #define THREADS2 5
  48. #define THREADS_WAKE1 2
  49. #define THREADS_WAKE2 3
  50. static int thread_state[THREADS1 + THREADS2] = { 0 };
  51. static void* thread_func(void* arg) {
  52. unsigned long i = (unsigned long)arg;
  53. int ret = -1;
  54. store(&thread_state[i], 1);
  55. if (i < THREADS1) {
  56. ret = futex_wait(&futex1, futex1, NULL);
  57. } else {
  58. ret = futex_wait(&futex2, futex2, NULL);
  59. }
  60. if (ret != 0) {
  61. printf("futex_wait in thread %lu returned %d (%s)\n", i, ret, strerror(ret));
  62. // skip setting state below
  63. return arg;
  64. }
  65. store(&thread_state[i], 2);
  66. return arg;
  67. }
  68. int main(void) {
  69. pthread_t th[THREADS1 + THREADS2];
  70. unsigned long i;
  71. int ret;
  72. int arg1 = 0x123846;
  73. int arg2 = 0xc73;
  74. int cmparg = 0x746; // so that arg1 >= cmparg
  75. int op = FUTEX_OP(FUTEX_OP_XOR, arg2, FUTEX_OP_CMP_GE, cmparg);
  76. store(&futex2, arg1);
  77. for (i = 0; i < THREADS1 + THREADS2; i++) {
  78. check(pthread_create(&th[i], NULL, thread_func, (void*)i));
  79. }
  80. // wait for all threads
  81. for (i = 0; i < THREADS1 + THREADS2; i++) {
  82. while (load(&thread_state[i]) != 1) {
  83. usleep(1000u);
  84. }
  85. }
  86. // and let them sleep on futex
  87. usleep(100000u);
  88. ret = futex_wake_op(&futex1, THREADS_WAKE1, &futex2, THREADS_WAKE2, op);
  89. arg2 = wakeop_arg_extend(arg2);
  90. op = load(&futex2);
  91. if (op != (arg1 ^ arg2)) {
  92. printf("futex_wake_op did not set futex2 value correctly: current value: 0x%x, expected: 0x%x\n", op, arg1 ^ arg2);
  93. return 1;
  94. }
  95. if (ret < 0) {
  96. fail("futex_wake_op", errno);
  97. }
  98. if (ret != THREADS_WAKE1 + THREADS_WAKE2) {
  99. printf("futex_wake_op returned %d instead of %d!\n", ret, THREADS_WAKE1 + THREADS_WAKE2);
  100. return 1;
  101. }
  102. // let the woken thread(s) end
  103. usleep(100000u);
  104. ret = 0;
  105. for (i = 0; i < THREADS1; i++) {
  106. if (load(&thread_state[i]) == 2) {
  107. ret++;
  108. check(pthread_join(th[i], NULL));
  109. store(&thread_state[i], 3);
  110. }
  111. }
  112. if (ret != THREADS_WAKE1) {
  113. printf("futex_wake_op woke-up %d threads on futex1 instead of %d!\n", ret, THREADS_WAKE1);
  114. return 1;
  115. }
  116. ret = 0;
  117. for (i = THREADS1; i < THREADS1 + THREADS2; i++) {
  118. if (load(&thread_state[i]) == 2) {
  119. ret++;
  120. check(pthread_join(th[i], NULL));
  121. store(&thread_state[i], 3);
  122. }
  123. }
  124. if (ret != THREADS_WAKE2) {
  125. printf("futex_wake_op woke-up %d threads on futex2 instead of %d!\n", ret, THREADS_WAKE2);
  126. return 1;
  127. }
  128. ret = futex_wake(&futex1, INT_MAX);
  129. if (ret < 0) {
  130. fail("futex_wake(&futex1)", errno);
  131. }
  132. if (ret != (THREADS1 - THREADS_WAKE1)) {
  133. printf("futex_wake on futex1 woke-up %d threads instead of %d!\n", ret, THREADS1 - THREADS_WAKE1);
  134. return 1;
  135. }
  136. ret = futex_wake(&futex2, INT_MAX);
  137. if (ret < 0) {
  138. fail("futex_wake(&futex2)", errno);
  139. }
  140. if (ret != (THREADS2 - THREADS_WAKE2)) {
  141. printf("futex_wake on futex2 woke-up %d threads instead of %d!\n", ret, THREADS2 - THREADS_WAKE2);
  142. return 1;
  143. }
  144. for (i = 0; i < THREADS1 + THREADS2; i++) {
  145. if (load(&thread_state[i]) != 3) {
  146. check(pthread_join(th[i], NULL));
  147. }
  148. }
  149. puts("Test successful!");
  150. return 0;
  151. }