-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsort.c
executable file
·384 lines (337 loc) · 10.7 KB
/
sort.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
/**
* Piotr Szuberski, ps386182
* Program written for Concurent Programming lecture at University of Warsaw.
* It has to perform concurrent sorting of int array, size N*2 in UNIX
* by creating N processes A and N-1 processes B.
* Processes A(i) compare and swap T[2*i] and T[2*i+1] elements and signalise to
* processes B(i) and B(i-1) (excluding A(0) and A(N-1)).
* Processes B(i) compare and swap T[2*i+1] and T[2*i+2] elements and signalise
* to processes A(i) and A(i+1).
* The program implements checking whether the array isn't already sorted by
* counting compares done in every loop processes A and B perform.
* It slows down program a lot for the pessimistic examples.
* The program uses unnamed shared memory and semaphores to communicate between
* processes.
*/
#include <stdio.h>
#include <semaphore.h>
#include <zconf.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <sys/wait.h>
#define A 0
#define B 1
#define STOP 2
#define SWAPS 3
/**
* @table - pointer to table of ints of size N*2 to be sorted
* @semaphoresA - table of semaphores of processes A, size N
* @semaphoresB - table of semaphores of processes B, size N-1
* @blockerA - semaphore forcing processes A to wait for each other before awakening
* processes B
* @blockerB - semaphore forcing processes B to wait for each other before awakening
* processes A
* @mutex - semaphore protecting the flags value
* @flags - table of 4 unsigned integers, indexed as follows:
* A - counts how many processes A finished comparing
* B - counts how many processes B finished comparing
* STOP - if != 0 then the table is already sorted
* SWAPS - counts how many swaps has been performed in the current loop
*/
struct shared {
int* table;
sem_t* semaphoresA;
sem_t* semaphoresB;
sem_t* blockerA;
sem_t* blockerB;
sem_t* mutex;
unsigned* flags;
};
void syserr(const char* msg) {
fprintf(stderr, "%s", msg);
exit(1);
}
/**
* Swap function
* @param x1
* @param x2
*/
static void swap(int* x1, int* x2) {
int tmp = *x1;
*x1 = *x2;
*x2 = tmp;
}
/**
* Helper function making it easier to initialize semaphores
* @param sem
* @param start_value
*/
static void init_semaphore(sem_t* sem, unsigned start_value) {
if (sem_init(sem, 1, start_value) == -1)
syserr("sem_init");
}
/**
* Helper function making it easier to destroy semaphores
* @param sem
*/
static void destroy_semaphore(sem_t* sem) {
if (sem_destroy(sem) == -1)
syserr("sem_destroy");
}
/**
* Helper function making it easier to perform P() operation on semaphores
* @param sem
*/
static void P(sem_t* sem) {
if (sem_wait(sem))
syserr("sem_wait");
}
/**
* Helper function making it easier to perform V() operation on semaphores
* @param sem
*/
static void V(sem_t* sem) {
if (sem_post(sem))
syserr("sem_post");
}
/**
* Function printing the table
* @param table - table of ints
* @param N - table size (doubled N value)
*/
static void print_table(const int* table, unsigned N) {
unsigned i, size;
size = N * 2;
for (i = 0; i < size; ++i)
printf("%d\n", table[i]);
}
/**
* Initializes the unnamed shared memory, allocates amount of space needed
* @param N - pointer to N value
* @param *size_mem - pointer to variable containing whole size of shared memory
* @return pointer to the beginning of shared memory array
*/
static void* init_memory(unsigned* N, size_t* size_mem) {
int fd_memory = -1;
int flags, prot;
void* mapped_mem;
prot = PROT_READ | PROT_WRITE;
flags = MAP_SHARED | MAP_ANONYMOUS;
if (scanf("%u", N) == EOF)
syserr("scanf");
if (*N < 2)
syserr("N too low");
*size_mem = sizeof(sem_t) * (2 * (*N) + 2) + sizeof(int) * ((*N) * 2 + 4);
mapped_mem = mmap(NULL, *size_mem, prot, flags, fd_memory, 0);
if(mapped_mem == MAP_FAILED)
syserr("mmap");
return mapped_mem;
}
/**
* Initializes the structure of pointers to areas of shared memory
* @param shm - pointer to the initialized structure
* @param N - N value
* @param mapped_mem - pointer to the beginning of array of shared memory
*/
static void init_shm(struct shared *shm, unsigned N, void *mapped_mem) {
unsigned i, size_tab, sizeB;
size_t dist1 = sizeof(int) * N * 2;
size_t dist2 = sizeof(sem_t);
size_tab = N * 2;
sizeB = N - 1;
shm->table = (int*) (mapped_mem);
shm->semaphoresA = (sem_t*) ((char*) mapped_mem + dist1);
shm->semaphoresB = (sem_t*) ((char*) mapped_mem + dist1 + dist2*N);
shm->blockerA = (sem_t*) ((char*) mapped_mem + dist1 + dist2*(2*N-1));
shm->blockerB = (sem_t*) ((char*) mapped_mem + dist1 + dist2*(2*N));
shm->mutex = (sem_t*) ((char*) mapped_mem + dist1 + dist2*(2*N+1));
shm->flags = (unsigned*) ((char*) mapped_mem + dist1 + dist2*(2*N+2));
for (i = 0; i < size_tab; ++i)
if(scanf("%d", &shm->table[i]) == EOF)
syserr("scanf");
for (i = 0; i < N; ++i)
init_semaphore(&shm->semaphoresA[i], (i == 0 || i == sizeB) ? 1 : 2);
for (i = 0; i < sizeB; ++i)
init_semaphore(&shm->semaphoresB[i], 0);
init_semaphore(shm->blockerA, 0);
init_semaphore(shm->blockerB, 0);
init_semaphore(shm->mutex, 1);
shm->flags[A] = shm->flags[B] = shm->flags[SWAPS] = shm->flags[STOP] = 0;
}
/**
* Destroys the initialized semaphores in structure shm
* @param shm - struct with pointers to the shared memory
*/
static void destruct(struct shared* shm, unsigned N) {
unsigned i, sizeB;
sizeB = N -1;
for (i = 0; i < N; ++i)
destroy_semaphore(&shm->semaphoresA[i]);
for (i = 0; i < sizeB; ++i)
destroy_semaphore(&shm->semaphoresB[i]);
destroy_semaphore(shm->blockerA);
destroy_semaphore(shm->blockerB);
destroy_semaphore(shm->mutex);
}
/**
* Process A, indexed by i, compares i*2 and i*2+1 value of array in shared memory
* and swaps it if left value is higher.
* First, the process tries to pass its semaphore.
* Then, it checks whether the flag[STOP] isn't set to be true value - if so, it
* awakens the processes B it is connected with.
* Next, it compares its values and if the swap was executed, increases flag[COMP].
* After that all processes but not the last one are stopped on stopperA. The
* last one does V(stopperA) and it is repeated until the last process came
* across stopperA. The last one opens the mutex.
* @param shm -structure with pointers to the shared memory
* @param N - N value
* @param i - index of the process A
*/
void processA(struct shared* shm, unsigned N, unsigned i) {
int* table;
unsigned idxl, idxr;
sem_t *semA, *semlB, *semrB;
idxl = i * 2;
idxr = idxl + 1;
table = shm->table;
semA = &shm->semaphoresA[i];
semrB = &shm->semaphoresB[i];
semlB = &shm->semaphoresB[i-1];
while (1) {
if (i != 0)
P(semA);
if (i != N - 1)
P(semA);
if (shm->flags[STOP]) {
if (i != 0)
V(semlB);
if (i != N - 1)
V(semrB);
return;
}
if (table[idxl] > table[idxr]) {
swap(&table[idxl], &table[idxr]);
P(shm->mutex);
++(shm->flags[SWAPS]);
V(shm->mutex);
}
P(shm->mutex);
if (++(shm->flags[A]) != N) {
V(shm->mutex);
P(shm->blockerA);
}
if (--(shm->flags[A]) != 0)
V(shm->blockerA);
else
V(shm->mutex);
if (i != 0)
V(semlB);
if (i != N - 1)
V(semrB);
}
}
/**
* Process B, indexed by i, compares i*2+1 and i*2+2 value of array in shared memory
* and swaps it if left value is higher.
* First, the process tries to pass its semaphore
* Then, it checks whether the flag[STOP] isn't set to be true value. If so, process
* stops executing.
* Next, it compares its values and if the swap was executed, increases flag[COMP].
* After that all processes but not the last one are stopped on stopperB. The
* last one checks how the loop went. If there was no swap performed, then it sets
* flag[STOP] to be true and zeroes flag[SWAP]. Then, the last process B awakened
* opens mutex.
* @param shm -structure with pointers to the shared memory
* @param N - N value
* @param i - index of the process B
*/
void processB(struct shared* shm, unsigned N, unsigned i) {
int *table;
unsigned idxl, idxr, sizeB;
sem_t *semB, *semlA, *semrA;
idxl = i * 2 + 1;
idxr = idxl + 1;
sizeB = N - 1;
table = shm->table;
semB = &shm->semaphoresB[i];
semrA = &shm->semaphoresA[i];
semlA = &shm->semaphoresA[i + 1];
while (1) {
P(semB);
P(semB);
if (shm->flags[STOP])
return;
if (table[idxl] > table[idxr]) {
swap(&table[idxl], &table[idxr]);
P(shm->mutex);
++(shm->flags[SWAPS]);
V(shm->mutex);
}
P(shm->mutex);
if (++(shm->flags[B]) == sizeB) {
if (shm->flags[SWAPS] == 0)
shm->flags[STOP] = 1;
shm->flags[SWAPS] = 0;
} else {
V(shm->mutex);
P(shm->blockerB);
}
if (--(shm->flags[B]) != 0)
V(shm->blockerB);
else
V(shm->mutex);
V(semlA);
V(semrA);
}
}
/**
* Main function - first reads N value from the standard output and allocates
* unnamed shared memory and calculates its size, then initializes helping
* structure shm, then forks N processes A and N-1 processes B, waits for them
* to finish and lastly prints the (hopefully) sorted array and destroys
* everything it allocated.
*/
int main() {
unsigned N, i, sizeB, processes_number;
void* mapped_mem;
struct shared shm;
int* table;
size_t size_mem;
mapped_mem = init_memory(&N, &size_mem);
init_shm(&shm, N, mapped_mem);
table = shm.table;
sizeB = N - 1;
processes_number = N * 2 - 1;
for (i = 0; i < N; ++i) {
switch (fork()) {
case -1:
syserr("forkA");
break;
case 0:
processA(&shm, N, i);
munmap(mapped_mem, size_mem);
return 0;
default:
break;
}
}
for (i = 0; i < sizeB; ++i) {
switch (fork()) {
case -1:
syserr("forkB");
break;
case 0:
processB(&shm, N, i);
munmap(mapped_mem, size_mem);
return 0;
default:
break;
}
}
for (i = 0; i < processes_number; ++i)
wait(0);
print_table(table, N);
destruct(&shm, N);
munmap(mapped_mem, size_mem);
return 0;
}