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

/*
 * mailbox.d -- thread communication queue
 *
 * Copyright (c) 2012 Juan Jose Garcia Ripoll
 *
 * See file 'LICENSE' for the copyright details.
 *
 */

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

/* NOTE: The mailbox functions are not interrupt safe. */

cl_object
ecl_make_mailbox(cl_object name, cl_fixnum count)
{
  cl_env_ptr env = ecl_process_env();
  cl_object output = ecl_alloc_object(t_mailbox);
  output->mailbox.name = name;
  output->mailbox.data = si_make_vector(ECL_T, /* element type */
                                        ecl_make_fixnum(count), /* size */
                                        ECL_NIL, /* adjustable */
                                        ECL_NIL, /* fill pointer */
                                        ECL_NIL, /* displaced to */
                                        ECL_NIL); /* displacement */
  output->mailbox.message_count = 0;
  output->mailbox.read_pointer = 0;
  output->mailbox.write_pointer = 0;
  ecl_disable_interrupts_env(env);
  ecl_mutex_init(&output->mailbox.mutex, FALSE);
  ecl_cond_var_init(&output->mailbox.reader_cv);
  ecl_cond_var_init(&output->mailbox.writer_cv);
  ecl_set_finalizer_unprotected(output, ECL_T);
  ecl_enable_interrupts_env(env);
  return output;
}

#line 42
cl_object mp_make_mailbox(cl_narg narg, ...)
{
#line 42

  #line 43
#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 43
	static cl_object KEYS[2] = {(cl_object)(cl_symbols+1309), (cl_object)(cl_symbols+1499)};
	cl_object name;
	cl_object count;
#line 43
	cl_object KEY_VARS[4];
#line 43
	ecl_va_list ARGS;
	ecl_va_start(ARGS, narg, narg, 0);
#line 43
	if (ecl_unlikely(narg < 0)) FEwrong_num_arguments(ecl_make_fixnum(1513));
#line 43
	cl_parse_key(ARGS, 2, KEYS, KEY_VARS, NULL, 0);
#line 43
	if (KEY_VARS[2]==ECL_NIL) {
#line 43
	  name = ECL_NIL;
	} else {
#line 43
	  name = KEY_VARS[0];
	}
#line 43
	if (KEY_VARS[3]==ECL_NIL) {
#line 43
	  count = ecl_make_fixnum(128);
	} else {
#line 43
	  count = KEY_VARS[1];
	}
#line 43
{
    {
#line 44
	#line 44
	cl_object __value0 = ecl_make_mailbox(name, fixnnint(count));
#line 44
	the_env->nvalues = 1;
#line 44
	the_env->values[0] = __value0;
#line 44
	ecl_va_end(ARGS);
#line 44
	return __value0;
#line 44
}
;
  } }

cl_object
mp_mailbox_name(cl_object mailbox)
{
  cl_env_ptr env = ecl_process_env();
  unlikely_if (ecl_t_of(mailbox) != t_mailbox) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::MAILBOX-NAME*/1514), mailbox, ecl_make_fixnum(/*MP::MAILBOX*/1512));
  }
  ecl_return1(env, mailbox->mailbox.name);
}

cl_object
mp_mailbox_count(cl_object mailbox)
{
  cl_env_ptr env = ecl_process_env();
  unlikely_if (ecl_t_of(mailbox) != t_mailbox) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::MAILBOX-COUNT*/1515), mailbox, ecl_make_fixnum(/*MP::MAILBOX*/1512));
  }
  ecl_return1(env, ecl_make_fixnum(mailbox->mailbox.data->vector.dim));
}

cl_object
mp_mailbox_empty_p(cl_object mailbox)
{
  cl_env_ptr env = ecl_process_env();
  unlikely_if (ecl_t_of(mailbox) != t_mailbox) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::MAILBOX-EMPTY-P*/1516), mailbox, ecl_make_fixnum(/*MP::MAILBOX*/1512));
  }
  ecl_return1(env, mailbox->mailbox.message_count? ECL_NIL : ECL_T);
}

