/*
 * Copyright (c) 2011, ETH Zurich.
 * All rights reserved.
 *
 * This file is distributed under the terms in the attached LICENSE file.
 * If you do not find this file, copies can be found by writing to:
 * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
 */

#ifndef _THC_STUBS_H_
#define _THC_STUBS_H_

#ifdef BARRELFISH
#include <flounder/flounder.h>
#include <barrelfish/idc_export.h>
#include <barrelfish/static_assert.h>
#include <thc/thc.h>
#endif // BARRELFISH

#ifdef _MSC_VER

typedef int32_t idc_control_t;

struct event_mutex
{
    int dummy;
};

#define STATIC_ASSERT(x, msg) C_ASSERT(x)

#endif

//......................................................................
//
// This must match the layout of the binding structures generated by
// Flounder.  The flounder-generated code asserts that the offsets
// and sizes of the common fields match those in a specific binding
// structure.

struct common_binding;

typedef  void common_continuation_fn(struct common_binding *_binding);
typedef  bool common_can_send_fn(struct common_binding *_binding);
typedef  errval_t common_register_send_fn(struct common_binding *_binding, struct waitset *ws, struct event_closure _continuation);
typedef  errval_t common_change_waitset_fn(struct common_binding *_binding, struct waitset *ws);
typedef  errval_t common_control_fn(struct common_binding *_binding, idc_control_t control);
typedef  void common_error_handler_fn(struct common_binding *_binding, errval_t err);

struct common_binding {
  void *st;
  struct waitset *waitset;
  struct event_mutex mutex;
  common_can_send_fn *can_send;
  common_register_send_fn *register_send;
  common_change_waitset_fn *change_waitset;
  common_control_fn *control;
  common_error_handler_fn *error_handler;
};

// The THC stubs use the following function-like macro to check that they
// layout of a concrete binding structure matches the common_binding
// structure.

#define CHECK_FIELD(_t,_f)						\
  STATIC_ASSERT(offsetof(struct common_binding, _f)==offsetof(_t,_f) &&	\
		sizeof(((struct common_binding*)NULL)->_f)==sizeof(((_t*)NULL)->_f), "OFFSET mismatch"); \

//......................................................................
//
// Common functions and data types used in the THC stubs.

#define NO_DEMUX (uint64_t)-1

// Each running receive operation maintains a thc_receier_info struct
// for synchronization with bottom-half functions invoked via the
// underlying binding's rx_vtbl

struct thc_receiver_info {
  // AWE for the bottom-half to resume.  NULL implies that the
  // top-half has already been resumed (and so future
  // bottom-half invocations should not attempt to supply a
  // second message to it)
  awe_t *waiter;

  // Pointer to union-of-pointers-to-args to show where to
  // put the incoming data.
  void *args;

  // Pointer to where to place incoming message type
  volatile int *msg;

  // Demux information (for out-of-order RPC receivers).
  volatile uint64_t demux;

  struct thc_receiver_info *next;
};


// Common fields for each THC binding

struct thc_per_binding_state_t {
  // Concurrency control over the fields in the THC binding structure
  thc_lock_t thc_binding_lock;

  // Saved state of the thread waiting for an upcall on being able
  // to send
  awe_t *waiting_sender;

  // Saved state of the thread waiting for an upcall on a send being complete
  // (in thc_complete_send).  NB: at most one of waiting_sender and
  // waiting_complete_sender should be used at a time, since both are
  // protected by the thc_next_sender semaphore.  The fields are kept
  // separate for clarity.
  awe_t *waiting_complete_sender;
  int thc_send_complete;

  // Semaphore use to choose which sender calls register_send
  thc_sem_t thc_next_sender;

  // Flag set when a register_send call has been made, but the critical
  // section in the subsequent event handler has not yet run (a thread
  // can enter thc_await_send and find this flag set because an
  // earlier thc_await_send_x was canceled)
  int send_possible_event_requested;
};

// Common fields for each message on each THC binding; a head of a
// linked list of thc_receiver_info structures for the running
// receive operations, and a pointer to a condition variable (indicating
// whether a bottom-half function is waiting).
//
// num_discard counts the number of messages to discard in the bottom-half
// before delivering any.  This is used when sequential RPC responses
// arrive for receivers that have been canceled.

struct thc_per_recv_t {
  struct thc_receiver_info * volatile r;
  thc_condvar_t cv_bh;
  volatile int num_bh;
  int num_discard;
  thc_lock_t fifo_rpc_lock;
  thc_queue_t fifo_rpc_q;
  uint64_t fifo_rpc_next_recv;
};

