/*
 * $Id: /mirror/trunk/src/stm/stm_internal.h 29313 2008-07-11T07:34:04.766405Z cotto  $
 * Copyright (C) 2006-2008, The Perl Foundation.
 */

#ifndef PARROT_STM_INTERNAL_H_GUARD
#define PARROT_STM_INTERNAL_H_GUARD

#include <parrot/parrot.h>
#include <parrot/stm/backend.h>
#include "stm_waitlist.h"

#if STM_PROFILE

#  define CYCLE_BUCKETS 20
#  define TIME_BUCKETS 20
#  define PROFILE(log) (log)->profile
#  define PROFILE_WAIT(log, time, cycles) \
      do { \
          FLOATVAL the_time = (time); \
          int time_bucket = ceil(the_time * 200.0); \
          int cycle_bucket = (cycles) - 1; \
          struct STM_profile_data *profile = &PROFILE(log); \
          if ((cycles) == 0) { \
              ++profile->num_non_waits; \
          } \
          else { \
              if (time_bucket >= TIME_BUCKETS) { \
                  time_bucket = TIME_BUCKETS - 1; \
              } \
              else if (time_bucket == 0) { \
                  abort(); \
                  time_bucket = 0; \
              } \
              if (cycle_bucket >= CYCLE_BUCKETS) { \
                  cycle_bucket = CYCLE_BUCKETS - 1; \
              } \
              ++profile->num_waits; \
              ++profile->wait_cycles[cycle_bucket]; \
              ++profile->wait_time[time_bucket]; \
              profile->total_wait_time += the_time; \
              profile->total_wait_cycles += (cycles); \
          } \
      } while (0)

#  define PROFILE_TRIED_COMMIT(log) \
      ++PROFILE(log).attempted_commits
#  define PROFILE_FAILED_COMMIT(log) \
      ++PROFILE(log).failed_commits
#  define PROFILE_ABORTED(profile) \
      ++PROFILE(log).num_aborts

typedef struct STM_profile_data {
    long attempted_commits;
    long failed_commits;
    long num_aborts;
    long num_non_waits;
    long num_waits;
    long wait_cycles[CYCLE_BUCKETS];
    long wait_time[TIME_BUCKETS];
    double total_wait_time;
    long total_wait_cycles;
} STM_profile_data;

#else

#  define PROFILE(x)
#  define PROFILE_TRIED_COMMIT(x)
#  define PROFILE_FAILED_COMMIT(x)
#  define PROFILE_ABORTED(x)
#  define PROFILE_WAIT(x, y, z)

#endif

typedef struct Parrot_STM_PMC_handle_data {
    Buffer buf;
    Parrot_atomic_pointer owner_or_version;
    void * volatile last_version;
    PMC *value;
    STM_waitlist change_waitlist;
} Parrot_STM_PMC_handle_data;

struct STM_write_record {
    Parrot_STM_PMC_handle handle;
    void *saw_version;
    PMC *value;
};

struct STM_read_record {
    Parrot_STM_PMC_handle handle;
    void *saw_version;
    PMC *value;
};

typedef struct STM_write_record STM_write_record;
typedef struct STM_read_record STM_read_record;

enum {
    STM_STATUS_ACTIVE = 0,
    STM_STATUS_ABORTED = 1,
    STM_STATUS_COMMITTED = 2,
    STM_STATUS_INVALID = 3
};

#define STM_START_RECORDS 32

#define STM_MAX_TX_DEPTH 32 /* TODO: remove limitation */

struct STM_tx_log_sub;

typedef struct STM_tx_log_sub STM_tx_log_sub;

/* log for a sub transaction */
struct STM_tx_log_sub {
    Parrot_atomic_integer status;
    /* for poor man's deadlock detection; based on Ennals' implementation */
    Parrot_atomic_integer wait_length;
    int first_write;
    int first_read;
};

struct STM_tx_log {
    int depth;

#if STM_PROFILE
    STM_profile_data profile;
#endif

    STM_tx_log_sub inner[STM_MAX_TX_DEPTH];

    int last_write;
    int last_read;
    /* TODO: probably better to make these a list of hashes (one for each tx) */
    STM_write_record *writes;
    int writes_alloced;
    STM_read_record *reads;
    int reads_alloced;

    struct waitlist_thread_data *waitlist_data;
};

struct STM_saved_tx_log {
    int num_reads;
    int num_writes;
    STM_read_record *reads;
    STM_write_record *writes;
};
typedef struct STM_saved_tx_log STM_saved_tx_log;

#endif /* PARROT_STM_INTERNAL_H_GUARD */

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
