ex1_1.c (3266B)
1 #include <err.h> 2 #include <pthread.h> 3 #include <semaphore.h> 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <unistd.h> 8 9 /* 10 * Εργαστήριο ΛΣ2 (Δ6) / Εργασία 2: Άσκηση 1.1 / 2020-2021 11 * Ονοματεπώνυμο: Χρήστος Μαργιώλης 12 * ΑΜ: 19390133 13 * Τρόπος μεταγλώττισης: `cc ex1_1.c -lpthread -lrt -o ex1_1` 14 */ 15 16 /* Calculate an array's length. */ 17 #define LEN(x) (sizeof(x) / sizeof(x[0])) 18 19 struct foo { 20 char *str; 21 int tid; 22 sem_t sem; 23 }; 24 25 /* Function declarations */ 26 static void *thread_callback(void *); 27 static void *emalloc(size_t); 28 static void usage(void); 29 30 /* Global variables */ 31 static char *argv0; 32 /* 33 * Each thread will print one of these. Everything will adapt in case more 34 * strings are added to the array. 35 */ 36 static const char *nums[] = { 37 "<one>", 38 "<two>", 39 "<three>", 40 }; 41 42 static void * 43 thread_callback(void *foo) 44 { 45 struct foo *f; 46 47 f = (struct foo *)foo; 48 /* Lock the semaphore (decrement by one). */ 49 if (sem_wait(&f->sem) < 0) 50 err(1, "sem_wait"); 51 /* 52 * Get appropriate string. `f->tid` has the thread's ID -- we'll use 53 * it to give each thread a unique string. Thread 0 will get the first 54 * string, thread 1 the second, and so on. 55 */ 56 if ((f->str = strdup(nums[f->tid++])) == NULL) 57 err(1, "strdup"); 58 fputs(f->str, stdout); 59 free(f->str); 60 /* Unlock the semaphore (increment by one). */ 61 if (sem_post(&f->sem) < 0) 62 err(1, "sem_post"); 63 64 return NULL; 65 } 66 67 static void * 68 emalloc(size_t nb) 69 { 70 void *p; 71 72 if ((p = malloc(nb)) == NULL) 73 err(1, "malloc"); 74 75 return p; 76 } 77 78 static void 79 usage(void) 80 { 81 fprintf(stderr, "usage: %s [-n times]\n", argv0); 82 exit(1); 83 } 84 85 int 86 main(int argc, char *argv[]) 87 { 88 struct foo *f; 89 pthread_t *tds; 90 int i, len, n = 5; 91 char ch; 92 93 argv0 = *argv; 94 while ((ch = getopt(argc, argv, "n:")) != -1) { 95 switch (ch) { 96 case 'n': 97 /* 98 * Manually choose how many times the string sequence 99 * is going to be printed. Obviously, we cannot allow 100 * an N less than 1. By default `n` is 5 (see 101 * declaration above). 102 */ 103 if ((n = atoi(optarg)) < 1) 104 errx(1, "value must be greater than 1"); 105 break; 106 case '?': 107 default: 108 usage(); 109 } 110 } 111 argc -= optind; 112 argv += optind; 113 114 len = LEN(nums); 115 /* 116 * Instead of hardcoding how many threads we want to have, the 117 * number of threads is always equal to how many elements the 118 * `nums` array has. That means in case we want to add/remove 119 * entries from `nums`, everything will adapt automatically. 120 */ 121 tds = emalloc(len * sizeof(pthread_t)); 122 f = emalloc(sizeof(struct foo)); 123 124 /* 125 * sem_init(3)'s second argument defines whether the semaphore 126 * should be shared by multiple processes or not. This is done 127 * by passing a non-zero value, but in this case we want the 128 * semaphore to be shared only by this process. 129 */ 130 if (sem_init(&f->sem, 0, 1) < 0) 131 err(1, "sem_init"); 132 133 while (n--) { 134 f->tid = 0; 135 for (i = 0; i < len; i++) 136 if (pthread_create(&tds[i], NULL, 137 thread_callback, (void *)f) != 0) 138 err(1, "pthread_create"); 139 for (i = 0; i < len; i++) 140 if (pthread_join(tds[i], NULL) != 0) 141 err(1, "pthread_join"); 142 } 143 printf("\n"); 144 145 (void)sem_destroy(&f->sem); 146 pthread_exit(NULL); 147 free(tds); 148 free(f); 149 150 return 0; 151 }