futex.c 2.7 KB

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