/*
 * Copyright (c) 2011 Ken McDonell.  All Rights Reserved.
 *
 * exercise multi-threaded support for local PMNS functions
 */

#include <stdio.h>
#include <stdlib.h>
#include <pcp/pmapi.h>
#include "libpcp.h"
#include <pthread.h>

#ifndef HAVE_PTHREAD_BARRIER_T
#include "pthread_barrier.h"
#endif

static pthread_barrier_t barrier;

static char *pmnsfile;
static char pmns[] = "#undef  EXTRA\n\
root {\n\
        a\n\
	b       1:0:0\n\
	c       2:0:0\n\
#ifdef EXTRA\n\
	d\n\
	e	4:0:0\n\
#endif\n\
}\n\
a {\n\
	one     0:0:0\n\
	two     0:1:0\n\
	three\n\
}\n\
a.three {\n\
	foo     0:2:0\n\
	bar     0:2:1\n\
}\n\
#ifdef EXTRA\n\
d {\n\
	mumble	3:0:0\n\
	f\n\
}\n\
d.f {\n\
	fumble	3:0:1\n\
}\n\
#endif\n\
";


static void *
func1(void *arg)
{
    int		sts;
    char	*fn = "func1";
    char	**offspring;
    int		*status;
    int		i;
    int		j;
    const char	*namelist[] = { "b", "a.three.foo", "d.f.fumble" };
    pmID	pmidlist[3];

    pthread_barrier_wait(&barrier);

    __pmDumpNameSpace(stdout, 1);

    sts = pmGetChildrenStatus("", &offspring, &status);
    if (sts >= 0) {
	printf("%s: pmGetChildrenStatus -> %d\n", fn, sts);
	for (i = 0; i < sts; i++) {
	    printf("[%d] %s %s\n", i, offspring[i], status[i] == PMNS_LEAF_STATUS ? "leaf" : "non-leaf");
	}
	free(offspring);
	free(status);
    }
    else
	printf("%s: pmGetChildrenStatus -> %s\n", fn, pmErrStr(sts));

    for (i = 0; i < 5; i++) {
	pthread_barrier_wait(&barrier);
	sts = pmLookupName(sizeof(namelist)/sizeof(namelist[0]), namelist, pmidlist);
	if (sts < 0)
	    printf("%s: pmGetChildrenStatus[%d] -> %s\n", fn, i, pmErrStr(sts));
	else {
	    for (j = 0; j < sizeof(namelist)/sizeof(namelist[0]); j++) {
		printf("%s: [%d] %s -> %s", fn, i, namelist[j], pmIDStr(pmidlist[j]));
		if (pmidlist[j] == PM_ID_NULL)
		    printf("\n");
		else {
		    char	*name;
		    sts = pmNameID(pmidlist[j], &name);
		    if (sts < 0)
			printf(": pmNameID -> %s\n", pmErrStr(sts));
		    else {
			printf(" -> %s\n", name);
			free(name);
		    }
		}
	    }
	}
	pthread_barrier_wait(&barrier);
    }

    return(NULL);	/* pthread done */
}

static void *
func2(void *arg)
{
    int		sts;
    char	*fn = "func2";
    char	*p;
    FILE	*f;

    pthread_barrier_wait(&barrier);

    /* iter 0 */
    pthread_barrier_wait(&barrier);
    pthread_barrier_wait(&barrier);

    /* iter 1 */
    pmUnloadNameSpace();
    pthread_barrier_wait(&barrier);
    pthread_barrier_wait(&barrier);

    /* iter 2 */
    if ((sts = pmLoadASCIINameSpace(pmnsfile, 1)) < 0) {
	printf("%s: pmLoadASCIINameSpace: [reload] %s\n", fn, pmErrStr(sts));
	exit(1);
    }
    pthread_barrier_wait(&barrier);
    pthread_barrier_wait(&barrier);

    /* iter 3 */
    pmUnloadNameSpace();
    for (p = pmns; *p; p++) {
	if (*p == 'u') {
	    *p++ = 'd';
	    *p++ = 'e';
	    *p++ = 'f';
	    *p++ = 'i';
	    *p++ = 'n';
	    *p++ = 'e';
	    break;
	}
    }
    if ((f = fopen(pmnsfile, "w")) == NULL) {
	printf("fopen: %s: failed: %s\n", pmnsfile, pmErrStr(-oserror()));
	exit(1);
    }
    if (fwrite(pmns, strlen(pmns), 1, f) != 1) {
	printf("fwrite: %s: failed: %s\n", pmnsfile, pmErrStr(-oserror()));
	exit(1);
    }
    fclose(f);
    if ((sts = pmLoadASCIINameSpace(pmnsfile, 1)) < 0) {
	printf("%s: pmLoadASCIINameSpace: [extra reload] %s\n", fn, pmErrStr(sts));
	exit(1);
    }
    pthread_barrier_wait(&barrier);
    pthread_barrier_wait(&barrier);

    /* iter 4 */
    pthread_barrier_wait(&barrier);
    pthread_barrier_wait(&barrier);

    return(NULL);	/* pthread done */
}

static void
wait_for_thread(char *name, pthread_t tid)
{
    int		sts;
    char	*msg;

    sts = pthread_join(tid, (void *)&msg);
    if (sts == 0) {
	if (msg == PTHREAD_CANCELED)
	    printf("thread %s: pthread_join: cancelled?\n", name);
	else if (msg != NULL)
	    printf("thread %s: pthread_join: %s\n", name, msg);
    }
    else
	printf("thread %s: pthread_join: error: %s\n", name, strerror(sts));
}

int
main(int argc, char **argv)
{
    pthread_t	tid1;
    pthread_t	tid2;
    int		sts;
    FILE	*f;

    if (argc != 2) {
	printf("Usage: multithread3 tmpfile\n");
	exit(1);
    }
    pmnsfile = argv[1];
    if ((f = fopen(pmnsfile, "w")) == NULL) {
	printf("fopen: %s: failed: %s\n", pmnsfile, pmErrStr(-oserror()));
	exit(1);
    }
    if (fwrite(pmns, strlen(pmns), 1, f) != 1) {
	printf("fwrite: %s: failed: %s\n", pmnsfile, pmErrStr(-oserror()));
	exit(1);
    }
    fclose(f);

    sts = pthread_barrier_init(&barrier, NULL, 2);
    if (sts != 0) {
	printf("pthread_barrier_init: sts=%d\n", sts);
	exit(1);
    }

    if ((sts = pmLoadASCIINameSpace(pmnsfile, 1)) < 0) {
	printf("pmLoadASCIINameSpace: %s\n", pmErrStr(sts));
	exit(1);
    }

    sts = pthread_create(&tid1, NULL, func1, NULL);
    if (sts != 0) {
	printf("thread_create: tid1: sts=%d\n", sts);
	exit(1);
    }
    sts = pthread_create(&tid2, NULL, func2, NULL);
    if (sts != 0) {
	printf("thread_create: tid2: sts=%d\n", sts);
	exit(1);
    }

    wait_for_thread("tid1", tid1);
    wait_for_thread("tid2", tid2);

    exit(0);
}
