#line 1 "/build/ecl/src/ecl-26.3.27/src/c/threads/semaphore.d"
/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* vim: set filetype=c tabstop=2 shiftwidth=2 expandtab: */

/*
 * semaphore.d - POSIX-like semaphores
 *
 * Copyright (c) 2011 Juan Jose Garcia Ripoll
 * Copyright (c) 2020 Marius Gerbershagen
 *
 * See file 'LICENSE' for the copyright details.
 *
 */

#include <ecl/ecl.h>
#include <ecl/internal.h>

cl_object
ecl_make_semaphore(cl_object name, cl_fixnum count)
{
  cl_env_ptr env = ecl_process_env();
  cl_object output = ecl_alloc_object(t_semaphore);
  output->semaphore.name = name;
  output->semaphore.counter = count;
  output->semaphore.wait_count = 0;
  ecl_disable_interrupts_env(env);
  ecl_cond_var_init(&output->semaphore.cv);
  ecl_mutex_init(&output->semaphore.mutex, FALSE);
  ecl_set_finalizer_unprotected(output, ECL_T);
  ecl_enable_interrupts_env(env);
  return output;
}

#line 33
cl_object mp_make_semaphore(cl_narg narg, ...)
{
#line 33

  #line 34
#if defined(__clang__) || defined(__GNUC__)
	__attribute__((unused)) const cl_env_ptr the_env = ecl_process_env();
#else
	const cl_env_ptr the_env = ecl_process_env();
#endif
#line 34
	static cl_object KEYS[2] = {(cl_object)(cl_symbols+1309), (cl_object)(cl_symbols+1499)};
	cl_object name;
	cl_object count;
#line 34
	cl_object KEY_VARS[4];
#line 34
	ecl_va_list ARGS;
	ecl_va_start(ARGS, narg, narg, 0);
#line 34
	if (ecl_unlikely(narg < 0)) FEwrong_num_arguments(ecl_make_fixnum(1491));
#line 34
	cl_parse_key(ARGS, 2, KEYS, KEY_VARS, NULL, 0);
#line 34
	if (KEY_VARS[2]==ECL_NIL) {
#line 34
	  name = ECL_NIL;
	} else {
#line 34
	  name = KEY_VARS[0];
	}
#line 34
	if (KEY_VARS[3]==ECL_NIL) {
#line 34
	  count = ecl_make_fixnum(0);
	} else {
#line 34
	  count = KEY_VARS[1];
	}
#line 34
{
    {
#line 35
	#line 35
	cl_object __value0 = ecl_make_semaphore(name, fixnnint(count));
#line 35
	the_env->nvalues = 1;
#line 35
	the_env->values[0] = __value0;
#line 35
	ecl_va_end(ARGS);
#line 35
	return __value0;
#line 35
}
;
  } }

cl_object
mp_semaphore_name(cl_object semaphore)
{
  cl_env_ptr env = ecl_process_env();
  unlikely_if (ecl_t_of(semaphore) != t_semaphore) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::SEMAPHORE-NAME*/1497), semaphore, ecl_make_fixnum(/*MP::SEMAPHORE*/1490));
  }
  ecl_return1(env, semaphore->semaphore.name);
}

cl_object
mp_semaphore_count(cl_object semaphore)
{
  cl_env_ptr env = ecl_process_env();
  unlikely_if (ecl_t_of(semaphore) != t_semaphore) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::SEMAPHORE-COUNT*/1496), semaphore, ecl_make_fixnum(/*MP::SEMAPHORE*/1490));
  }
  ecl_return1(env, ecl_make_fixnum(semaphore->semaphore.counter));
}

cl_object
mp_semaphore_wait_count(cl_object semaphore)
{
  cl_env_ptr env = ecl_process_env();
  unlikely_if (ecl_t_of(semaphore) != t_semaphore) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::SEMAPHORE-WAIT-COUNT*/1498), semaphore, ecl_make_fixnum(/*MP::SEMAPHORE*/1490));
  }
  ecl_return1(env, ecl_make_fixnum(semaphore->semaphore.wait_count));
}

