uni

University stuff
git clone git://git.christosmarg.xyz/uni-assignments.git
Log | Files | Refs | README | LICENSE

ex3.c (4204B)


      1 #include <err.h>
      2 #include <pthread.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <time.h>
      6 #include <unistd.h>
      7 
      8 /*
      9  * Εργαστήριο ΛΣ2 (Δ6) / Εργασία 1: Άσκηση 3 / 2020-2021
     10  * Ονοματεπώνυμο: Χρήστος Μαργιώλης
     11  * ΑΜ: 19390133
     12  * Τρόπος μεταγλώττισης: `cc ex3.c -lpthread -DLLIM=x -DULIM=y -o ex3`
     13  *	Όπου `x` και `y` το κάτω και πάνω όριο αντίστοιχα.
     14  */
     15 
     16 /* No globals allowed :-) */
     17 struct foo {
     18 	int *sums;		/* Sums for each thread */
     19 	int *arr;		/* Global vector */
     20 	int n;			/* `arr`'s length */
     21 	int ntd;		/* Number of threads */
     22 	int tid;		/* Thread ID */
     23 	pthread_mutex_t mutex;	/* Protect critical parts */
     24 };
     25 
     26 /* Function declarations */
     27 static void *calc(void *);
     28 static void *emalloc(size_t);
     29 
     30 /* 
     31  * Each thread calculates the sum of the squares of each element in a specified
     32  * region in `arr`. That range is determined by the thread's ID (e.g the current
     33  * thread) and the number of elements that each thread has to compute. Since
     34  * `arr`'s length is a multiple of `ntd`, we can calculate the number of elements
     35  * each thread will compute by doing `n / ntd`.
     36  *
     37  * For example, `ntd = 2` and `n = 4`, and `arr = [1, 2, 3, 4]`.
     38  * Each thread will compute `n / ntd = 2` elements.
     39  *
     40  * Thread 0 will operate in the region:
     41  *	`td * (n / ntd) = 0 * 2 = 0` to 
     42  *	`(td + 1) * (n / ntd) - 1 = (0 + 1) * 2 - 1 = 1`
     43  *
     44  * Thread 1 will operate in the region:
     45  *	`1 * 2 = 2` to `(1 + 1) * 2 - 1 = 3`
     46  *
     47  * So effectively, each thread will be assigned to compute an equal amount of
     48  * elements.
     49  */
     50 static void *
     51 calc(void *foo)
     52 {
     53 	struct foo *f;
     54 	int localsum;
     55 	int tid, n, i;
     56 
     57 	f = (struct foo *)foo;
     58 	tid = f->tid;
     59 	n = f->n / f->ntd;
     60 	f->sums[tid] = 0;
     61 	localsum = 0;
     62 
     63 	for (i = tid * n; i < (tid + 1) * n; i++) {
     64 		localsum += f->arr[i] * f->arr[i];
     65 		printf("tid: %d\tarr[%d]: %d\n", tid, i, f->arr[i]);
     66 	}
     67 	/* 
     68 	 * Lock the mutex so that no other thread can write
     69 	 * to f->sums at the same time.
     70 	 */ 
     71 	if (pthread_mutex_lock(&f->mutex) != 0)
     72 		err(1, "pthread_mutex_lock");
     73 	f->sums[tid] = localsum;
     74 	if (pthread_mutex_unlock(&f->mutex) != 0)
     75 		err(1, "pthread_mutex_unlock");
     76 
     77 	return NULL;
     78 }
     79 
     80 /* Error checking malloc(2) */
     81 static void *
     82 emalloc(size_t nb)
     83 {
     84 	void *p;
     85 
     86 	if ((p = malloc(nb)) == NULL)
     87 		err(1, "malloc");
     88 	return p;
     89 }
     90 
     91 int
     92 main(int argc, char *argv[])
     93 {
     94 	struct foo *f;	/* Each callback will receive this */
     95 	pthread_t *tds; /* Threads */
     96 	pthread_t fin;	/* Will calculate the total sum */
     97 	int totalsum;	/* What its name says */
     98 	int i;		/* Counter */
     99 
    100 	f = emalloc(sizeof(struct foo));
    101 	/* 
    102 	 * We do error checks for `n` and `ntd` but in case we read from a
    103 	 * file the program might break if the data is wrong (i.e fails the
    104 	 * error checks at least once) since it will keep going further down
    105 	 * into the file. The same doesn't apply for when reading from stdin --
    106 	 * we can just give it a new number until it's correct.
    107 	 */
    108 	do {
    109 		printf("p: ");
    110 		scanf("%d", &f->ntd);
    111 	/* Cannot have < 0 threads. */
    112 	} while (f->ntd < 0);
    113 
    114 	do {
    115 		printf("n: ");
    116 		scanf("%d", &f->n);
    117 	/* Make sure `n` is positive and also a multiple of `ntd`. */
    118 	} while (f->n < 0 || f->n % f->ntd != 0);
    119 
    120 	tds = emalloc(f->ntd * sizeof(pthread_t));
    121 	f->arr = emalloc(f->n * sizeof(int));
    122 	f->sums = emalloc(f->ntd * sizeof(int));
    123 
    124 	/* Fill the vector. */
    125 	srand(time(NULL));
    126 	for (i = 0; i < f->n; i++)
    127 		f->arr[i] = rand() % (ULIM - LLIM) + LLIM;
    128 
    129 	if (pthread_mutex_init(&f->mutex, NULL) != 0)
    130 		err(1, "pthread_mutex_init");
    131 	/* 
    132 	 * Start multithreading. For each thread we assign `calc`
    133 	 * to be the callback function that each thread will call
    134 	 * and we pass it the `foo` struct as an argument to avoid
    135 	 * declaring globals.
    136 	 */
    137 	for (i = 0; i < f->ntd; i++) {
    138 		f->tid = i;
    139 		if (pthread_create(&tds[i], NULL, calc, (void *)f) != 0)
    140 			err(1, "pthread_create");
    141 		if (pthread_join(tds[i], NULL) != 0)
    142 			err(1, "pthread_join");
    143 	}
    144 	totalsum = 0;
    145 	while (f->ntd--)
    146 		totalsum += *f->sums++;
    147 	printf("total sum: %d\n", totalsum);
    148 
    149 	(void)pthread_mutex_destroy(&f->mutex);
    150 	pthread_exit(NULL);
    151 	free(tds);
    152 	free(f->arr);
    153 	free(f->sums);
    154 	free(f);
    155 
    156 	return 0;
    157 }