/*****************************************************************/
/* philosophers.c                                                */
/* demo of some of the features of odin.library 2.5              */
/* Created 6-Jun-90                                              */
/* Last modified 27-Aug-90                                       */
/* Link with odinglue.o                                          */
/*****************************************************************/

#include	"exec/types.h"
#include	"exec/semaphores.h"
#include	"stdio.h"
#include	"odin.h"

struct Library *OpenLibrary();

#define PHILONUM 4			/* number of philosophers */

struct OdinBase *OdinBase;		/* the global baseptr */

/* structure of allocation-envelope */
struct Forks {
	Envelope e;
	BOOL inuse[PHILONUM];
};

/* argument for the philosopher processes */
struct PEnv {
	Envelope e;
	int	num;
};

struct Forks *the_forks;
struct SignalSemaphore xx;

/* protect C-functions that are not re-entrant */
#define CR(s) ObtainSemaphore(&xx); { s } ReleaseSemaphore(&xx);
#define USE(n) (copy_forks->inuse[n])?printf("%d ",n):printf("  ")

char s[PHILONUM][20];			/* holds the names */

/* make name with single-digit suffix */
char *philoname(str,name,num)
char *str,*name;
int  num;
{
	register char *s2;

	s2 = str;
	while(*name) *s2++ = *name++;
	*s2++ = num  + '0';
	*s2 = 0;
	return str;
}

/* the body of the new processes, must be re-entrant */
struct PEnv *philosopher(env)
struct PEnv *env;
{
	int i,me;
	struct Forks *f;
	FILE *win;

	me = env->num;

	/* open monitorwindow */
	switch(me) {
		case 0:	CR(win = fopen("CON:10/10/200/80/Philo0","w");)
			break;
		case 1: CR(win = fopen("CON:220/10/200/80/Philo1","w");)
			break;
		case 2: CR(win = fopen("CON:10/115/200/80/Philo2","w");)
			break;
		case 3: CR(win = fopen("CON:220/115/200/80/Philo3","w");)
			break;
		default:
			break;
	}

	/* let each philosopher eat 20 times */
	for(i = 0; i < 20; i++) {
		f = (struct Forks *)AwaitNamedEnvelope("Forkalloc");
		while (f->inuse[me] || f->inuse[(me+1) % PHILONUM]) {
			Out(f);
			f = (struct Forks *)AwaitNamedEnvelope("Forkalloc");
		}

		/* no one used the forks, now sit on them */
		f->inuse[me] = TRUE;
		f->inuse[(me+1) % PHILONUM] = TRUE;

		/* allow others to look at me eating */
		Out(f);

		/* eat */
		CR(fprintf(win,"philosopher #%d eating\n",me);)

		/* release the forks */
		f = (struct Forks *)AwaitNamedEnvelope("Forkalloc");
		f->inuse[me] = FALSE;
		f->inuse[(me+1) % PHILONUM] = FALSE;
		Out(f);
	}
	CR(fclose(win);)
	return(env);
}

main()
{
	int i;
	struct PEnv *env;
	struct Forks *copy_forks;

	/* set up semaphore to protect C library routines that are */
	/* not re-entrant */

	InitSemaphore(&xx);

	/* open odin.library */
	OdinBase = (struct OdinBase *)OpenLibrary("RAM:odin.library",0L);
	if(OdinBase == NULL) {
		printf("Error: Couldn't open odin.library\n");
		exit(10);
	}

	/* now create a few philosopher-processes */
	for(i = 0; i < PHILONUM; i++) {
		env = (struct PEnv *)CreateEnvelope(
			philoname(&s[i][0],"phil#",i),
			(ULONG)sizeof(struct PEnv));
		env->e.e_proc = philosopher;
		env->num = i;
		(void)Eval(env,0L,8000L,EVAL_PROCESS);
	}

	/* init the allocation envelope */
	the_forks = (struct Forks *)CreateEnvelope("Forkalloc",
						(ULONG)sizeof(struct Forks));

	for(i = 0; i < PHILONUM; i++) the_forks->inuse[i] = FALSE;

	Out(the_forks);

	copy_forks = (struct Forks *)CreateEnvelope("Forkalloc",
						(ULONG)sizeof(struct Forks));
	while((env = (struct PEnv *)PollNamedEnvelope(&s[0][0])) == NULL) {
		Rd(copy_forks);
		CR(	printf("Forks in use: ");
			USE(0);
			USE(1);
			USE(2);
			USE(3);
			printf("\n");
		)
	}

	DisposeEnvelope(copy_forks);
	DisposeEnvelope(env);

	/* now wait for other philosophers to die */
	for(i = 1; i < PHILONUM; i++) {
		DisposeEnvelope(AwaitNamedEnvelope(&s[i][0]));
	}

	/* get rid of allocation-envelope, wait at most 10 secs */
	DisposeEnvelope(AwaitTimed("Forkalloc",10L,0L));
	CloseLibrary(OdinBase);
}
