#ifndef SCHEME_FUTURES_H
#define SCHEME_FUTURES_H

#ifdef MZ_USE_FUTURES

typedef Scheme_Object **(*prim_on_demand_t)(Scheme_Object **, Scheme_Object **, int);
typedef Scheme_Object* (*prim_obj_int_pobj_obj_t)(Scheme_Object*, int, Scheme_Object**);
typedef Scheme_Object* (*prim_int_pobj_obj_t)(int, Scheme_Object**);
typedef Scheme_Object* (*prim_int_pobj_obj_obj_t)(int, Scheme_Object**, Scheme_Object*);
typedef void* (*prim_pvoid_pvoid_pvoid_t)(void*, void*);
typedef void (*prim_allocate_values_t)(int, Scheme_Thread *);

#define PENDING 0
#define RUNNING 1
#define WAITING_FOR_PRIM 2
#define FINISHED 3
#define PENDING_OVERSIZE 4
#define HANDLING_PRIM 5
#define WAITING_FOR_FSEMA 6
#define SUSPENDED 7

/* FSRC_OTHER means: descriptive string is provided for logging,
   called function *DOES NOT NEED* to lookup continuation marks. */
#define FSRC_OTHER 0
/* FSRC_RATOR means: Racket function provided, so use it in logging,
   called function can lookup continuation marks. */
#define FSRC_RATOR 1
/* FSRC_PRIM means: Racket primitive provided, so use it in logging,
   called function can lookup continuation marks. */
#define FSRC_PRIM  2
/* FSRC_MARKS means: like FSRC_OTHER, but
   called function may need to lookup continuation marks. */
#define FSRC_MARKS 3

typedef struct future_t {
  Scheme_Object so;

  int id;
  int thread_short_id;

  int status;
  /* The status field is the main locking mechanism. It
     should only be read and written when holding a lock
     (and all associated fields for a status should be 
     set at the same time). */

  mzrt_sema *can_continue_sema;
  /* this semcpahore is non_NULL when a future thread is blocked
     while trying to run the future; th want_lw flag may be set in
     that case */

  Scheme_Object *orig_lambda;
  void *code;

  Scheme_Custodian *cust; /* an approximate custodian; don't use a future
                             thread if this custodian is shut down */

  /* Runtime call stuff */

  char want_lw; 
  /* flag to indicate waiting for lw capture; if this flag is set,
     then the future thread currently running the future must be
     blocked, and the runtime thread must not already be working on
     behalf of the future; since a future thread is blocked on this
     future, then can_continue_sema is normally set, but the runtime
     thread sets can_continue_sema to NULL while trying to capture the
     continuation --- in case anoter thread tries to let the original
     future thread continue because it was blocked on a touch for a
     future that completed; the `want_lw' flag should be changed only
     while holding a lock */

  char in_queue_waiting_for_lwc;
  /* flag to indicate whether the future is in the "waiting for lwc"
     queue; the future might be in the queue even if want_lw is set to
     0, and so this flag just prevents  */

  char in_touch_queue;   
  /* like `in_queue_waiting_for_lwc' but for being in a `touch'
     future */

  char in_future_specific_touch_queue; /* a back-door argument */

  char rt_prim_is_atomic;
  /* when a future thread is blocked on this future, it sets
     `rt_prim_is_atomic' if the blocking operation can run
     in any thread atomically (i.e., it's a "synchronizing" 
     operation insteda of a general blocking operation) */

  double time_of_request;
  const char *source_of_request;
  int source_type;

  uintptr_t alloc_retval;
  uintptr_t alloc_sz_retval;
  int alloc_retval_counter;

  void *prim_func;
  int prim_protocol;
  Scheme_Object *arg_s0;
  const Scheme_Object *arg_t0;
  Scheme_Object **arg_S0;
  Scheme_Bucket *arg_b0;
  int arg_i0;
  intptr_t arg_l0;
  size_t arg_z0;
  Scheme_Native_Closure_Data *arg_n0;
  Scheme_Object *arg_s1;
  const Scheme_Object *arg_t1;
  Scheme_Object **arg_S1;
  int arg_i1;
  intptr_t arg_l1;
  Scheme_Object *arg_s2;
  Scheme_Object **arg_S2;
  int arg_i2;
  void *arg_p2;

  const char *arg_str0;
  const char *arg_str1;
  int arg_i3;
  Scheme_Object **arg_S4;

  Scheme_Thread *arg_p;
  /* when a future thread is blocked while running this future,
     `arg_p' s set along with the blocking-operation arguments to
     indicate the future thread's (fake) Racket thread, which has the
     runstack, etc. */
  struct Scheme_Current_LWC *lwc;
  /* when a future thread is blocked while running this future,
     if `want_lw' is set, then `lwc' points to information for
     capturing a lightweight continuation */
  struct Scheme_Future_Thread_State *fts;
  /* when a future thread is blocked while running this future,
     `fts' is set to identify the future thread */

  struct Scheme_Lightweight_Continuation *suspended_lw;
  /* holds a lightweight continuation captured for the operation,
     if any */
  int maybe_suspended_lw;
  /* set to 1 with suspended_lw untl test in runtime thread; this
     extra flag avoids spinning if the suspended continuation
     cannot be resumed in the main thread for some reason */

  Scheme_Object *retval_s;
  void *retval_p; /* use only with conservative GC */
  MZ_MARK_STACK_TYPE retval_m;
  int retval_i;
  char no_retval;
  char retval_is_rs_plus_two; /* => special result handling for on-demand JIT */

  Scheme_Object **multiple_array;
  int multiple_count;

  Scheme_Object *tail_rator;
  Scheme_Object **tail_rands;
  int num_tail_rands;

  Scheme_Object *retval;
  struct future_t *prev;
  struct future_t *next;

  struct future_t *next_waiting_atomic;
  struct future_t *next_waiting_lwc;
  struct future_t *next_waiting_touch;

  struct future_t *prev_in_fsema_queue;
  struct future_t *next_in_fsema_queue;

  Scheme_Object *touching; /* a list of weak pointers to futures touching this one */
} future_t;

