/*
 * SCSMP.C - shared memory parallel processing example and test
 *
 * Source Version: 2.0
 * Software Release #92-0043
 *
 */

#include "score.h"

void *SC_DECLARE(mm_test, (void *x));

typedef struct s_state state;

struct s_state
   {int niter;};

static state
  parallel = { 100 };

/* These declarations are only necessary if an attribute or lock is to be
 * referenced in more than one source file. They will usually appear in an
 * appropriate include file.
 */
SC_DECLARE_THREAD_ATTR(smp_attr);
SC_DECLARE_THREAD_LOCK(smp_lock);

/* These definitions (and initializations) must appear in only one source file
 * outside of any function definitions and after any includes.
 */
SC_THREAD_ATTR(smp_attr);
SC_THREAD_LOCK(smp_lock);

/* Define a key variable */
SC_thread_key index_key;

int m = FALSE, s = TRUE, p = FALSE;
int global_count;

/*--------------------------------------------------------------------------*/

/* TID - return the thread id */

static void tid(id)
   int *id;
   {

    SC_GET_KEY(int, index_key, *id);

    return;}

/*--------------------------------------------------------------------------*/

void *mm_test(x)
   void *x;
   {int i, n, index;
    double *arr;

    n = parallel.niter;

/* Here we set the key to the thread index */
    SC_SET_KEY(int, index_key, x);

    if (s)
       for (i = 1; i <= n; ++i)
           {arr = FMAKE_N(double, i, "MM_TEST:arr");  
            if (p)
               printf("%d ", SC_arrlen(arr)/sizeof(double));
            SFREE(arr);};

    if (m)
       for (i = 1; i <= n; ++i)
           {arr = malloc(sizeof(double)*i);
            if (p)
               printf("%d ", i);
            free(arr);};

/* Make sure we get the correct key value back */
    SC_GET_KEY(int, index_key, index);
    if (index != *((int *) x))
       printf("\nKey value error: in = %d, out = %d\n", *((int *) x), index);

    SC_LOCKON(smp_lock);
    global_count++;
    SC_LOCKOFF(smp_lock);

    return NULL;}

/*--------------------------------------------------------------------------*/

/* MAIN - a multithreading example that tests the SMP safety of the SCORE MM */

/* Note that the code below reduces to a single-threaded version if PACT was
 * not thread configured (i.e. HAVE_THREADS is not defined). In either case,
 * mm_test will be executed n times. If PACT was thread configured the main
 * thread does not call mm_test (it doesn't do any work). This gives better
 * performance on some systems.
 */

int main(argc, argv)
   int argc;
   char **argv;
   {int n = 4;
    int i, j;
    SC_thread *threads;
    int *indexes;

    SC_setbuf(STDOUT, NULL);

/* process the command line arguments */
    for (i = 1; i < argc; i++)
        if (argv[i][0] == '-')
           {switch (argv[i][1]) 
               {case 'i' : parallel.niter = SC_stoi(argv[++i]);
		           break;
                case 'm' : m = TRUE;
                           s = FALSE;
                           break;
                case 'n' : n = SC_stoi(argv[++i]);
                           break;
                case 'p' : p = TRUE;
                           break;
                case 's' : s = TRUE;
                           m = FALSE;
                           break;};};

/* Make this call exactly once for each key */
    SC_CREATE_KEY(index_key, NULL);

    SC_tid_hook = tid;

    i = 0;
    SC_SET_KEY(int, index_key, &i);

/* This must appear before any SC_GET_KEYs */
    indexes    = FMAKE_N(int, n + 1, "MAIN:threads");
    indexes[0] = 0;
    SC_SET_KEY(int, index_key, &indexes[0]);

    threads  = FMAKE_N(SC_thread, n + 1, "MAIN:threads");

    for (i = 1; i <= n; i++)
        {indexes[i] = i;
	 j = pthread_create((pthread_t *) &(threads[i]),
                            (const pthread_attr_t *) NULL,
                            (pthread_startroutine_t) mm_test,
                            (void *) &indexes[i]);};
/*	 SC_THREAD_CREATE(threads[i], smp_attr, mm_test, indexes[i]);}; */

    for (i = 1; i <= n; i++)
        SC_THREAD_JOIN(threads[i], NULL);

    if (global_count != n)
       printf("\nError: Bad thread count = %d\n", global_count);

    printf("\n");

    SFREE(threads);

/* This must appear after last key reference */
    SFREE(indexes);

    return 0;}

/*--------------------------------------------------------------------------*/
