futex.c 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #define _GNU_SOURCE
  2. #include <malloc.h>
  3. #include <unistd.h>
  4. #include <sys/types.h>
  5. #include <sys/wait.h>
  6. #include <signal.h>
  7. #include <sched.h>
  8. #include <stdio.h>
  9. #include <sys/syscall.h>
  10. #include <asm/prctl.h>
  11. #include <linux/futex.h>
  12. #include <assert.h>
  13. // 64kB stack
  14. #define FIBER_STACK (1024 * 64)
  15. #define THREADS 2
  16. static int myfutex = 0;
  17. struct atomic_int {
  18. volatile int counter;
  19. };
  20. static struct atomic_int my_counter;
  21. static inline void atomic_inc (struct atomic_int * v)
  22. {
  23. asm volatile( "lock; incl %0"
  24. : "+m" (v->counter));
  25. }
  26. static int
  27. futex(int *uaddr, int futex_op, int val,
  28. const struct timespec *timeout, int *uaddr2, int val3)
  29. {
  30. return syscall(SYS_futex, uaddr, futex_op, val,
  31. timeout, uaddr, val3);
  32. }
  33. int thread_function (void * argument)
  34. {
  35. int *ptr = (int *) argument;
  36. int rv;
  37. atomic_inc(&my_counter);
  38. // Sleep on the futex
  39. rv = futex(&myfutex, FUTEX_WAIT_BITSET, 0, NULL, NULL, *ptr);
  40. assert(rv == 0);
  41. //printf("child thread %d awakened\n", getpid());
  42. return 0;
  43. }
  44. int main (int argc, const char ** argv)
  45. {
  46. void * stacks[THREADS];
  47. pid_t pids[THREADS];
  48. int varx[THREADS];
  49. my_counter.counter = 0;
  50. for (int i = 0; i < THREADS; i++) {
  51. varx[i] = (1 << i);
  52. // Allocate the stack
  53. stacks[i] = malloc(FIBER_STACK);
  54. if (stacks[i] == 0) {
  55. perror("malloc: could not allocate stack");
  56. _exit(1);
  57. }
  58. // Call the clone system call to create the child thread
  59. pids[i] = clone(&thread_function, (void *) stacks[i] + FIBER_STACK,
  60. CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM,
  61. &varx[i]);
  62. //printf("clone() creates new thread %d\n", pids[i]);
  63. if (pids[i] == -1) {
  64. perror("clone");
  65. _exit(2);
  66. }
  67. }
  68. // Make sure the threads are sleeping
  69. do {
  70. sleep(1);
  71. } while(my_counter.counter != THREADS);
  72. printf("Waking up kiddos\n");
  73. /* Wake in reverse order */
  74. for (int i = THREADS-1; i >= 0; i--) {
  75. pid_t pid;
  76. int rv;
  77. int var = (1 << i);
  78. // Wake up the thread
  79. rv = futex(&myfutex, FUTEX_WAKE_BITSET, 1, NULL, NULL, var);
  80. assert(rv == 1);
  81. // Wait for the child thread to exit
  82. pid = waitpid(pids[i], NULL, __WALL);
  83. if (pid == -1) {
  84. perror("waitpid");
  85. _exit(3);
  86. }
  87. // Free the stack
  88. free(stacks[i]);
  89. }
  90. printf("Woke all kiddos\n");
  91. return 0;
  92. }