/**@name Synchronization and Communication for Object-Oriented Programming (SCOOP)
 *
 * The intended semantics of this file is to provide SCOOP facilities as defined by Bertrand Meyer
 * (see http://www.eiffel.com/doc/manuals/technology/concurrency/CONCURRENCY.html)
 *
 * @author Philippe Ribet, Cyril Adrian
 */
//@{
#define SE_SCOOP 1

typedef struct se_subsystem     se_subsystem_t;
typedef struct se_subsystem_vft se_subsystem_vft_t;
typedef struct se_origin        se_origin_t;

/**
 * Remote object definition
 */
typedef struct sS0 {
  // Identifier of the object
  int id;
  // Reference to the subsystem the referenced object belongs to
  se_subsystem_t* subsystem;
  // Reference to the local object
  T0* ref;
} sT0;

/**
 * The interface of each subsystem
 */
struct se_subsystem_vft {

#ifndef SE_BOOST
  /**
   * The id: "type" of subsystem. Not present in boost mode.
   */
  int id;
#endif

  /**
   * Create a new subsystem
   */
  se_subsystem_t* (*new)(char* name);

  /**
   * Create a subsystem mapping the current processor. Used only for root
   * processor (init).
   */
  se_subsystem_t* (*from_root)(char* name);

  /**
   * Start the subsystem event loop.
   */
  void (*run)(se_subsystem_t* root);

#ifdef SE_GC_LIB
  /** GC sweep.
   *
   * Called by the GC when an instance in this subsystem is to be collected.
   */
  void (*gc_sweep)(T0* a);
#endif

  /**
   * Destroy the resources associated with the subsystem
   */
  void (*delete)(se_subsystem_t* subsystem);

  /**
   * Put a command in the queue of the subsystem
   */
  void (*command)(se_subsystem_t* subsystem,
                  void (*command)(void*,int,void*), // data, data length, NULL
                  void* data,
                  int length);

  /**
   * Put a query in the queue of the subsystem, and {\em wait} for the result
   * to be available.
   */
  void (*query)(se_subsystem_t* subsystem,
                void (*command)(void*,int,void*), // data, data length, result address
                void* data,
                int length,
                void* result);

#ifndef SE_BOOST
  /** Dump Stack */
  //@{

  /**
   * Print the runstack of the subsystem
   */
  void (*print_run_time_stack)(se_subsystem_t* subsystem);

  /**
   * Print all the stack traces of the same processor type
   */
  void (*print_all_stack_traces)();

  /**
   * Get the Dump Stack, and lock for operations on it
   */
  void* (*get_dst_and_lock)(se_subsystem_t* subsystem);

  /**
   * Set the Dump Stack, and unlock; no further operations should be performed
   * on it
   */
  void (*set_dst_and_unlock)(se_subsystem_t* subsystem, void* dst);

  //@}
#endif

  /**
   * Increment the number of instances in the subsystem
   * @return the new count
   */
  int (*inc_count)(se_subsystem_t* subsystem);

  /**
   * Decrement the number of instances in the subsystem
   * @return the new count
   */
  int (*dec_count)(se_subsystem_t* subsystem);

  /**
   * Set a new thread-once
   */
  void (*set_once)(se_subsystem_t* subsystem, char* key, void* once);

  /**
   * Returns non-0 if the thread-once object was defined
   */
  int (*is_set_once)(se_subsystem_t* subsystem, char* key);

  /**
   * Get a thread-once
   */
  void* (*get_once)(se_subsystem_t* subsystem, char* key);

  /** Lock Manager */
  //@{

  /**
   * Returns non-zero if waiting is useful (it's useless if the providers are
   * all already locked by the client)
   */
  int (*must_wait_for_providers)(se_subsystem_t* subsystem, se_subsystem_t** providers, int count);

  /**
   * The wait loop: wait for all providers to signal the subsystem. Returns
   * only when the providers can safely be locked.
   */
  void (*wait_for_providers)(se_subsystem_t* subsystem, se_subsystem_t** providers, int count);

  /**
   * Unqueue the client from the subsystem. It must be the first waiter.
   */
  void (*unqueue_providers)(se_subsystem_t* subsystem, se_subsystem_t** providers, int count);