static cl_object
read_message(cl_object mailbox)
{
  cl_object output;
  cl_fixnum ndx = mailbox->mailbox.read_pointer++;
  if (mailbox->mailbox.read_pointer >= mailbox->mailbox.data->vector.dim) {
    mailbox->mailbox.read_pointer = 0;
  }
  output = mailbox->mailbox.data->vector.self.t[ndx];
  mailbox->mailbox.message_count--;
  ecl_cond_var_signal(&mailbox->mailbox.writer_cv);
  return output;
}

cl_object
mp_mailbox_read(cl_object mailbox)
{
  cl_env_ptr env = ecl_process_env();
  cl_object output;
  unlikely_if (ecl_t_of(mailbox) != t_mailbox) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::MAILBOX-READ*/1517), mailbox, ecl_make_fixnum(/*MP::MAILBOX*/1512));
  }
  ecl_mutex_lock(&mailbox->mailbox.mutex);
  {
    while (mailbox->mailbox.message_count == 0) {
      ecl_cond_var_wait(&mailbox->mailbox.reader_cv, &mailbox->mailbox.mutex);
    }
    output = read_message(mailbox);
  }
  ecl_mutex_unlock(&mailbox->mailbox.mutex);
  ecl_return1(env, output);
}

cl_object
mp_mailbox_try_read(cl_object mailbox)
{
  cl_env_ptr env = ecl_process_env();
  cl_object output;
  unlikely_if (ecl_t_of(mailbox) != t_mailbox) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::MAILBOX-TRY-READ*/1518), mailbox, ecl_make_fixnum(/*MP::MAILBOX*/1512));
  }
  ecl_mutex_lock(&mailbox->mailbox.mutex);
  {
    if (mailbox->mailbox.message_count == 0) {
      output = ECL_NIL;
    } else {
      output = read_message(mailbox);
    }
  }
  ecl_mutex_unlock(&mailbox->mailbox.mutex);
  ecl_return1(env, output);
}

static void
store_message(cl_object mailbox, cl_object msg)
{
  cl_fixnum ndx = mailbox->mailbox.write_pointer++;
  if (mailbox->mailbox.write_pointer >= mailbox->mailbox.data->vector.dim) {
    mailbox->mailbox.write_pointer = 0;
  }
  mailbox->mailbox.data->vector.self.t[ndx] = msg;
  mailbox->mailbox.message_count++;
  ecl_cond_var_signal(&mailbox->mailbox.reader_cv);
}

cl_object
mp_mailbox_send(cl_object mailbox, cl_object msg)
{
  cl_env_ptr env = ecl_process_env();
  unlikely_if (ecl_t_of(mailbox) != t_mailbox) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::MAILBOX-SEND*/1519), mailbox, ecl_make_fixnum(/*MP::MAILBOX*/1512));
  }
  ecl_mutex_lock(&mailbox->mailbox.mutex);
  {
    while (mailbox->mailbox.message_count == mailbox->mailbox.data->vector.dim) {
      ecl_cond_var_wait(&mailbox->mailbox.writer_cv, &mailbox->mailbox.mutex);
    }
    store_message(mailbox, msg);
  }
  ecl_mutex_unlock(&mailbox->mailbox.mutex);
  ecl_return1(env, msg);
}

cl_object
mp_mailbox_try_send(cl_object mailbox, cl_object msg)
{
  cl_env_ptr env = ecl_process_env();
  cl_object output;
  unlikely_if (ecl_t_of(mailbox) != t_mailbox) {
    FEwrong_type_only_arg(ecl_make_fixnum(/*MP::MAILBOX-TRY-SEND*/1520), mailbox, ecl_make_fixnum(/*MP::MAILBOX*/1512));
  }
  ecl_mutex_lock(&mailbox->mailbox.mutex);
  {
    if (mailbox->mailbox.message_count == mailbox->mailbox.data->vector.dim) {
      output = ECL_NIL;
    } else {
      store_message(mailbox, msg);
      output = msg;
    }
  }
  ecl_mutex_unlock(&mailbox->mailbox.mutex);
  ecl_return1(env, output);
}

