static char rcsid[] = "$Id: ompc_thread.c,v 1.15 2001/07/24 16:14:41 a-hasega Exp $";
/* 
 * $RWC_Release: Omni-1.6 $
 * $RWC_Copyright:
 *  Omni Compiler Software Version 1.5-1.6
 *  Copyright (C) 2002 PC Cluster Consortium
 *  
 *  This software is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License version
 *  2.1 published by the Free Software Foundation.
 *  
 *  Omni Compiler Software Version 1.0-1.4
 *  Copyright (C) 1999, 2000, 2001.
 *   Tsukuba Research Center, Real World Computing Partnership, Japan.
 *  
 *  Please check the Copyright and License information in the files named
 *  COPYRIGHT and LICENSE under the top  directory of the Omni Compiler
 *  Software release kit.
 *  
 *  
 *  $
 */
/* RWC OpenMP C compiler Run-time libraries */

#include <unistd.h>
#include <stdlib.h>
#include "exc_platform.h"
#include "ompclib.h"

#define INSERT_POLLING 1

int _ompc_debug_flag = 0;	/* debug output control */

volatile int _ompc_nested;	/* nested enable/disable */
volatile int _ompc_dynamic;	/* dynamic enable/disable */

volatile int _ompc_max_threads;	/* max number of thread */
volatile int _ompc_num_threads;	/* number of team member? */

int _ompc_n_proc = N_PROC_DEFAULT;	/* number of PE */

/* system lock variables */
_ompc_lock_t _ompc_proc_lock,_ompc_thread_lock;

/* prototype */
/*static*/ void _ompc_set_thread(struct ompc_thread*);
static void _ompc_remove_thread(void);
/*static*/ struct ompc_thread *_ompc_current_thread(void);

st_int_loc_t _ompc_serial_count;

/* 
 * initialize library
 */
#ifdef not
void _ompc_init_proc_num(int pnum)
{
    _ompc_n_proc = pnum;
    _ompc_init();
}
#endif

void _ompc_init(int argc,char *argv[])
{
    char  * cp;
    int val;
    long lnp;
    struct ompc_thread *tp;
#ifdef USE_PTHREAD
    static pthread_t thds[MAX_PROC];
    static pthread_attr_t attr;

    pthread_attr_init(&attr);
#ifndef OMNI_OS_FREEBSD
#ifndef OMNI_OS_IRIX
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
#endif
#endif
    /*pthread_attr_setschedpolicy(&attr, SCHED_FIFO);*/
#endif

#ifdef OMNI_OS_SOLARIS
    lnp = sysconf(_SC_NPROCESSORS_ONLN);
#else
#ifdef OMNI_OS_IRIX
    lnp = sysconf(_SC_NPROC_ONLN);
#else
#ifdef OMNI_OS_LINUX
    {
      char buff[BUFSIZ];
      FILE *fp;
      int  npes;
      char procfile[] = "/proc/stat";

      fp = fopen (procfile, "r");
      if ( fp == NULL ){
	fprintf (stderr, "cannot open \"%s\".\n"
		 "cannot get maximum number of processors.\n", procfile);
	lnp = 1;
      }
      else {
	npes = 0;
	while( fgets(buff, BUFSIZ, fp) != NULL ){
	  if ( !strncmp(buff, "cpu", 3) && isdigit(buff[3]) ){
	    npes += 1;
	  }
	}
	fclose (fp);
	lnp = (npes == 0)? 1: npes;
      }
    }
#else
    lnp = _ompc_n_proc;
#endif
#endif
#endif
    if (_ompc_n_proc != lnp)
	_ompc_n_proc = lnp;
    
#if defined(USE_SPROC) && defined(OMNI_OS_IRIX)
    __ateachexit(_ompc_finalize);
#else    
    atexit(_ompc_finalize);
#endif /* USE_SPROC && OMNI_OS_IRIX */

    cp = getenv("OMPC_DEBUG");
    if(cp != NULL){
	_ompc_debug_flag = TRUE;
	fprintf(stderr,"debug flag on ...\n");
    }

    cp = getenv("OMP_SCHEDULE");
    if(cp != NULL)
	_ompc_set_runtime_schedule(cp);

    cp = getenv("OMP_DYNAMIC");
    if(cp != NULL && (strcmp(cp, "TRUE") == 0 || strcmp(cp, "true") == 0))
	_ompc_dynamic = 1;
    else
	_ompc_dynamic = 0;	/* dynamic enable/disable */

    cp = getenv("OMP_NESTED");
    if(cp != NULL && (strcmp(cp,"FALSE") == 0 || strcmp(cp,"false") == 0))
	_ompc_nested = 0;
    else
	_ompc_nested = 1;	/* nested enable/disable */

    cp = getenv("OMPC_NUM_PROCS");   /* processor */
    if ( cp != NULL ){
	sscanf(cp, "%d", &val);
	if(val <= 0) _ompc_fatal("bad OMPC_NUM_PROCS(<= 0)");
	_ompc_n_proc = val;
    }

    _ompc_max_threads = _ompc_n_proc;	/* max number of thread, default */

    cp = getenv("OMP_NUM_THREADS");	/* a number of team member */
    if ( cp == NULL )
	_ompc_num_threads = _ompc_n_proc;
    else {
	sscanf(cp, "%d", &val);
	if(val <= 0) _ompc_fatal("bad OMP_NUM_THREADS(<= 0)");
	_ompc_num_threads = val;

	if(_ompc_num_threads > _ompc_max_threads)
	    _ompc_max_threads = _ompc_num_threads;
    }
#ifdef OMNI_OS_IRIX
#ifdef USE_PTHREAD
    pthread_setconcurrency(_ompc_max_threads);
#endif
#endif

    /* init system lock */
    _ompc_init_lock(&_ompc_proc_lock);
    _ompc_init_lock(&_ompc_thread_lock);
    _ompc_critical_init ();	/* initialize critical lock */
    _ompc_atomic_init_lock ();	/* initialize atomic lock */
    ST_INT_LOC_INIT(&_ompc_serial_count, 1);

    if(_ompc_debug_flag)
	printf("Creating %d slave thread ...\n", _ompc_max_threads-1);

    /* setup master root thread */
    tp = (struct ompc_thread *)malloc(sizeof(struct ompc_thread));
    tp->num = 0;        /* team master */
    tp->parent = NULL;
    tp->ser_num = 0;
    _ompc_set_thread(tp);

    if(_ompc_debug_flag) printf("init end(Master)\n");
}

