uni

University stuff
git clone git://git.margiolis.net/uni.git
Log | Files | Refs | README | LICENSE

ex3.c (4541B)


      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 	if (pthread_mutex_lock(&f->mutex) != 0)
     59 		err(1, "pthread_mutex_lock");
     60 	tid = f->tid++;
     61 	if (pthread_mutex_unlock(&f->mutex) != 0)
     62 		err(1, "pthread_mutex_unlock");
     63 	n = f->n / f->ntd;
     64 	f->sums[tid] = 0;
     65 	localsum = 0;
     66 
     67 	for (i = tid * n; i < (tid + 1) * n; i++) {
     68 		localsum += f->arr[i] * f->arr[i];
     69 		printf("tid: %d\tarr[%d]: %d\n", tid, i, f->arr[i]);
     70 	}
     71 	/* 
     72 	 * Lock the mutex so that no other thread can write
     73 	 * to f->sums at the same time.
     74 	 */ 
     75 	if (pthread_mutex_lock(&f->mutex) != 0)
     76 		err(1, "pthread_mutex_lock");
     77 	f->sums[tid] = localsum;
     78 	if (pthread_mutex_unlock(&f->mutex) != 0)
     79 		err(1, "pthread_mutex_unlock");
     80 
     81 	return NULL;
     82 }
     83 
     84 /* Error checking malloc(2) */
     85 static void *
     86 emalloc(size_t nb)
     87 {
     88 	void *p;
     89 
     90 	if ((p = malloc(nb)) == NULL)
     91 		err(1, "malloc");
     92 	return p;
     93 }
     94 
     95 int
     96 main(int argc, char *argv[])
     97 {
     98 	struct foo *f;	/* Each callback will receive this */
     99 	pthread_t *tds; /* Threads */
    100 	int totalsum;	/* What its name says */
    101 	int rc;		/* scanf(3) return value. */
    102 	int i;		/* Counter */
    103 
    104 	f = emalloc(sizeof(struct foo));
    105 	/* 
    106 	 * We do error checks for `n` and `ntd` but in case we read from a
    107 	 * file the program might break if the data is wrong (i.e fails the
    108 	 * error checks at least once) since it will keep going further down
    109 	 * into the file. The same doesn't apply for when reading from stdin --
    110 	 * we can just give it a new number until it's correct.
    111 	 */
    112 	do {
    113 		printf("\rp: ");
    114 		/* 
    115 		 * Keep scanf(3)'s return value to make sure we
    116 		 * did read valid input.
    117 		 */
    118 		rc = scanf("%d", &f->ntd);
    119 		/* Flush input buffer. */
    120 		(void)getchar();
    121 	/* Cannot have < 0 threads. */
    122 	} while (f->ntd < 0 || rc != 1);
    123 
    124 	do {
    125 		printf("\rn: ");
    126 		scanf("%d", &f->n);
    127 		(void)getchar();
    128 	/* Make sure `n` is positive and also a multiple of `ntd`. */
    129 	} while (f->n < 0 || f->n % f->ntd != 0 || rc != 1);
    130 
    131 	tds = emalloc(f->ntd * sizeof(pthread_t));
    132 	f->arr = emalloc(f->n * sizeof(int));
    133 	f->sums = emalloc(f->ntd * sizeof(int));
    134 
    135 	/* Fill the vector. */
    136 	srand(time(NULL));
    137 	for (i = 0; i < f->n; i++)
    138 		f->arr[i] = rand() % (ULIM - LLIM) + LLIM;
    139 
    140 	if (pthread_mutex_init(&f->mutex, NULL) != 0)
    141 		err(1, "pthread_mutex_init");
    142 	/* 
    143 	 * Start multithreading. For each thread we assign `calc`
    144 	 * to be the callback function that each thread will call
    145 	 * and we pass it the `foo` struct as an argument to avoid
    146 	 * declaring globals.
    147 	 */
    148 	for (i = 0; i < f->ntd; i++)
    149 		if (pthread_create(&tds[i], NULL, calc, (void *)f) != 0)
    150 			err(1, "pthread_create");
    151 	for (i = 0; i < f->ntd; i++)
    152 		if (pthread_join(tds[i], NULL) != 0)
    153 			err(1, "pthread_join");
    154 	totalsum = 0;
    155 	while (f->ntd--)
    156 		totalsum += *f->sums++;
    157 	printf("total sum: %d\n", totalsum);
    158 
    159 	(void)pthread_mutex_destroy(&f->mutex);
    160 	pthread_exit(NULL);
    161 	free(tds);
    162 	free(f->arr);
    163 	free(f->sums);
    164 	free(f);
    165 
    166 	return 0;
    167 }