-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.cpp
349 lines (275 loc) · 7.8 KB
/
main.cpp
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
// c headers
#include <aio.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
// cpp headers
#include <iostream>
#include <unordered_map>
// relative headers
#include "handlers.hpp"
// append a number to this string and you get the filepath
#define FIFO_TEMPLATE "/home/i516739/fifo"
// n is the number of children
#define n 3
// struct that a child recieves
struct msg {
int value;
};
// struct that a parent sends
struct ans {
int value;
};
#define LOG
/**
* currenly not used - for setting tty raw mode
*
struct termios orig_termios;
void disableRawMode() {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
}
void enableRawMode() {
tcgetattr(STDIN_FILENO, &orig_termios);
atexit(disableRawMode);
struct termios raw = orig_termios;
raw.c_lflag &= ~(ECHO);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
*/
int main() {
// signal mask for SIGCHLD
sigset_t sigset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGCHLD);
sigprocmask(SIG_BLOCK, &sigset, NULL);
// TODO set the terminal to raw mode - so that
// I can read single characters
// for now, read one character from tty,
// attached to the current process
// these hold file descriptors for FIFOs
int my_fds[n];
// this holds process ids ofchildren processes
int children_pids[n];
// signal handler for catching zombies
signal(SIGCHLD, HReapZombies);
// todo: maybe no sense in calling this?
// umask(0);
// pipe for synchronisation - when
// all children close it, we
// can read EOF in the main process => works as
// a kind of a barrier
int pfd[2];
pipe(pfd);
// dummy variable to read from synchronisation pipe
int dummy_var;
#ifdef LOG
std::cout << "CREATED PIPE" << std::endl;
#endif
for (int i = 0; i < n; ++i) {
// SETUP PART
// TODO: do it in current directory instead
std::string str = FIFO_TEMPLATE + std::to_string(i);
// TODO: use other permissions, we just create
// regular file in current diirectory (we will do so)
mkfifo(str.c_str(), S_IRUSR | S_IWUSR | S_IWGRP);
// open fifo to read from it later
int rfd = open(str.c_str(), O_RDONLY | O_NONBLOCK);
my_fds[i] = rfd;
// send a messge to a child
int wfd = open(str.c_str(), O_WRONLY);
msg yo_msg = {1};
write(wfd, &yo_msg, sizeof(msg));
int success = fork();
if (success == 0) {
// close read end - see comment
// above about synchronization pipe
close(pfd[0]);
// read data from parent
int fd = open(str.c_str(), O_RDONLY);
msg my_msg;
read(fd, &my_msg, sizeof(msg));
// ready to report that we have finished - see comment
// above about synchronization pipe
close(pfd[1]);
// useful work here
sleep(2 * i + 2);
// reponse
ans a;
a.value = i + 1;
/**
* UNCOMMENT IF YOU WANT TO
* EXPERIMENT WITH SHORT-CIRCUIT
* EVALUATION
if (i == 1) {
a.value = 0;
}
*/
int wdf = open(str.c_str(), O_WRONLY);
write(wdf, &a, sizeof(ans));
_exit(0);
}
#ifdef LOG
std::cout << "AFTER FORK " << std::endl;
#endif
// remember the pid, we will need it
children_pids[i] = success;
}
// MAIN BLOCK
// close fd to synchronise yourself with children -
// see comment above about synchronization pipe
close(pfd[1]);
// wait for children to finish reading
// what they have to read
if (read(pfd[0], &dummy_var, sizeof(int)) < 0) {
std::cout << "=== error reading in from sync pipe === " << std::endl;
}
// return values from children
int results[n];
// how many children have reported
int ready_cnt = 0;
// ready or not to break the loop?
int ready_flag = 0;
// for select system call:
// sigset we will watch
fd_set reads;
// optimization parameter for select
// set to max value of all file descriptors
// that we are watching + 1
int nfd = 0;
for (int i = 0; i < n; ++i) {
nfd = (nfd > my_fds[i] ? nfd : my_fds[i]);
}
++nfd;
#ifdef LOG
std::cout << "NFD VALUE: " << nfd << std::endl;
#endif
// TODO remove this? what is it for?
for (int i = 0; i < n; ++i) {
results[i] = -1;
}
while (!ready_flag) {
// prepare file descriptors of interest
FD_ZERO(&reads);
for (int i = 0; i < n; ++i) {
// have already read response -> leave it alone
if (results[i] >= 0) {
continue;
}
FD_SET(my_fds[i], &reads);
}
// init file descriptors to watch them
FD_SET(STDIN_FILENO, &reads);
// do select
int ret_ = pselect(nfd, &reads, NULL, NULL, NULL, &sigset);
#ifdef LOG
// show return value
std::cout << "--RETVAL" << ret_ << std::endl;
#endif
// for every file descriptor, show their status
for (int i = 0; i < n; ++i) {
#ifdef LOG
std::cout << "IS_SET (T/F): " << FD_ISSET(my_fds[i], &reads) << std::endl;
#endif
}
// someone wants to report
if (FD_ISSET(STDIN_FILENO, &reads)) {
#ifdef LOG
std::cout << "LISTENING" << std::endl;
#endif
// ready to read, do it!
char c;
std::cin >> c;
if (c == 'q') { // user wanted to quit
// the user wanted to quit
// but maybe we can repotr ot him now?
// let's check that
bool can_report_before_quitting = true;
for (int i = 0; i < n && can_report_before_quitting; ++i) {
can_report_before_quitting =
can_report_before_quitting && (results[i] >= 0);
}
if (can_report_before_quitting) { // just do it
#ifdef LOG
std::cout << "===WAS ABLE TO TELL THE RESULTS" << std::endl;
#endif
// send SIGTERM to children and exit
for (int j = 0; j < n; ++j) {
kill(children_pids[j], SIGTERM);
}
// print results
for (int i = 0; i < n; ++i) {
std::cout << results[i] << std::endl;
}
// exit with closing all
// fds that were open automatically
exit(1);
}
// TODO send SIGTERM or something else
// that gets handled automatically for NOW;
// LATER write a handler for children to quit nicely
#ifdef LOG
std::cout << "===TERMINATE EVERYTHING! " << std::endl;
#endif
for (int j = 0; j < n; ++j) {
kill(children_pids[j], SIGTERM);
}
exit(1);
}
}
for (int i = 0; i < n; i++) {
// already remembered or not ready to read
if ((results[i] >= 0) || !(FD_ISSET(my_fds[i], &reads)))
continue;
// safe to read here - no blocking
ans a;
int ret_val = read(my_fds[i], &a, sizeof(ans));
// app logic
if (ret_val > 0) {
if (a.value == 0) {
#ifdef LOG
std::cout << "===SHORT CIRCUIT EVALUATION" << std::endl;
#endif
std::cout << "NULL" << std::endl;
// TODO teminate here nively - same as above - change this
// to something better
#ifdef LOG
std::cout << "===TERMINATE EVERYTHING! " << std::endl;
#endif
for (int j = 0; j < n; ++j) {
kill(children_pids[j], SIGTERM);
}
exit(1);
}
#ifdef LOG
std::cout << "===USUAL PROCESSING " << std::endl;
#endif
results[i] = a.value;
++ready_cnt;
}
}
if (ready_cnt == n)
ready_flag = 1;
#ifdef LOG
std::cout << "===score for next iteration " << ready_cnt
<< std::endl;
#endif
}
#ifdef LOG
std::cout << "==REPORT FINAL RESULTS; WERE ABLE TO FINISH" << std::endl;
#endif
for (int i = 0; i < n; ++i) {
std::cout << results[i] << std::endl;
}
// unblock signals from children
sigprocmask(SIG_UNBLOCK, &sigset, NULL);
int pid;
while ((pid = wait(NULL)) > 0) {
std::cout << "main waited for " << pid << std::endl;
continue;
}
return 0;
}