futex.c 2.5 KB

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