/*******************************************************************

               Example 7 (pthreads_MatrixVector.c)

Objective             : To compute the matrix-matrix multiplication with 'p'
                        threads using Self Scheduling algorithm.
                        Matirx is populated internally with 1 and 2 as elements.
                        Demonstrates use of:
                        pthread_create()
                        pthread_join()
                        pthread_mutex_t
                        ptread_mutex_lock()
                        pthread_mutex_unlock()

Input                 : Sizes of matrices to be multiplied.

Output                : Product of the matrix multiplication.

 ********************************************************************/ 

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

 /* Size of Vector and Matrix. */ 
int             row, col, currentRow = 0, **Mat, *Vec, *Result;


 /* Mutex for the currentRow. */ 
pthread_mutex_t mutex_Row = PTHREAD_MUTEX_INITIALIZER;


void           *
doMyWork(int Id)
{
	
	int             i, myRow;
	
		
		while (1) {
		
			printf("\n %d: Locking....", Id);
		
			pthread_mutex_lock(&mutex_Row); {
			
				if (currentRow >= row) {
				
					pthread_mutex_unlock(&mutex_Row);
				
					printf("\n %d: Unlocking...\n I am Thread No.: %d. I have no work to do.", Id, Id);
				
					if (Id == 0)
					
						return;
				
					pthread_exit(0);
				
			} 
				myRow = currentRow;
			
				currentRow++;
			
				printf("\n I am Thread No.: %d. I have work to do.", Id);
			
		} 
			pthread_mutex_unlock(&mutex_Row);
		
			printf("\n %d: unlocking.... Operating on Row: %d.", Id, myRow);
		
			for (i = 0; i < col; i++)
			
				Result[myRow] += Mat[myRow][i] * Vec[i];
		
	} 
} 


main(int argc, char *argv[])
{
	
		
		 /* Hold for Threads. */ 
		pthread_t * threads;
	
		int             numberOfThreads;
	
		
		int             i, j;
	
		
		if (argc != 4) {
		
			
			printf(" Missing Arguments: exe Row Column Threads.\n");
		
			return;
		
			
	} 
		
		row = abs(atoi(argv[1]));
	
		col = abs(atoi(argv[2]));
	
		numberOfThreads = abs(atoi(argv[3]));
	
		
		printf("\n Row: %d. Col: %d, Number: %d.", row, col, numberOfThreads);
	
		
		Mat = (int **) malloc(sizeof(int) * row);
	
		for (i = 0; i < row; i++)
		
			Mat[i] = (int *) malloc(sizeof(int) * col);
	
		
		Vec = (int *) malloc(sizeof(int) * col);
	
		Result = (int *) malloc(sizeof(int) * row);
	
		
		 /* Populate the Matirx and the Vector. */ 
		
		for (i = 0; i < row; i++) {
		
			for (j = 0; j < col; j++)
			
				Mat[i][j] = 1;
		
			Result[i] = 0;
		
	} 
		
		for (i = 0; i < col; i++)
		
			Vec[i] = 1;
	
		
		threads = (pthread_t *) malloc(sizeof(pthread_t) * numberOfThreads);
	
		
		 /* Start Distributing the work. */ 
		
		currentRow = 0;
	
		
		for (i = 0; i < numberOfThreads; i++) {
		
			pthread_create(&threads[i], NULL, (void *(*) (void *)) doMyWork, (void *) (i + 1));
		
			printf("\n Thread %d created.", i + 1);
		
	} 
		doMyWork(0);
	
		
		for (i = 1; i < numberOfThreads; i++) {
		
			printf("\n About to join Thread %d.", i + 1);
		
			pthread_join(threads[i], NULL);
		
			printf("\n Thread %d Joined.", i + 1);
		
	} 
		
		printf("\n We're done!!!!\n");
	
} 
