/*
 * See how fast we can send a whole pile of messages between two tasks.
 *
 * Here's what I get on my system (your mileage may vary):
 *
 * 10000 round trip messages took 9.18 seconds (1089.0/s, 918us/msg)
 * child got 10001 messages (including shutdown msg)
 *
 */

#include <stdio.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <signal.h>
#include <sys/time.h>

#define NUM_MSGS	10000

struct msgnode { long typ; char msg[100]; };

double
cur_time()
{
    struct timeval tv;
    gettimeofday(&tv,NULL);
    return( tv.tv_sec + tv.tv_usec / 1e6 );
}

main()
{
    int msgkey = 0xffaabb;
    int trial;
    int i, rval, msqid, msgcnt;
    int parent_pid, child_pid;
    long typ;
    struct msgnode msg;
    double start, end;

    msqid = msgget(msgkey, IPC_CREAT | 0777);
    if ( msqid < 0 ) {
	perror("msgget #1 failed");
	exit(1);
    }
    if ( msgctl(msqid,IPC_RMID,NULL) < 0 ) {
	perror("msgctl(IPC_RMID) failed");
	exit(1);
    }
    msqid = msgget(msgkey, IPC_CREAT | 0777);
    if ( msqid < 0 ) {
	perror("msgget #2 failed");
	exit(1);
    }

    parent_pid = getpid();
    switch (child_pid = fork()) {

    case 0:	/* child - waits for a message */
	msgcnt = 0;
	while (1) {
	    if ( msgrcv(msqid,&msg,sizeof(msg.msg),-2,0) < 0 ) {
		perror("msgrcv(child)");
		kill(parent_pid,SIGTERM);	/* blow away the parent */
		exit(1);
	    }
	    msgcnt += 1;
	    if ( msg.typ == 2 ) {
		sleep(1);
		printf("child got %d messages (including shutdown msg)\n",msgcnt);
		exit(0);		/* We've been told to take a hike! */
	    }
	    msg.typ = 99;		/* regular reply */
	    if ( msgsnd(msqid,&msg,sizeof(msg.msg),0) < 0 ) {
		perror("msgsnd(child)");
		kill(parent_pid,SIGTERM);	/* blow away the parent */
		exit(1);
	    }
	}
    case -1:	/* trouble */
	perror("fork");
	exit(1);
    default:	/* parent - sends the first message */
	msg.typ = 1;
	sleep(2);	/* Let child get ready */
	start = cur_time();
	strcpy(msg.msg,"this is a long message to send to the other task");
	if ( msgsnd(msqid,&msg,sizeof(msg.msg),0) < 0 ) {
	    perror("msgsnd(parent #1)");
	    kill(child_pid,SIGTERM);	/* blow away the child */
	    exit(1);
	}
	for ( i = 0; i < NUM_MSGS; i += 1 ) {
	    if ( msgrcv(msqid,&msg,sizeof(msg.msg),99,0) < 0 ) {
		perror("msgrcv(parent)");
		kill(child_pid,SIGTERM);	/* blow away the child */
		exit(1);
	    }
	    /* printf("got msg #%d\n",i+1); */
	    if ( i == NUM_MSGS-1 ) {
		msg.typ = 2;		/* go away now */
	    } else {
		msg.typ = 1;		/* regular message */
	    }
	    if ( msgsnd(msqid,&msg,sizeof(msg.msg),0) < 0 ) {
		perror("msgsnd(parent #2)");
		kill(child_pid,SIGTERM);	/* blow away the child */
		exit(1);
	    }
	}
	end = cur_time();
	printf("%d round trip messages took %.2f seconds (%.1f/s, %.0fus/msg)\n",
	NUM_MSGS,end-start,NUM_MSGS / (end-start),1e6*(end-start) / NUM_MSGS);
	break;
    }

    exit(0);
}
