/* OpSys sample thread code.

   This program has two threads:
    1. reads from standard input and places whatever
	it reads into a buffer.

	2. looks for stuff in the buffer and writes it
	to standard output.

   The result is simply a program that prints out whatever 
   you type in...

   IMPORTANT NOTE: This is a very good bad example! 
   (a good example of the wrong way to use threads).
   The busy waiting is the problem, this is as bad as it
   gets.... Check threadecho2.c for the better version!

*/

#include <stdio.h>	
#include <unistd.h>	
#include <sys/types.h>	
#include <stdlib.h>	
#include <pthread.h>	
#include <unistd.h>	


/*==========================================================*/
/* Global variables shared by the threads

   msgbuf is a circular buffer that holds whatever has been
   read from standard input by the reading thread. The writing 
   thread grabs stuff from here and writes it to stdout.

   in, out are indicies of the last character
   put in and the last char that was removed (respectively).
   if in == out, this means there is nothing in the buffer
   that hasn't been removed.
*/

#define BUFSIZE 100
char msgbuf[BUFSIZE];
int in = 0;
int out = 0;

/* mutex used to restrict access to shared data structure
*/

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

/* functions to grab and release the mutex 
   any error is fatal here...
*/

void grablock() {
  /* grab the lock */
  if (pthread_mutex_lock(&mtx)!=0) {
    printf("error getting mutex lock\n");
    exit(1);
  }
}

void releaselock() {
  /* release the lock */
  if (pthread_mutex_unlock(&mtx)!=0) {
    printf("error releasing mutex lock\n");
    exit(1);
  }	
}


/* add character to the circular buffer. waits
   until this is possible !
*/
void put_a_char(char c) {
  int spot_avail;

  /* BUSY WAITING!!!!! */
  do {
	grablock();
	// check to see if there is room for another char
	spot_avail = ((in+1)%BUFSIZE!=out);
    releaselock();
  }	while (! spot_avail);

  grablock();
  in = (in+1)%BUFSIZE;
  msgbuf[in]=c;
  releaselock();
}


/* get next char from buffer - waits until there is a byte available */

char get_a_char() {
  int char_avail;
  char c;
  /* BUSY WAITING !!!!! */
  do {
	grablock();
	// check to see if there is a char available
	char_avail = (in != out);
    releaselock();
  }	while (! char_avail);
  grablock();
  out = (out+1)%BUFSIZE;
  c = msgbuf[out];
  releaselock();
  return(c);
}

/* reader reads from standard input (one byte at a time) and places into
   the shared buffer */

void *reader(void *arg) {
  int nbytes;
  char c;
  while (1) {
	/* attempt to read as much as we can */
	if (read(STDIN_FILENO,&c,1)!=1) {
	  /* Either EOF or an error , in either case just quit */
	  write(STDOUT_FILENO,"\nAll Done\n",10);
	  exit(1);
	}
	put_a_char(c);
  }
}	  
	
void *writer(void *arg) {
  char c;
  while (1) {
	c = get_a_char();
	if (1!= write(STDOUT_FILENO,&c,1)) {
	  /* write error ? punt... */
	  exit(1);
	}
  }
}

int main() {
  int i;
  pthread_t pt;
  /* start up the reader */
    pthread_create(&pt,NULL,reader,NULL); 
  //  reader(NULL);
  /* become the writer */
    writer(NULL);
}

