atomic approach

Pros

  • Re-enterable (set counter to 0 to execute again)

Cons

  • More complex compared to call_once() and pthread_once()
#include <stdio.h>
#include <stdbool.h>
#include <pthread.h>
#include <stdatomic.h>

atomic_bool is_ready = false;

void *func_to_run_once(void *arg) {
	atomic_int *counter = arg;
	while (!atomic_load(&is_ready));
	if (__sync_add_and_fetch(counter, 1) != 1) {
		__sync_fetch_and_sub(counter, 1);
		return NULL;
	}
	fputs("This message is printed only once.\n", stdout);
	fflush(stdout);
	return NULL;
}

int main(void) {
	atomic_int counter = 0;
	pthread_t threads[1000];
	for (size_t i = 0; i < 1000; ++i) {
		if (pthread_create(&threads[i], NULL, func_to_run_once, &counter) != 0) {
			return 1;
		}
	}
	atomic_store(&is_ready, true);
	for (size_t i = 0; i < 1000; ++i) {
		pthread_join(threads[i], NULL);
	}
	return 0;
}