#line 68
cl_object mp_signal_semaphore(cl_narg narg, cl_object semaphore, ...)
{
#line 68

  #line 69
#if defined(__clang__) || defined(__GNUC__)
	__attribute__((unused)) const cl_env_ptr the_env = ecl_process_env();
#else
	const cl_env_ptr the_env = ecl_process_env();
#endif
#line 69
	cl_object count;
#line 69
	va_list ARGS;
	va_start(ARGS, semaphore);
#line 69
	if (ecl_unlikely(narg < 1|| narg > 2)) FEwrong_num_arguments(ecl_make_fixnum(1492));
#line 69
	if (narg > 1) {
#line 69
		count = va_arg(ARGS,cl_object);
#line 69
	} else {
#line 69
		count = ecl_make_fixnum(1);
#line 69
	}
#line 69
{
    cl_fixnum n = fixnnint(count);
    unlikely_if (ecl_t_of(semaphore) != t_semaphore) {
      FEwrong_type_nth_arg(ecl_make_fixnum(/*MP::SIGNAL-SEMAPHORE*/1492), 1, semaphore, ecl_make_fixnum(/*MP::SEMAPHORE*/1490));
    }
    ecl_disable_interrupts_env(the_env);
    ecl_mutex_lock(&semaphore->semaphore.mutex);
    semaphore->semaphore.counter += n;
    for (; n > 0; n--) {
      ecl_cond_var_signal(&semaphore->semaphore.cv);
    }
    ecl_mutex_unlock(&semaphore->semaphore.mutex);
    ecl_enable_interrupts_env(the_env);
    {
the_env->nvalues = 0; return ECL_NIL;
#line 82
}
;
  } }

static inline void
semaphore_wait_unprotected(cl_object semaphore, cl_object count, cl_object timeout)
{
  int rc;
  cl_env_ptr the_env = ecl_process_env();
  cl_fixnum counter = fixnnint(count);
  ecl_mutex_t *mutex = &semaphore->semaphore.mutex;
  ecl_cond_var_t *cv = &semaphore->semaphore.cv;
  if (timeout == ECL_NIL) {
    do {
      ECL_SETQ(the_env, ECL_INTERRUPTS_ENABLED, ECL_T);
      ecl_cond_var_wait(cv, mutex);
      ECL_SETQ(the_env, ECL_INTERRUPTS_ENABLED, ECL_NIL);
    } while (semaphore->semaphore.counter < counter);
  } else {
    cl_object deadline = ecl_plus(cl_get_internal_real_time(),
                                  ecl_times(timeout, ecl_make_fixnum(1000000)));
    double seconds = ecl_to_double(timeout);
    do {
      ecl_setq(the_env, ECL_INTERRUPTS_ENABLED, ECL_T);
      rc = ecl_cond_var_timedwait(cv, mutex, seconds);
      ecl_setq(the_env, ECL_INTERRUPTS_ENABLED, ECL_NIL);
      timeout = ecl_minus(deadline, cl_get_internal_real_time());
      seconds = ecl_to_double(timeout);
    } while(semaphore->semaphore.counter < counter
            && rc != ECL_MUTEX_TIMEOUT
            && seconds >= 0);
  }
}

