Honour 80cols per line limit
[eleutheria.git] / pthreads / matrixmul / matrixmul.c
blob712c9b98c0ac604d74f8f5330f669c94d061a0af
1 /*
2 * Compile with:
3 * gcc matrixmul.c -o matrixmul -lpthread -Wall -W -Wextra -ansi -pedantic
4 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <pthread.h>
10 typedef struct matrix {
11 size_t rows;
12 size_t cols;
13 int **data;
14 } matrix_t;
16 typedef struct matrix_index {
17 size_t row;
18 size_t col;
19 } matindex_t;
21 typedef enum {
22 MM_OK,
23 MM_ENOMEM,
24 MM_EIO
25 } mmret_t;
27 matrix_t *mat1, *mat2, *mat3;
29 /* Function prototypes */
30 mmret_t matrix_alloc(matrix_t **mat, size_t rows, size_t cols);
31 void matrix_free(matrix_t **mat);
32 mmret_t matrix_read(const char *path, matrix_t **mat);
33 void matrix_print(const matrix_t *mat);
34 void *mulvect(void *arg);
35 void diep(const char *s);
37 int main(int argc, char *argv[])
39 pthread_t *tid;
40 matindex_t *v;
41 size_t i, j, k, mdepth, numthreads;
43 /* Check argument count */
44 if (argc != 3) {
45 fprintf(stderr, "Usage: %s matfile1 matfile2\n", argv[0]);
46 exit(EXIT_FAILURE);
50 * Initialize `mdepth' variable
52 * We increase `mdepth' every time we succesfully call malloc().
53 * That way we can keep track of the "allocation depth" and easily
54 * free the memory whenever needed, e.g. if a fatal error occurs.
56 mdepth = 0;
58 /* Read matrix data from files */
59 if (matrix_read(argv[1], &mat1) != MM_OK)
60 goto CLEANUP_AND_EXIT;
61 mdepth++;
63 if (matrix_read(argv[2], &mat2) != MM_OK)
64 goto CLEANUP_AND_EXIT;
65 mdepth++;
67 /* Is the multiplication feasible by definition? */
68 if (mat1->cols != mat2->rows) {
69 fprintf(stderr, "Matrices' dimensions size must satisfy"
70 "(NxM)(MxK)=(NxK)\n");
71 goto CLEANUP_AND_EXIT;
74 /* Allocate memory for the result */
75 if (matrix_alloc(&mat3, mat1->rows, mat2->cols) != MM_OK)
76 goto CLEANUP_AND_EXIT;
77 mdepth++;
79 /* How many threads do we need ? */
80 numthreads = mat1->rows * mat2->cols;
82 /* v[k] holds the (i, j) pair in the k-th computation */
83 if ((v = malloc(numthreads * sizeof *v)) == NULL) {
84 perror("malloc()");
85 goto CLEANUP_AND_EXIT;
87 mdepth++;
89 /* Allocate memory for the threads' ids */
90 if ((tid = malloc(numthreads * sizeof *tid)) == NULL) {
91 perror("malloc()");
92 goto CLEANUP_AND_EXIT;
94 mdepth++;
96 /* Create the threads */
97 for (i = 0; i < mat1->rows; i++) {
98 for (j = 0; j < mat2->cols; j++) {
99 k = i*mat1->rows + j;
100 v[k].row = i;
101 v[k].col = j;
102 if (pthread_create(&tid[k], NULL, mulvect, (void *)&v[k])) {
103 perror("pthread_create()");
104 goto CLEANUP_AND_EXIT;
109 /* Make sure all threads are done */
110 for (i = 0; i < numthreads; i++)
111 if (pthread_join(tid[i], NULL)) {
112 perror("pthread_join()");
113 goto CLEANUP_AND_EXIT;
116 /* Print the result */
117 matrix_print(mat3);
119 CLEANUP_AND_EXIT:;
120 switch(mdepth) {
121 case 5: free(tid);
122 case 4: free(v);
123 case 3: matrix_free(&mat3);
124 case 2: matrix_free(&mat2);
125 case 1: matrix_free(&mat1);
126 case 0: ; /* free nothing */
129 return EXIT_SUCCESS;
132 mmret_t matrix_alloc(matrix_t **mat, size_t rows, size_t cols)
134 size_t i, j, mdepth = 0;
136 *mat = malloc(sizeof **mat);
137 if (*mat == NULL)
138 goto CLEANUP_AND_RETURN;
139 mdepth++;
141 (*mat)->rows = rows;
142 (*mat)->cols = cols;
144 (*mat)->data = malloc(rows * sizeof(int *));
145 if ((*mat)->data == NULL)
146 goto CLEANUP_AND_RETURN;
147 mdepth++;
149 for (i = 0; i < rows; i++) {
150 (*mat)->data[i] = malloc(cols * sizeof(int));
151 if ((*mat)->data[i] == NULL) {
152 if (i != 0)
153 mdepth++;
154 goto CLEANUP_AND_RETURN;
158 return MM_OK;
160 CLEANUP_AND_RETURN:;
161 perror("malloc()");
162 switch(mdepth) {
163 case 3: for (j = 0; j < i; j++) free((*mat)->data[j]);
164 case 2: free((*mat)->data);
165 case 1: free(*mat);
166 case 0: ; /* free nothing */
169 return MM_ENOMEM;
172 void matrix_free(matrix_t **mat)
174 size_t i;
176 for (i = 0; i < (*mat)->rows; i++)
177 free((*mat)->data[i]);
179 free((*mat)->data);
180 free(*mat);
183 mmret_t matrix_read(const char *path, matrix_t **mat)
185 FILE *fp;
186 size_t i, j, rows, cols;
188 /* Open file */
189 if ((fp = fopen(path, "r")) == NULL) {
190 fprintf(stderr, "Error opening file: %s\n", path);
191 return MM_EIO;
194 /* Read matrix dimensions */
195 fscanf(fp, "%u%u", &rows, &cols);
197 /* Allocate memory for matrix */
198 if (matrix_alloc(mat, rows, cols) == MM_ENOMEM) {
199 fclose(fp);
200 return MM_ENOMEM;
203 /* Read matrix elements */
204 for (i = 0; i < (*mat)->rows; i++) {
205 for (j = 0; j < (*mat)->cols; j++) {
206 fscanf(fp, "%d", &(*mat)->data[i][j]);
210 /* Close file */
211 fclose(fp);
213 return MM_OK;
216 void matrix_print(const matrix_t *mat)
218 size_t i, j;
220 for (i = 0; i < mat->rows; i++) {
221 for (j = 0; j < mat->cols; j++) {
222 printf("%d ", mat->data[i][j]);
224 printf("\n");
229 void *mulvect(void *arg)
231 size_t i, row, col;
233 row = *((int *)arg + 0);
234 col = *((int *)arg + 1);
236 mat3->data[row][col] = 0;
237 for (i = 0; i < mat1->cols; i++)
238 mat3->data[row][col] += mat1->data[row][i] * mat2->data[i][col];
240 pthread_exit(NULL);
243 void diep(const char *s)
245 perror(s);
246 exit(EXIT_FAILURE);