/* finalize */
void _ompc_finalize()
{
    /* do nothing */
}

void _ompc_fatal(char * msg)
{
    fprintf(stderr, "OMPC FATAL: %s\n", msg);
    exit(-1);
}

void _ompc_set_thread(struct ompc_thread *tp)
{
    ST_POLLING();
    tls(user[0]).__uslong = (long)tp;
}

static void _ompc_remove_thread()
{
    ST_POLLING();
    tls(user[0]).__uslong = (long)NULL;
}

/*static*/ struct ompc_thread *_ompc_current_thread()
{
    struct ompc_thread *tp;

    tp = (struct ompc_thread *)(tls(user[0]).__uslong);
    if( tp == NULL )
        _ompc_fatal("unknown thread is running");
    ST_POLLING();
    return tp;
}

static void _ompc_start_thread(struct ompc_parent *tpp, int a, 
			       int b, st_join_counter_t *jc
#if !INSERT_POLLING
			       ,st_int_loc_t *start_sync
#endif
)
{    
    if (b - a == 1) { 
        struct ompc_thread tp;
#if !INSERT_POLLING
	int i, count;
#endif

        tp.parent = tpp;
        tp.num = a;        /* set thread_num */
	tp.count = 0;
	tp.red_count = -1;
        tp.barrier_phase = 0;
	tp.ser_num = tpp->ser_count + a;
        bzero(tp.thdprv, sizeof(void *) * MAX_THDPRV);

#if !INSERT_POLLING
        if (tpp->parent == NULL && _ompc_num_threads <= _ompc_n_proc)
            count = 1 << 30;
        else
            count = 1;
        st_fetch_and_add_int(start_sync, -1);
        for (i = 0; i < count; i++) {
            if (ST_INT_LOC_CHECK(start_sync, 0))
                break;
            ST_POLLING();
        }
#endif
        _ompc_set_thread(&tp);
        if ( tpp->args == NULL )
            (*tpp->func)(&tp);
        else 
            (*tpp->func)(tpp->args, &tp);
        ST_POLLING();
        
        MBAR();
        _ompc_remove_thread();
        st_join_counter_finish(jc);
    } else {
        int m = (a + b) / 2;
#if !INSERT_POLLING
        int i, count;
        st_int_loc_t sync;

        if (tpp->parent == NULL && _ompc_num_threads <= _ompc_n_proc)
            count = 1 << 30;
        else
            count = 1;
        st_fetch_and_add_int(start_sync, -1);
        for (i = 0; i < count; i++) {
            if (ST_INT_LOC_CHECK(start_sync, 0))
                break;
            ST_POLLING();
        }

        ST_INT_LOC_INIT(&sync, 2);
        ST_THREAD_CREATE(_ompc_start_thread(tpp, a, m, jc, &sync));
        _ompc_start_thread(tpp, m, b, jc, &sync);
#else
        ST_THREAD_CREATE(_ompc_start_thread(tpp, a, m, jc));
        _ompc_start_thread(tpp, m, b, jc);
#endif
    }
}

