/*
*  DBCP - Double-Buffer Copy
* 
*  Copyright (C) 1995 by Sun Microsystems, Inc.
*  All rights reserved.
* 
*  This file is a product of SunSoft, Inc. and is provided for
*  unrestricted use provided that this legend is included on all
*  media and as a part of the software program in whole or part.
*  Users may copy, modify or distribute this file at will.
* 
*  THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING
*  THE WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
*  PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
* 
*  This file is provided with no support and without any obligation on the
*  part of SunSoft, Inc. to assist in its use, correction, modification or
*  enhancement.
* 
*  SUNSOFT AND SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT
*  TO THE INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS
*  FILE OR ANY PART THEREOF.
* 
*  IN NO EVENT WILL SUNSOFT OR SUN MICROSYSTEMS, INC. BE LIABLE FOR ANY
*  LOST REVENUE OR PROFITS OR OTHER SPECIAL, INDIRECT AND CONSEQUENTIAL
*  DAMAGES, EVEN IF THEY HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
*  DAMAGES.
* 
*  SunSoft, Inc.
*  2550 Garcia Avenue
*  Mountain View, California  94043
*/

/* Include Files		*/

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

/* Constants and Macros		*/

#define	NBUF		2

#define	ST_DIRTY	0x01
#define	ST_CLEAN	0x02
#define	ST_INUSE	0x04

/* Data Declarations		*/

typedef struct {
	char		buffer[BUFSIZ];
	int		state;
	int		nbytes;
	pthread_mutex_t	lk;
	pthread_cond_t	cv;
} buffer_t;

/* External Declarations	*/

buffer_t	buf[NBUF];

/* External References		*/

extern	void		*reader( void * );
extern	void		*writer( void * );
extern	buffer_t	*alloc_buffer( int, int );
extern	void		enable_buffer( buffer_t *, int );
extern	int		read_buffer( int, buffer_t * );
extern	int		write_buffer( int, buffer_t * );

/* Main				*/

main( int argc, char *argv[] ) {
	int		i;
	int		n;
	pthread_attr_t	attr;
	pthread_t	tid;

	for ( i=0; i < NBUF; ++i ) {
		buf[i].state	= ST_CLEAN;
		pthread_mutex_init( &buf[i].lk, NULL );
		pthread_cond_init( &buf[i].cv, NULL );
		buf[i].nbytes	= 0;
	}

	pthread_attr_init( &attr );
	pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );

	if ( n = pthread_create( &tid, &attr, reader, (void *) STDIN_FILENO ) ) {
		fprintf( stderr, "pthread_create:reader: %s\n", strerror( n ) );
		exit( 1 );
	}

	if ( n = pthread_create( &tid, &attr, writer, (void *) STDOUT_FILENO ) ) {
		fprintf( stderr, "pthread_create:writer: %s\n", strerror( n ) );
		exit( 1 );
	}

	pthread_attr_destroy( &attr );
	pthread_exit( 0 );
/* NOTREACHED */
	return( 0 );
}

/* Reader - reader thread		*/

	void *
reader( void *arg ) {
	int	fd	= (int) arg;
	int	eof	= 0;
	int	iter	= 0;

	while ( !eof ) {
		buffer_t	*bp	= alloc_buffer( iter++, ST_CLEAN );

		eof	= read_buffer( fd, bp );
		enable_buffer( bp, ST_DIRTY );
	}

	return( 0 );
}

/* Writer - writer thread		*/

	void *
writer( void *arg ) {
	int	fd	= (int) arg;
	int	eof;
	int	iter	= 0;

	do {
		buffer_t	*bp	= alloc_buffer( iter++, ST_DIRTY );

		eof	= write_buffer( fd, bp );
		enable_buffer( bp, ST_CLEAN );
	} while ( !eof );

	return( 0 );
}

/* alloc_buffer - allocate the buffer for iteration 'iter' when in 'state'.	*/

	buffer_t *
alloc_buffer( int iter, int state ) {
	buffer_t	*bp	= &buf[iter % NBUF];

	pthread_mutex_lock( &bp->lk );

	while ( bp->state != state )
		pthread_cond_wait( &bp->cv, &bp->lk );

	bp->state	= ST_INUSE;

	pthread_mutex_unlock( &bp->lk );

	return( bp );	
}

/* enable_buffer - change 'state' of buffer 'bp'.				*/

	void
enable_buffer( buffer_t *bp, int state ) {
	pthread_mutex_lock( &bp->lk );
	bp->state	= state;
	pthread_cond_signal( &bp->cv );
	pthread_mutex_unlock( &bp->lk );

	return;
}

/* read_buffer - read into buffer 'bp'.						*/

	int
read_buffer( int fd, buffer_t *bp ) {
	if ( ( bp->nbytes = read( fd, bp->buffer, BUFSIZ ) ) == -1 ) {
		fprintf( stderr, "read_buffer: %s\n", strerror( errno ) );
		exit( 1 );
	}

	return( bp->nbytes == 0 );
}

	int
write_buffer( int fd, buffer_t *bp ) {
	if ( bp->nbytes )
		if ( write( fd, bp->buffer, bp->nbytes ) != bp->nbytes ) {
			fprintf( stderr, "write_buffer: %s\n", strerror( errno ) );
			exit( 1 );
		}

	return( bp->nbytes == 0 );
}
