futex_bitset.c 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. #define _GNU_SOURCE
  2. #include <asm/prctl.h>
  3. #include <assert.h>
  4. #include <errno.h>
  5. #include <linux/futex.h>
  6. #include <pthread.h>
  7. #include <sched.h>
  8. #include <signal.h>
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <sys/syscall.h>
  12. #include <sys/types.h>
  13. #include <sys/wait.h>
  14. #include <unistd.h>
  15. #define THREADS 8
  16. static int myfutex = 0;
  17. static int futex(int* uaddr, int futex_op, int val, const struct timespec* timeout, int* uaddr2,
  18. int val3) {
  19. return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
  20. }
  21. void* thread_function(void* argument) {
  22. int* ptr = (int*)argument;
  23. long rv;
  24. // Sleep on the futex
  25. rv = futex(&myfutex, FUTEX_WAIT_BITSET, 0, NULL, NULL, *ptr);
  26. return (void*)rv;
  27. }
  28. int main(int argc, const char** argv) {
  29. pthread_t thread[THREADS];
  30. int varx[THREADS];
  31. setbuf(stdout, NULL);
  32. setbuf(stderr, NULL);
  33. static_assert(THREADS < sizeof(int) * 8 - 1, "Left shift in the loop below would overflow!");
  34. for (int i = 0; i < THREADS; i++) {
  35. varx[i] = (1 << i);
  36. int ret = pthread_create(&thread[i], NULL, &thread_function, &varx[i]);
  37. if (ret) {
  38. errno = ret;
  39. perror("pthread_create");
  40. return 1;
  41. }
  42. }
  43. printf("Waking up kiddos\n");
  44. /* Wake in reverse order */
  45. for (int i = THREADS - 1; i >= 0; i--) {
  46. int rv;
  47. int var = (1 << i);
  48. // Wake up the thread
  49. do {
  50. rv = futex(&myfutex, FUTEX_WAKE_BITSET, 1, NULL, NULL, var);
  51. if (rv == 0) {
  52. // the thread of thread_function() may not reach
  53. // futex(FUTEX_WAIT_BITSET) yet.
  54. // Wait for the thread to sleep and try again.
  55. // Since synchronization primitive, futex, is being tested,
  56. // futex can't be used here. resort to use sleep.
  57. sleep(1);
  58. }
  59. } while (rv == 0);
  60. printf("FUTEX_WAKE_BITSET i = %d rv = %d (expected: 1)\n", i, rv);
  61. if (rv != 1) {
  62. return 1;
  63. }
  64. // Wait for the child thread to exit
  65. intptr_t retval = 0;
  66. int ret = pthread_join(thread[i], (void**)&retval);
  67. if (ret) {
  68. errno = ret;
  69. perror("pthread_join");
  70. return 1;
  71. }
  72. if (retval != 0) {
  73. printf("Thread %d returned %zd (%s)\n", i, retval, strerror(retval));
  74. return 1;
  75. }
  76. if (i != 0) {
  77. errno = 0;
  78. ret = pthread_tryjoin_np(thread[0], (void**)&retval);
  79. if (ret != EBUSY) {
  80. printf("Unexpectedly pthread_tryjoin_np returned: %d (%s)\n", ret, strerror(ret));
  81. return 1;
  82. }
  83. }
  84. }
  85. printf("Woke all kiddos\n");
  86. return 0;
  87. }