typedef struct fsemaphore_t {
  Scheme_Object so;

  int ready;
  mzrt_mutex *mut; 
  future_t *queue_front;
  future_t *queue_end;
} fsemaphore_t;

/* Primitive instrumentation stuff */

/* Exceptions */ 
void scheme_wrong_type_from_ft(const char *who, const char *expected_type, int what, int argc, Scheme_Object **argv);

#define SCHEME_WRONG_TYPE_MAYBE_IN_FT(who, expected_type, what, argc, argv) \
  if (scheme_use_rtcall) \
    scheme_wrong_type_from_ft(who, expected_type, what, argc, argv); \
  else \
    scheme_wrong_type(who, expected_type, what, argc, argv);

/* Signature flags for primitive invocations */
#define SIG_ON_DEMAND          1
#define SIG_ALLOC              2
#define SIG_ALLOC_MARK_SEGMENT 3
#define SIG_ALLOC_VALUES       4
#define SIG_MAKE_FSEMAPHORE    5
#define SIG_FUTURE             6
#define SIG_WRONG_TYPE_EXN     7

# include "jit_ts_protos.h"

extern Scheme_Object *scheme_ts_scheme_force_value_same_mark(Scheme_Object *v);

extern Scheme_Object **scheme_rtcall_on_demand(const char *who, int src_type, prim_on_demand_t f, Scheme_Object **argv);
extern uintptr_t scheme_rtcall_alloc(const char *who, int src_type);
extern void scheme_rtcall_new_mark_segment(Scheme_Thread *p);
extern void scheme_rtcall_allocate_values(const char *who, int src_type, int count, Scheme_Thread *t, 
                                          prim_allocate_values_t f);
extern Scheme_Object *scheme_rtcall_make_fsemaphore(const char *who, int src_type, Scheme_Object *ready);
extern Scheme_Object *scheme_rtcall_make_future(const char *who, int src_type, Scheme_Object *proc);

void scheme_future_block_until_gc();
void scheme_future_continue_after_gc();
void scheme_check_future_work();
void scheme_future_gc_pause();
void scheme_future_check_custodians();

#endif /* MZ_USE_FUTURES */

/* always defined: */
Scheme_Object *scheme_future(int argc, Scheme_Object *argv[]);
Scheme_Object *scheme_current_future(int argc, Scheme_Object *argv[]);
Scheme_Object *scheme_fsemaphore_p(int argc, Scheme_Object *argv[]);

Scheme_Object *scheme_fsemaphore_count(int argc, Scheme_Object *argv[]);
Scheme_Object *scheme_make_fsemaphore(int argc, Scheme_Object *argv[]);
Scheme_Object *scheme_make_fsemaphore_inl(Scheme_Object *ready);
Scheme_Object *scheme_fsemaphore_wait(int argc, Scheme_Object *argv[]);
Scheme_Object *scheme_fsemaphore_post(int argc, Scheme_Object *argv[]);
Scheme_Object *scheme_fsemaphore_try_wait(int argc, Scheme_Object *argv[]);

#endif