// Initialization functions

extern void thc_init_per_binding_state(struct thc_per_binding_state_t *thc);
extern void thc_init_per_recv_state(struct thc_per_recv_t *recv);

// Bottom-half receive operations

static inline struct thc_receiver_info *thc_start_bh(struct thc_per_binding_state_t *thc,
                                       void *f,
                                       struct thc_per_recv_t *recv) {
  thc_lock_acquire(&thc->thc_binding_lock);

  // Wait for a receiver
  while (recv->num_discard == 0 &&
         (recv->r == NULL || recv->r->waiter == NULL)) {
    recv->num_bh++;
    thc_condvar_wait(&recv->cv_bh, &thc->thc_binding_lock);
    recv->num_bh--;
  }

  if (recv->num_discard > 0) {
    // Message is a response to an RPC that has been canceled
    recv->num_discard--;
    thc_lock_release(&thc->thc_binding_lock);
    return NULL;
  } else {
    assert(recv->r->next == NULL && recv->r->demux == NO_DEMUX &&
           "Expected single non-demux receiver");
    return recv->r;
  }
}

extern struct thc_receiver_info *thc_start_demuxable_bh(struct thc_per_binding_state_t *thc,
                                                        void *common_binding,
                                                        struct thc_per_recv_t *recv,
                                                        uint64_t demux);

static inline void thc_end_bh(struct thc_per_binding_state_t *thc,
                void *f,
                struct thc_per_recv_t *recv,
                struct thc_receiver_info *rxi) {
  assert(recv->r != NULL && "thc_start_bh: no-one waiting");
  assert(rxi->waiter != NULL);
  awe_t *awe = rxi->waiter;
  rxi->waiter = NULL;
  THCYieldTo(awe);
  // thc->thc_binding_lock passed to the receiver
}

// Top-half single-message receive
extern errval_t thc_receive(struct thc_per_binding_state_t *thc,
                            struct thc_per_recv_t *recv,
                            struct thc_receiver_info *rxi);
extern errval_t thc_receive_x(struct thc_per_binding_state_t *thc,
                              struct thc_per_recv_t *recv,
                              struct thc_receiver_info *rxi);
// Cause the next "n" messages to be discarded on receipt
extern void thc_discard(struct thc_per_binding_state_t *thc,
                        struct thc_per_recv_t *recv,
                        uint64_t n);

// Top-half demux-message receive.
//
// Valid call sequences are start->receive
//                          start->receive_x
//                      and start->cancel
extern void thc_start_receive_demux(struct thc_per_binding_state_t *thc,
                                    struct thc_per_recv_t *recv,
                                    struct thc_receiver_info *rxi);
extern errval_t thc_receive_demux(struct thc_per_binding_state_t *thc,
                                  struct thc_per_recv_t *recv,
                                  struct thc_receiver_info *rxi);
extern errval_t thc_receive_demux_x(struct thc_per_binding_state_t *thc,
                                    struct thc_per_recv_t *recv,
                                    struct thc_receiver_info *rxi);
extern errval_t thc_cancel_receive_demux(struct thc_per_binding_state_t *thc,
                                         struct thc_per_recv_t *recv,
                                         struct thc_receiver_info *rxi);

// Top-half receive-any
extern void thc_start_receive_any(struct thc_per_binding_state_t *thc);
extern void thc_start_receiving(struct thc_per_binding_state_t *thc,
                                struct thc_per_recv_t *recv,
                                struct thc_receiver_info *rxi);
extern void thc_wait_receive_any(struct thc_per_binding_state_t *thc,
                                 struct thc_receiver_info *rxi);
extern errval_t thc_wait_receive_any_x(struct thc_per_binding_state_t *thc,
                                       struct thc_receiver_info *rxi);
extern void thc_stop_receiving(struct thc_per_binding_state_t *thc,
                               struct thc_per_recv_t *recv,
                               struct thc_receiver_info *rxi);
extern void thc_end_receive_any(struct thc_per_binding_state_t *thc);

// Top-half send
extern void thc_await_send(struct thc_per_binding_state_t *thc,
                           void *common_binding);
extern errval_t thc_await_send_x(struct thc_per_binding_state_t *thc,
                                 void *common_binding);
extern void thc_complete_send(struct thc_per_binding_state_t *thc,
                              void *common_binding);

// Callback to execute when a send is complete (to synchronoize with
// thc_complete_send)
extern void thc_complete_send_cb(void *f);


#endif
