/*   
 * Copyright (c) 2002, Intel Corporation. All rights reserved.
 * Created by:  crystal.xiong REMOVE-THIS AT intel DOT com
 * This file is licensed under the GPL license.  For the full content
 * of this license, see the COPYING file at the top level of this 
 * source tree.
 *
 * Test the well-known sleeping barber problem. 
 */

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>

#include "posixtest.h"
#define CHAIR_NUM	5
#define CUS_NUM		10
#define LOOP_NUM	30

sem_t customer;
sem_t barber;
sem_t lock;
sem_t print;

int waiting = 0;

#ifdef __GNUC__
#define my_printf(x...) do { \
	sem_wait(&print); \
	printf(x); \
	sem_post(&print); \
} while (0)
#else
#define my_printf printf
#endif

void mdelay(unsigned msecs)
{
    struct timespec req;
    req.tv_sec = msecs / 1000;
    req.tv_nsec = (msecs % 1000) * 1000000;
    nanosleep(&req, NULL);
}

void *barbers(void *unused)
{
	int i;
	for (i = 0; i < LOOP_NUM; i++) {
		if (-1 == sem_wait(&lock)) {
			perror("sem_wait(&lock) didn't return success");
			pthread_exit((void *)1);
		}
		if (waiting == 0) {
			my_printf("There are no more customers waiting, barber will sleep.\n");
		}
		if (-1 == sem_post(&lock)) {
			perror("sem_post(&lock) didn't return success");
			pthread_exit((void *)1);
		}
		if (-1 == sem_wait(&customer)) {
			perror("sem_wait(&customer) didn't return success");
			pthread_exit((void *)1);
		}
		if (-1 == sem_wait(&lock)) {
			perror("sem_wait(&lock) didn't return success");
			pthread_exit((void *)1);
		}
		if (waiting >= 1)
			waiting--;
		my_printf("A customer sits in the barber's chair and get a hair cut.  %d customers left waiting.\n", waiting);
		if (-1 == sem_post(&lock)) {
			perror("sem_post(&lock) didn't return success");
			pthread_exit((void *)1);
		}
		if (-1 == sem_post(&barber)) {
		perror("sem_post(&barber) didn't return success");
			pthread_exit((void *)1);
		}
			
	}	
	return (void *) 0;
}
void *customers(void* ID)
{
	int CusID;
	CusID = *(int *)ID;

	if (CusID == 8) mdelay(10);

	my_printf("customer %d enters the room.\n", CusID);
	if (-1 == sem_wait(&lock)) {
		perror("sem_wait(&lock) didn't return success");
		pthread_exit((void *)1);
	}
	if (waiting < CHAIR_NUM) {
		waiting = waiting + 1;
		if (-1 == sem_post(&customer)) {
			perror("sem_post(&customer) didn't return success");
			pthread_exit((void *)1);
		}
		my_printf("Customer %d sits down, now %d customers are waiting.\n", CusID, waiting);
		if (-1 == sem_post(&lock)) {	
			perror("sem_post(&lock) didn't return success");
			pthread_exit((void *)1);
		}
		if (-1 == sem_wait(&barber)) {
			perror("sem_wait(&barber) didn't return success");
			pthread_exit((void *)1);
		}
		my_printf("Customer %d leaves with nice hair.\n", CusID);
	}
	else
	{
		my_printf("No chairs available, customer %d leaves without a haircut.\n", CusID);
		if (-1 == sem_post(&lock)) {	
			perror("sem_post(&lock) didn't return success");
			pthread_exit((void *)1);
		}
	}
	return (void *) 0;
}
int main(int argc, char *argv[])
{
	pthread_t bar, cus[CUS_NUM];
	int shared = 0;
	int barber_value = 0;
	int customer_value = 0;
	int lock_value=1;
	int i, ID[CUS_NUM];

	if (-1 == sem_init(&print, shared, 1)) {
		perror("sem_init(&print) didn't return success"); 
		return PTS_UNRESOLVED;
	}

#ifndef  _POSIX_SEMAPHORES
	my_printf("_POSIX_SEMAPHORES is not defined\n");
	return PTS_UNRESOLVED;
#endif 
	if (-1 == sem_init(&customer, shared, customer_value)) {
		perror("sem_init(&customer) didn't return success"); 
		return PTS_UNRESOLVED;
	}
	if (-1 == sem_init(&barber, shared, barber_value)) {
		perror("sem_init(&barber) didn't return success"); 
		return PTS_UNRESOLVED;
	}
	if (-1 == sem_init(&lock, shared, lock_value)) {
		perror("sem_init(&lock) didn't return success"); 
		return PTS_UNRESOLVED;
	}
	for (i = 0; i < CUS_NUM; i++) {
		ID[i] = i;
		pthread_create(&cus[i], NULL, customers, (void *)&ID[i]);	
	}
	pthread_create(&bar, NULL, barbers, NULL);	
	for (i = 0; i< CUS_NUM; i++) 
		pthread_join(cus[i], NULL);

	return PTS_PASS;
}
