// Lecteurs-redacteurs sans priorité aux lecteurs
// Pour compiler, utiliser gcc -Wall lec_red.c -o lec_red -O3 -lpthread

// On peut donner priorité aux lecteurs, mais vu que lecteurs partagent
// leur lock les redacteurs tombent en famine dans la majorité des cas

#include <pthread.h>
#include <stdio.h>

#define N_REDACTEURS 100
#define N_LECTEURS_PAR_REDACTEUR 10

void access_init(void);

void begin_read(void);
void end_read(void);

void begin_write(void);
void end_write(void);

void lect_func(void);
void redac_func(void);

int reading;
int writing;
int wait_read;
pthread_cond_t cond;
pthread_mutex_t mutex;

int texte = 0;

int main( void )
{
	int i, j;

	access_init();

	pthread_t redacteurs[ N_REDACTEURS ];
	pthread_t lecteurs[ N_REDACTEURS * N_LECTEURS_PAR_REDACTEUR ];

	for( i = 0 ; i < N_REDACTEURS ; i++ )
	{
		pthread_create( &redacteurs[ i ], NULL, (void*)&redac_func, NULL );

		for( j = 0 ; j < N_LECTEURS_PAR_REDACTEUR ; j++ )
		{
			pthread_create( &lecteurs[ i * N_LECTEURS_PAR_REDACTEUR + j ], NULL, (void*)&lect_func, NULL );
		}
	}

	for( i = N_REDACTEURS ; i > 0 ; i-- )
	{
		pthread_join( redacteurs[ i ], NULL );

		for( j = N_LECTEURS_PAR_REDACTEUR ; j > 0 ; j-- )
		{
			pthread_join( lecteurs[ i * N_LECTEURS_PAR_REDACTEUR + j ], NULL );
		}
	}

	return  0;
}

void access_init( void )
{
	pthread_mutex_init( &mutex, NULL);

	pthread_cond_init( &cond, NULL);

	reading = writing = wait_read = 0;

//	printf( "Init OK\n" );
}

void begin_read( void )
{
	pthread_mutex_lock( &mutex );
	while( writing )
	{
		// wait_read a été desactive -pas de priorité aux lecteurs
//		wait_read++;

		pthread_cond_wait( &cond, &mutex );

//		wait_read--;
	}
	reading++;
	printf( "begin_read OK:  w%4d ; r%4d ; p%4d\n", writing, reading, wait_read );
	pthread_mutex_unlock( &mutex );
}

void end_read( void )
{
	pthread_mutex_lock( &mutex );
	reading--;
	pthread_cond_broadcast( &cond );
	printf( "end_read OK:    w%4d ; r%4d ; p%4d\n", writing, reading, wait_read );
	pthread_mutex_unlock( &mutex );
}

void begin_write( void )
{
	pthread_mutex_lock( &mutex );
	while( writing || reading || wait_read )
	{
		pthread_cond_wait( &cond, &mutex );
	}
	writing = 1;
	printf( "begin_write OK: w%4d ; r%4d ; p%4d\n", writing, reading, wait_read );
	pthread_mutex_unlock( &mutex );
}

void end_write( void )
{
	pthread_mutex_lock( &mutex );
	writing=0;
	pthread_cond_broadcast( &cond );
	printf( "end_write OK:   w%4d ; r%4d ; p%4d\n", writing, reading, wait_read );
	pthread_mutex_unlock( &mutex );
}

void lect_func( void )
{
	begin_read();
	printf( "     > Je lis %d\n", texte );
	end_read();
}

void redac_func( void )
{
	begin_write();
	texte++;
	printf( "     > J'ecris %d\n", texte );
	end_write();
}
