
/*
	The Dining Philosophers Problem using C-TASK

	Marco Savegnago 26/04/1990


*/

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>
#include <time.h>

#include "tsk.h"
#include "tsksup.h"
#include "tskprf.h"

#define STACKSIZE 2048
#define N   5                   /* Number of philosophers */
#define LEFT    (i-1)%N         /* Number of i's left neighbor */
#define RIGHT   (i+1)%N         /* Number of i's right neighbor */
#define THINKING    0           /* philosopher is thinking */
#define HUNGRY      1           /* philosopher is trying to get forks */
#define EATING      2           /* philosopher is eating */

#define THINKMAXTIC	182			/* Every Philosopher think at least 10 sec (random) */
#define EATMAXTIC	91          /* Every Philosopher eat in at least 5 sec (random) */

#undef MONOTASK

/* For Turbo C: Five tasks + keyboard + main Task Stack */

unsigned int _stklen = (N + 1) * STACKSIZE;

tcb kbtcb, tcbs[N];

int state[N];

resource mutex;
resource s[N];

unsigned noEating[N];

int endrun, outMode;

int taskNum[N];

char *stateStr[] = { "THINKING", "HUNGRY", "EATING" };

void test(int i)
{
	if(state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING)
	{
		state[i] = EATING;
		release_resource(&s[i]);
	}
}

void take_forks(int i)
{
	// critical

	request_cresource(&mutex, 0L);

	state[i] = HUNGRY;
	test(i);

	release_resource(&mutex);

	request_cresource(&s[i], 0L);

	// end critical
}

void put_forks(int i)
{
	request_cresource(&mutex, 0L);

	state[i] = THINKING;
	test(LEFT);
	test(RIGHT);

	release_resource(&mutex);
}

void refresh(void)
{
	int i;

	request_cresource(&mutex, 0L);

	for(i=0;i<N;i++)
	{
		tprintf(" %8s %i   ", stateStr[state[i]] , noEating[i] );
	}

	tprintf("\r");

	release_resource(&mutex);
}



void philosopher(void *arg)
{
	int* i = (int *)arg;

	tprintf ("Task %d started\n", *i);

	while(!endrun)
	{
		if(!outMode)
			tprintf ("Philospher %d now think\n", *i);
		else
			refresh();

		t_delay ((long)random(THINKMAXTIC));

		// think();

		if(!outMode)
			tprintf ("Philospher %d take forks\n", *i);
		else
			refresh();

		take_forks(*i);

		if(!outMode)
			tprintf ("Philospher %d now eat\n", *i);
		else
			refresh();

		noEating[*i]++;

		t_delay ((long)random(EATMAXTIC));

		// eat();

		if(!outMode)
			tprintf ("Philospher %d now put forks\n", *i);
		else
			refresh();

		put_forks(*i);
	}
}

void report(void)
{
	int i;

	request_cresource(&mutex, 0L);

	tprintf("Task#    State   Eat#\n");
	tprintf("---------------------\n");


	for(i=0;i<N;i++)
	{
		tprintf("  %02i   %8s    %i\n", i, stateStr[state[i]] , noEating[i] );
	}

	tprintf("---------------------\n");

	release_resource(&mutex);
}


#pragma argsused

void keyboard (void *a)
{
   int ch;

   tprintf ("Keyboard Task started\n");
   while (!endrun)
   {
	  ch = t_wait_key (30L);
	  if (ch < 0)
		 tputch (0x04);
	  else
	  {
		 switch (tolower (ch & 0xff))
		 {
			case 'e':   wake_task (NULL); /* Wake the main task */
						break;
			case 'r':	report();
						break;

   #if (TSK_NAMED)
			case 'd':   csnapshot ();
						break;
			case 'm':   tsk_set_mono (25, 80);
						screensnap (25);
						break;
			case 's':   tsk_set_colour (25, 80);
						screensnap (25);
						break;
   #endif
			case 't':	outMode = (outMode) ? 0 : 1;

						if(outMode)
							tprintf(" Task1   Eat#  Task2   Eat# Task3   Eat# Task4   Eat# Task5   Eat#\n");

						break;

			default:
				break;
		 }
	  }
   }
}

int main(void)
{
   char kbstack[STACKSIZE];

   char stack[N][STACKSIZE];

   int i;

   randomize();

#if(TSK_TURBO)
   directvideo = 0;
#endif
   endrun = 0;
   tsk_cprint_init (2, 1152);

   if ((i = install_tasker (0, 0, IFL_DISK | IFL_INT8_DIR | IFL_VIDEO, "Test")) < 0)
	  {
	  printf ("Can't install, returncode = %d\n", i);
	  return 1;
	  }

   init_conout ();

   create_resource(&mutex TN("MUTEX"));

   for (i=0; i < N; i++)
		create_resource(&s[i] TN("RES"));

   create_task (&kbtcb, keyboard, kbstack, STACKSIZE, PRI_STD, &i TN("KB"));

   start_task (&kbtcb);

#ifndef MONOTASK

   for (i=0; i < N; i++)
   {
		taskNum[i]=i;
		create_task (&tcbs[i], philosopher, stack[i], STACKSIZE, PRI_STD, &taskNum[i] TN("PHILO"));
   }

   for (i=0; i < N; i++)
		start_task (&tcbs[i]);
#else
   i=0;

   create_task (&tcbs[i], philosopher, stack[i], STACKSIZE, PRI_STD, &i TN("PHILO"));

   start_task (&tcbs[i]);
#endif

   preempt_on ();

   t_delay (0L);

   endrun = 1;

   report();

   kill_task(&kbtcb);

#ifndef MONOTASK
	for (i=0; i < N; i++)
		kill_task(&tcbs[i]);
#else
   i=0;

   kill_task(&tcbs[i]);
#endif

   for (i=0; i < N; i++)
		delete_resource(&s[i]);

   delete_resource(&mutex);

   schedule ();

   end_conout ();

   remove_tasker ();

   return 0;
}