  /**
   * USED INTERNALLY to set the current query originator
   */
  void (*set_origin)(se_subsystem_t* subsystem, se_origin_t* origin);

  /**
   * USED INTERNALLY to get the current query originator
   */
  se_origin_t* (*get_origin)(se_subsystem_t* subsystem);

  /**
   * USED INTERNALLY for inter-subsystem communication: tell the subsystem it
   * is ready to enter the provider. It means that the subsystem is at the top
   * of the provider's lock queue.
   */
  void (*signal)(se_subsystem_t* subsystem, se_subsystem_t* provider);

  /**
   * USED INTERNALLY for inter-subsystem communication: tell the subsystem it
   * was enqueued
   */
  void (*wait_commit)(se_subsystem_t* subsystem, se_subsystem_t* provider);

  /**
   * USED INTERNALLY for inter-subsystem communication: tell the subsystem it
   * was dequeued
   */
  void (*unqueue_waiter_commit)(se_subsystem_t* subsystem, se_subsystem_t* provider);

  //@}
};

/**
 * Used to represent the origin of a query cycle
 */
struct se_origin {
  /**
   * A unique identifier
   */
  long long id;
  /**
   * The processor that initiated the cycle
   */
  se_subsystem_t* subsystem;
  /**
   * A reference counter
   */
  int ref_count;

#ifndef SE_BOOST
  /// Used to detect bad memory management
  int is_free;
#endif

  /// Used by memory management
  se_origin_t* next;
};

/**
 * The most important structure: there is one such per processor.
 */
struct se_subsystem {

#ifndef SE_BOOST
  /**
   * The id: "type" of subsystem. Not present in boost mode.
   */
  int id;
#endif

  /**
   * The subsystems are chained between them.
   *
   * @see se_system
   */
  se_subsystem_t* next;

  /// The unique identifier used e.g. for lock sorting
  int num;

  /// Number of instances in this subsystem
  int count;

  /// Name of the subsystem
  char* name;

  /**
   * The subsystems contain their own functions a la unix
   */
  se_subsystem_vft_t vft;
};

/**
 * Interface used by the SmallEiffel generated runtime
 */
//@{

/**
 * Create a new subsystem. The type must exist.
 */
extern se_subsystem_t* se_new_subsystem(int type, char* name);

/**
 * Create a new subsystem mapping the current thread. Used only for root
 * thread (init). The type must exist.
 */
extern se_subsystem_t* se_init_separate_root(int type, char* name);

#ifndef SE_BOOST
/**
 * Print the stack traces of all threads
 */
extern void se_print_all_stack_traces();
#endif

/**
 * Wait for the current thread to be ready (sends it a dummy query)
 */
extern void se_join_subsystem(se_subsystem_t* subsystem);

//@}

/**
 * Interface used by the SCOOP internals (the subsystems
 * implementations). Those functions are defined in scoop.c
 */
//@{

/**
 * Register a new type of subsystem
 */
extern void se_register_vft(int type, se_subsystem_vft_t* vft);

#ifdef SE_BOOST

#define scoop_check_call(x) (x)
#define scoop_check(x) ((void)0)

#else

/// Executes x; if it returns non-zero, exits.
#define scoop_check_call(x) \
  do { \
    if (x) { \
      fprintf(stderr, "SCOOP internals: error while calling \"%s\" (line %d in %s)\n", #x, __LINE__, __FILE__); se_print_run_time_stack(); exit(1); \
    } \
  } while (0)

/// If x is non-zero, exits.
#define scoop_check(x) \
  do { \
    if (!(x)) { \
      fprintf(stderr, "SCOOP internals: check \"%s\" failed (line %d in %s)\n", #x, __LINE__, __FILE__); se_print_run_time_stack(); exit(1); \
    } \
  } while (0)

#endif

//@}

#define is_local(object) (get_subsystem(object, NULL) == NULL)
#define is_separate(object) (get_subsystem(object, NULL) != NULL)

T0* as_local(T0* object);
int scoop_cmp(T0* o1, T0* o2);

//@}
