blob: f62843fa7e04274214245e4902f68e56b7a984e5 [file]
#define _GNU_SOURCE
#include <assert.h>
#include <emscripten.h>
#include <emscripten/threading.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static _Atomic int* word;
static _Atomic bool woken = false;
static pthread_barrier_t started;
void* dummy_thread(void* arg) {
return NULL;
}
void* waiter_thread(void* arg) {
*word = 2;
pthread_barrier_wait(&started);
int res = __builtin_wasm_memory_atomic_wait32((int*)word, 2, -1);
assert(res == ATOMICS_WAIT_OK);
woken = true;
return NULL;
}
// See https://github.com/emscripten-core/emscripten/issues/27492
// Test that exiting threads clear their Atomics.waitAsync waiters. Otherwise,
// recycling the freed pthread_t struct memory via malloc can cause a stale
// waiter to intercept Atomics.notify notifications meant for another thread.
int main(void) {
pthread_barrier_init(&started, NULL, 2);
pthread_t dead;
pthread_create(&dead, NULL, dummy_thread, NULL);
pthread_join(dead, NULL);
// malloc should reuse the memory block freed when dummy_thread exited.
word = malloc(1024);
pthread_t w;
pthread_create(&w, NULL, waiter_thread, NULL);
pthread_barrier_wait(&started);
emscripten_thread_sleep(100);
*word = 0;
__builtin_wasm_memory_atomic_notify((int*)word, 1);
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 5;
if (pthread_timedjoin_np(w, NULL, &ts) != 0 || !woken) {
printf("DEADLOCK: parked thread was not woken!\n");
return 1;
}
pthread_barrier_destroy(&started);
printf("done\n");
return 0;
}