cl_object
mp_semaphore_wait(cl_object semaphore, cl_object count, cl_object timeout)
{
  cl_env_ptr the_env = ecl_process_env();
  cl_fixnum counter = fixnnint(count);
  volatile cl_object output;
  unlikely_if (ecl_t_of(semaphore) != t_semaphore) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::SEMAPHORE-WAIT*/1493), semaphore, ecl_make_fixnum(/*MP::SEMAPHORE*/1490));
  }
  ecl_bds_bind(the_env, ECL_INTERRUPTS_ENABLED, ECL_NIL);
  ecl_mutex_lock(&semaphore->semaphore.mutex);
  if (semaphore->semaphore.counter >= counter) {
    output = ecl_make_fixnum(semaphore->semaphore.counter);
    semaphore->semaphore.counter -= counter;
    ecl_mutex_unlock(&semaphore->semaphore.mutex);
  } else if (timeout == ECL_NIL || ecl_plusp(timeout)) {
    semaphore->semaphore.wait_count++;
    ECL_UNWIND_PROTECT_BEGIN(the_env) {
      semaphore_wait_unprotected(semaphore, count, timeout);
      if (semaphore->semaphore.counter >= counter) {
        output = ecl_make_fixnum(semaphore->semaphore.counter);
        semaphore->semaphore.counter -= counter;
      } else {
        output = ECL_NIL;
      }
    } ECL_UNWIND_PROTECT_THREAD_SAFE_EXIT {
      semaphore->semaphore.wait_count--;
      ecl_mutex_unlock(&semaphore->semaphore.mutex);
    } ECL_UNWIND_PROTECT_THREAD_SAFE_END;
  } else {
    output = ECL_NIL;
    ecl_mutex_unlock(&semaphore->semaphore.mutex);
  }
  ecl_bds_unwind1(the_env);
  ecl_check_pending_interrupts(the_env);
  ecl_return1(the_env, output);
}

#line 154
cl_object mp_wait_on_semaphore(cl_narg narg, cl_object semaphore, ...)
{
#line 154

  #line 155
#if defined(__clang__) || defined(__GNUC__)
	__attribute__((unused)) const cl_env_ptr the_env = ecl_process_env();
#else
	const cl_env_ptr the_env = ecl_process_env();
#endif
#line 155
	static cl_object KEYS[2] = {(cl_object)(cl_symbols+1499), (cl_object)(cl_symbols+1500)};
	cl_object count;
	cl_object timeout;
#line 155
	cl_object KEY_VARS[4];
#line 155
	ecl_va_list ARGS;
	ecl_va_start(ARGS, semaphore, narg, 1);
#line 155
	if (ecl_unlikely(narg < 1)) FEwrong_num_arguments(ecl_make_fixnum(1494));
#line 155
	cl_parse_key(ARGS, 2, KEYS, KEY_VARS, NULL, 0);
#line 155
	if (KEY_VARS[2]==ECL_NIL) {
#line 155
	  count = ecl_make_fixnum(1);
	} else {
#line 155
	  count = KEY_VARS[0];
	}
#line 155
	if (KEY_VARS[3]==ECL_NIL) {
#line 155
	  timeout = ECL_NIL;
	} else {
#line 155
	  timeout = KEY_VARS[1];
	}
#line 155
{
    cl_object output = mp_semaphore_wait(semaphore, count, timeout);
    {
#line 157
	#line 157
	cl_object __value0 = output;
#line 157
	the_env->nvalues = 1;
#line 157
	the_env->values[0] = __value0;
#line 157
	ecl_va_end(ARGS);
#line 157
	return __value0;
#line 157
}
;
  } }

#line 160
cl_object mp_try_get_semaphore(cl_narg narg, cl_object semaphore, ...)
{
#line 160

  #line 161
#if defined(__clang__) || defined(__GNUC__)
	__attribute__((unused)) const cl_env_ptr the_env = ecl_process_env();
#else
	const cl_env_ptr the_env = ecl_process_env();
#endif
#line 161
	cl_object count;
#line 161
	va_list ARGS;
	va_start(ARGS, semaphore);
#line 161
	if (ecl_unlikely(narg < 1|| narg > 2)) FEwrong_num_arguments(ecl_make_fixnum(1495));
#line 161
	if (narg > 1) {
#line 161
		count = va_arg(ARGS,cl_object);
#line 161
	} else {
#line 161
		count = ecl_make_fixnum(1);
#line 161
	}
#line 161
{
    cl_object timeout = ecl_make_fixnum(0);
    cl_object output = mp_semaphore_wait(semaphore, count, timeout);
    {
#line 164
	#line 164
	cl_object __value0 = output;
#line 164
	the_env->nvalues = 1;
#line 164
	the_env->values[0] = __value0;
#line 164
	va_end(ARGS);
#line 164
	return __value0;
#line 164
}
;
  } }
