/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2024 Brett Sheffield <bacs@librecast.net> */

/* test thread safety of context API calls
 * run with CFLAGS="-fsanitize=thread"
 */

#include "test.h"
#include <librecast_pvt.h>
#include <librecast/net.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>

static int nthreads = 3;
static sem_t sem;

void *thread_start(void *arg)
{
	lc_ctx_t *lctx = (lc_ctx_t *)arg;
	sem_wait(&sem);
	if (lctx) {
		/* touch ctx */
		test_log("reading from ctx->readers = %i\n", aload(&lctx->readers));
		/* release */
		test_log("calling lc_ctx_release()\n");
		lc_ctx_release(lctx);
	}
	return arg;
}

void *thread_ctx_del(void *arg)
{
	lc_ctx_t *lctx = (lc_ctx_t *)arg;
	sem_wait(&sem);
	test_log("%s() calling lc_ctx_free()\n", __func__);
	lc_ctx_free(lctx);
	return NULL;
}

int main(void)
{
	pthread_t tid[nthreads];
	lc_ctx_t *lctx;
	int rc;

	test_name("lc_ctx_acquire() / lc_ctx_release()");

	/* create a Librecast Context */
	lctx = lc_ctx_new();
	if (!test_assert(lctx != NULL, "lc_ctx_new()")) return test_status;
	/* start some threads */
	sem_init(&sem, 0, 0); /* use a semaphore to sync thread start */
	for (int i = 0; i < nthreads - 1; i++) {
		rc = pthread_create(&tid[i], NULL, &thread_start, lc_ctx_acquire(lctx));
		if (!test_assert(rc == 0, "pthread_create: %i", i)) {
			nthreads = i;
			break;
		}
	}
	rc = pthread_create(&tid[nthreads - 1], NULL, &thread_ctx_del, lctx);
	if (!test_assert(rc == 0, "pthread_create: %i", nthreads - 1)) {
		nthreads--;
	}
	for (int i = 0; i < nthreads; i++) sem_post(&sem); /* release the threads */
	/* clean up */
	for (int i = 0; i < nthreads; i++) {
		pthread_join(tid[i], NULL);
	}
	sem_destroy(&sem);
	return test_status;
}
