/****************************************************************************/
/*                           the diy toolsuite                              */
/*                                                                          */
/* Jade Alglave, University College London, UK.                             */
/* Luc Maranget, INRIA Paris-Rocquencourt, France.                          */
/*                                                                          */
/* Copyright 2015-present Institut National de Recherche en Informatique et */
/* en Automatique and the authors. All rights reserved.                     */
/*                                                                          */
/* This software is governed by the CeCILL-B license under French law and   */
/* abiding by the rules of distribution of free software. You can use,      */
/* modify and/ or redistribute the software under the terms of the CeCILL-B */
/* license as circulated by CEA, CNRS and INRIA at the following URL        */
/* "http://www.cecill.info". We also give a copy in LICENSE.txt.            */
/****************************************************************************/
#ifndef _UTILS_H
#define _UTILS_H 1

#include <stdio.h>
#include <inttypes.h>
#include <pthread.h>
#include "litmus_rand.h"


/********/
/* Misc */
/********/

void seterrlog(FILE *chan) ;

int log_error(const char *fmt,...) ;

void fatal(char *msg) ;
/* e is errno */
void errexit(char *msg,int e) ;

void *malloc_check(size_t sz) ;

void *mmap_exec(size_t sz) ;
void munmap_exec(void *p,size_t sz) ;
int max(int n,int m) ;

void pp_ints (FILE *fp,int *p,int n) ;

void *do_align(void *p, size_t sz) ;

void *do_noalign(void *p, size_t sz) ;

void cat_file(char *path,char *msg,FILE *out) ;

/***********/
/* CPU set */
/***********/

#define CPUS_DEFINED 1
typedef struct {
  int sz ;
  int *cpu ;
} cpus_t ;

cpus_t *cpus_create(int sz) ;
cpus_t *cpus_create_init(int sz, int t[]) ;
void cpus_free(cpus_t *p) ;
void cpus_dump(FILE *fp, cpus_t *p) ;
void cpus_dump_test(FILE *fp, int *p, int sz, cpus_t *cm,int nprocs) ;

int gcd(int a, int b) ;

cpus_t *coremap_seq(int navail, int nways) ;
cpus_t *coremap_end(int navail, int nways) ;

void custom_affinity
(st_t *st,cpus_t *cm,int **color,int *diff,cpus_t *aff_cpus,int n_exe, int *r) ;

/*************/
/* Int array */
/*************/

typedef struct {
  int sz ;
  int *t ;
} ints_t ;

void ints_dump(FILE *fp, ints_t *p) ;

/* Prefetch directives */
typedef enum {none, flush, touch, touch_store} prfdir_t ;

typedef struct {
  char *name ;
  prfdir_t dir ;
} prfone_t ;

typedef struct {
  int nvars ;
  prfone_t *t ;
} prfproc_t ;

typedef struct {
  int nthreads ;
  prfproc_t *t ;
} prfdirs_t ;

void prefetch_dump(FILE *fp, prfdirs_t *p) ;
int parse_prefetch(char *p, prfdirs_t *r) ;

/************************/
/* Command line options */
/************************/
typedef enum
  { aff_none, aff_incr, aff_random, aff_custom,
    aff_scan, aff_topo} aff_mode_t ;

typedef struct {
  int verbose ;
  /* Test parmeters */
  int max_run ;
  int size_of_test ;
  int stride ;
  int avail ;
  int n_exe ;
  int sync_n ;
  /* Affinity */
  aff_mode_t aff_mode ;
  int aff_custom_enabled ;
  int aff_scan_enabled ;
  int aff_incr ;
  cpus_t *aff_cpus ;
  char *aff_topo ;
  /* indirect mode */
  int shuffle ;
  /* loop test */
  int max_loop ;
  /* time base delays */
  ints_t * delta_tb ;
  /* prefetch control */
  prfdirs_t *prefetch ;
  int static_prefetch ;
  /* show time of synchronisation */
  int verbose_barrier ;
  /* Stop as soon as condition is settled */
  int speedcheck ;
  /* Enforce fixed launch order (ie cancel change lauch) */
  int fix ;
  /* Dump prelude to test output */
  int prelude ;
} cmd_t ;

void parse_cmd(int argc, char **argv, cmd_t *def, cmd_t *p) ;


/********************/
/* Thread utilities */
/********************/

/* Mutex */

typedef pthread_mutex_t pm_t ;

pm_t *pm_create(void) ;
void pm_free(pm_t *p) ;
void pm_lock(pm_t *m) ;
void pm_unlock(pm_t *m) ;

/* C11 locks interface */
#ifndef mtx_t
typedef pthread_mutex_t mtx_t ;

inline static void mtx_lock(mtx_t *p) { pm_lock(p); }
inline static void mtx_unlock(mtx_t *p) { pm_unlock(p); }
#endif

/* Condition variable */

typedef struct pc_t {
  pm_t *c_mutex ;
  pthread_cond_t *c_cond ;
#ifdef CACHE
  struct pc_t *next;
#endif
} pc_t ;

pc_t *pc_create(void) ;
void pc_free(pc_t *p) ;
void pc_wait(pc_t *p) ;
void pc_broadcast (pc_t *p) ;

/* Barrier */

/* Avoid pthread supplied barrier as they are not available in old versions */

typedef struct {
  volatile unsigned int count ;
  volatile int turn ;
  pc_t *cond ;
  unsigned int nprocs ;
} pb_t ;


pb_t *pb_create(int nprocs) ;
void pb_free(pb_t *p) ;
void pb_wait(pb_t *p) ;


/* Or flag */

typedef struct {
  pc_t *cond ;
  int nprocs ;
  int count ;
  volatile int val ;
  volatile int turn ;
} po_t ;

po_t *po_create(int nprocs) ;
void po_free(po_t *p) ;
/* Initialize flag, must be called by all participant */
void po_reinit(po_t *p) ;
/* Return the 'or' of the v arguments of all participants */
int po_wait(po_t *p, int v) ;

/* One place buffer */

typedef struct {
  pc_t *cond ;
  int volatile some ;
  void * volatile val ;
} op_t ;

op_t *op_create(void) ;
void op_free(op_t *p) ;
void op_set(op_t *p, void *v) ;
void *op_get(op_t *p) ;

/* Thread launch and join */

typedef void* f_t(void *);

void launch(pthread_t *th, f_t *f, void *a) ;

void *join(pthread_t *th) ;

/* Detached lauch and join */

op_t *launch_detached(f_t *f,void *a) ;
void *join_detached(op_t *p) ;

/* Thread cache */
void set_pool(void) ;
op_t *launch_cached(f_t *f,void *a) ;

/*****************/
/* Random things */
/*****************/

/* permutations */

void perm_prefix_ints(st_t *st,int t[], int used, int sz) ;
void perm_ints(st_t *st,int t[], int sz) ;
void perm_funs(st_t *st,f_t *t[], int sz) ;
void perm_threads(st_t *st,pthread_t t[], int sz) ;
void perm_ops(st_t *st,op_t *t[], int sz) ;

/* check permutation */
int check_shuffle(int **t, int *min, int sz) ;

/*********************/
/* Real time counter */
/*********************/

typedef unsigned long long tsc_t ;
#define PTSC "%llu"

/* Result in micro-seconds */
tsc_t timeofday(void) ;
double tsc_ratio(tsc_t t1, tsc_t t2) ;
double tsc_millions(tsc_t t) ;

/* String utilities */
int find_string(const char *t[],int sz,const char *s) ;

#endif
