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

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

#define _GNU_SOURCE /* gettid(2) */
#include "test.h"
#include <librecast_pvt.h>
#include <librecast/net.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdlib.h>
#include <unistd.h>

#ifndef gettid
# define gettid() 0
#endif

static int nthreads = 2;
static int iterations = 2;
static sem_t sem;

void *thread_start(void *arg)
{
	pid_t tid = gettid();
	lc_ctx_t *lctx = (lc_ctx_t *)arg;
	lc_channel_t *chan;
	sem_wait(&sem);
	for (int i = 0; i < iterations; i++) {
		chan = lc_channel_new(lctx, "atomic");
		test_log("[%i]: new chan %p\n", tid, (void *)chan);
		test_log("[%i]: chan->readers = %i\n", tid, aload(&chan->readers));
		test_log("[%i]: closing chan %p\n", tid, (void *)chan);
		lc_channel_free(chan);
	}
	return arg;
}

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

	test_name("lc_channel_acquire() / lc_channel_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; i++) {
		rc = pthread_create(&tid[i], NULL, &thread_start, lctx);
		if (!test_assert(rc == 0, "pthread_create: %i", i)) {
			nthreads = i;
			break;
		}
	}
	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);
	lc_ctx_free(lctx);
	return test_status;
}