/* called from compiled code. */
void _ompc_do_parallel(cfunc f, void *args)
{
    struct ompc_thread *cthd;
    struct ompc_parent parent[1];
    st_join_counter_t jc[1];
    st_join_counter_t bc[2];
    int n_thds, ser_count;
#if !INSERT_POLLING
    st_int_loc_t start_sync;
#endif

    cthd = _ompc_current_thread();
    if ((cthd->parent != NULL) && _ompc_nested == 0) {
      n_thds = 1;
    } else {
      n_thds = _ompc_num_threads;
    }

    /* initialize parent thread */
    parent->num_thds = n_thds;
    parent->args = args;
    parent->func = f;
    parent->parent = cthd->parent;
    parent->thread = cthd;
    parent->jc = jc;
    ser_count = st_read_and_lock_int(&_ompc_serial_count);
    st_write_and_unlock_int(&_ompc_serial_count, ser_count + n_thds);
    parent->ser_count = ser_count;

    /* initialize barrier structure */
    ST_INT_LOC_INIT(&parent->parent_count, 0);
#if !INSERT_POLLING
    ST_INT_LOC_INIT(&start_sync, 1);
#endif
    parent->bc[0] = &bc[0];
    parent->bc[1] = &bc[1];
    st_join_counter_init(&bc[0], n_thds);
    st_join_counter_init(&bc[1], n_thds);

    /* assign threads to LWPs */
    st_join_counter_init(jc, n_thds);
    _ompc_start_thread(parent, 0, n_thds, jc
#if !INSERT_POLLING
		       ,&start_sync
#endif
		       );
    st_join_counter_wait(jc);
    MBAR();

    _ompc_set_thread(cthd);
    ST_POLLING();
}

void _ompc_do_parallel_if(int cond, cfunc f, void *args)
{
    struct ompc_thread *tp;
    if(cond) _ompc_do_parallel(f,args);
    else {
	tp = _ompc_current_thread();
	if(args == NULL)
	  (*f)(tp);
	else
	  (*f)(args, tp);
    }
    ST_POLLING();
}

/* 
 * Barrier 
 */
void _ompc_thread_barrier(int id, struct ompc_parent *tpp)
{
    int phase, pcount, i = 0, max_count;
    struct ompc_thread *tp = _ompc_current_thread();

    if(tpp == NULL) return; /* not in parallel */

    phase = tp->barrier_phase % 2;
    st_join_counter_finish(tpp->bc[phase]);

    if (tpp->parent == NULL) {
	max_count = 1000000;
    } else {
	max_count = 100000;
    }
    while (1) {
	if (i++ > max_count) {
	    st_join_counter_wait(tpp->bc[phase]);
	    break;
	}
	if (ST_INT_LOC_CHECK(&tpp->bc[phase]->count, 0)) {
	    break;
	}
	ST_POLLING();
    }

    st_join_counter_wait(tpp->bc[phase]);
    _ompc_set_thread(tp);
    tp->barrier_phase++;

    for (i = 0; i < tpp->num_thds * 2; i++) ST_POLLING();

    pcount = st_read_and_lock_int(&tpp->parent_count);
    if (pcount == tp->count) {
	st_join_counter_init(tpp->bc[tp->barrier_phase%2], tpp->num_thds);
	pcount++;
    }
    st_write_and_unlock_int(&tpp->parent_count, pcount);
    tp->count++;
}


void
_ompc_terminate (int exitcode)
{
  exit (exitcode);
}
