reverie/tests/c_tests/threads_group_exit_blocking.c

73 lines
1.9 KiB
C
Raw Normal View History

/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
// create `NTHREADS`, all doing (indefinite) blocking futexes, while the thread
// group leader calling `SYS_exit_group`.
// This is to test all blocking futex syscall can be interrupted, and all
// threads can exit gracefully under `SYS_exit_group`.
#include <errno.h>
#include <limits.h>
#include <linux/futex.h>
#include <pthread.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#define NTHREADS 8
static _Atomic unsigned long counter;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void* thread_pfn(void* param) {
atomic_fetch_add(&counter, 1);
// Wait for enough time such that the main thread can kill this thread via
// `exit_group`.
pthread_mutex_lock(&mutex);
return NULL;
}
int main(int argc, char* argv[], char* envp[]) {
// Lock, but never unlock the mutex to force all threads to wait. All threads
// will get killed while waiting for for this mutex.
pthread_mutex_lock(&mutex);
pthread_t threads[NTHREADS];
for (int i = 0; i < NTHREADS; i++) {
if (pthread_create(&threads[i], NULL, thread_pfn, NULL) != 0) {
fprintf(
stderr,
"pthread_create to create thread #%d failed: %s\n",
i,
strerror(errno));
abort();
}
}
// Spin while we wait for the threads to finish initializing.
while (atomic_load(&counter) != NTHREADS) {
// Yield so that other threads have a chance to run.
sched_yield();
}
// do SYS_exit_group. All threads should be still blocked by mutex.
// SYS_exit_group should force all threads begin to exit.
syscall(SYS_exit_group, 0);
return 0;
}