#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include // 64kB stack #define FIBER_STACK (1024 * 64) #define THREADS 2 static int myfutex = 0; struct atomic_int { volatile int counter; }; static struct atomic_int my_counter; static inline void atomic_inc(struct atomic_int* v) { asm volatile("lock; incl %0" : "+m"(v->counter)); } static int futex(int* uaddr, int futex_op, int val, const struct timespec* timeout, int* uaddr2, int val3) { return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr, val3); } int thread_function(void* argument) { int* ptr = (int*)argument; int rv; atomic_inc(&my_counter); // Sleep on the futex rv = futex(&myfutex, FUTEX_WAIT_BITSET, 0, NULL, NULL, *ptr); assert(rv == 0); // printf("child thread %d awakened\n", getpid()); return 0; } int main(int argc, const char** argv) { void* stacks[THREADS]; pid_t pids[THREADS]; int varx[THREADS]; my_counter.counter = 0; for (int i = 0; i < THREADS; i++) { varx[i] = (1 << i); // Allocate the stack stacks[i] = malloc(FIBER_STACK); if (stacks[i] == 0) { perror("malloc: could not allocate stack"); _exit(1); } // Call the clone system call to create the child thread pids[i] = clone(&thread_function, (void*)stacks[i] + FIBER_STACK, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM, &varx[i]); // printf("clone() creates new thread %d\n", pids[i]); if (pids[i] == -1) { perror("clone"); _exit(2); } } // Make sure the threads are sleeping do { sleep(1); } while (my_counter.counter != THREADS); printf("Waking up kiddos\n"); /* Wake in reverse order */ for (int i = THREADS - 1; i >= 0; i--) { pid_t pid; int rv; int var = (1 << i); // Wake up the thread rv = futex(&myfutex, FUTEX_WAKE_BITSET, 1, NULL, NULL, var); assert(rv == 1); // Wait for the child thread to exit pid = waitpid(pids[i], NULL, __WALL); if (pid == -1) { perror("waitpid"); _exit(3); } // Free the stack free(stacks[i]); } printf("Woke all kiddos\n"); return 0; }