Commit | Line | Data |
---|---|---|
d44e3c4f | 1 | /****************************************************************************** |
2 | * Copyright (c) 2000-2016 Ericsson Telecom AB | |
3 | * All rights reserved. This program and the accompanying materials | |
4 | * are made available under the terms of the Eclipse Public License v1.0 | |
5 | * which accompanies this distribution, and is available at | |
6 | * http://www.eclipse.org/legal/epl-v10.html | |
7 | * | |
8 | * Contributors: | |
9 | * Baji, Laszlo | |
10 | * Balasko, Jeno | |
11 | * Czimbalmos, Eduard | |
12 | * Feher, Csaba | |
13 | * Kovacs, Ferenc | |
14 | * Pilisi, Gergely | |
15 | * Raduly, Csaba | |
16 | * Szabados, Kristof | |
17 | * Szabo, Janos Zoltan – initial implementation | |
18 | * Szalai, Gabor | |
19 | * Pandi, Krisztian | |
20 | * | |
21 | ******************************************************************************/ | |
970ed795 EL |
22 | #include <string.h> |
23 | #include <time.h> | |
24 | #include <sys/time.h> | |
25 | #include <math.h> | |
26 | #include <stdlib.h> | |
27 | #include <unistd.h> | |
28 | #include <fcntl.h> | |
29 | #include <errno.h> | |
30 | ||
a38c6d4c | 31 | #include <sys/select.h> |
970ed795 EL |
32 | #include <sys/types.h> |
33 | #include <poll.h> | |
34 | #ifdef USE_EPOLL | |
35 | #include <sys/epoll.h> | |
36 | #endif | |
37 | ||
38 | #include "Types.h" | |
39 | ||
40 | #include "Snapshot.hh" | |
41 | #include "Fd_And_Timeout_User.hh" | |
42 | #include "Timer.hh" | |
43 | #include "Logger.hh" | |
44 | #include "Error.hh" | |
45 | #include "Event_Handler.hh" | |
46 | ||
47 | ||
48 | static const int MAX_INT_VAL = (int) ((unsigned int) -2 >> 1u); | |
49 | ||
50 | /****************************************************************************** | |
51 | * class FdMap * | |
52 | ******************************************************************************/ | |
53 | ||
54 | int FdMap::nItems; | |
55 | int FdMap::capacity; | |
56 | FdMap::Item FdMap::items1[ITEM1_CAPACITY]; | |
57 | FdMap::Data * FdMap::items2; | |
58 | #ifndef USE_EPOLL | |
59 | pollfd FdMap::pollFds1[ITEM1_CAPACITY]; | |
60 | pollfd * FdMap::pollFds2; | |
61 | bool FdMap::needUpdate; | |
62 | int FdMap::nPollFdsFrozen; | |
63 | #endif | |
64 | #ifdef USE_EPOLL | |
65 | int FdMap::epollFd; | |
66 | epoll_event FdMap::epollEvents[MAX_EPOLL_EVENTS]; | |
67 | #endif | |
68 | ||
69 | fd_event_type_enum FdMap::add(int fd, Fd_Event_Handler * handler, | |
70 | fd_event_type_enum event) | |
71 | { | |
72 | if (handler == 0) | |
73 | TTCN_error("FdMap::add: Internal error"); // debug | |
74 | if (fd < 0 || fd >= capacity) { | |
75 | TTCN_error_begin("Trying to add events of an invalid file descriptor " | |
76 | "(%d) to the set of events handled by \"", fd); | |
77 | handler->log(); | |
78 | TTCN_Logger::log_event("\"."); | |
79 | TTCN_error_end(); | |
80 | } | |
81 | if ((event & ~(FD_EVENT_RD | FD_EVENT_WR | FD_EVENT_ERR)) != 0) { | |
82 | TTCN_error_begin("Trying to add invalid events (%d) of file descriptor " | |
83 | "(%d) to the set of events handled by \"", event, fd); | |
84 | handler->log(); | |
85 | TTCN_Logger::log_event("\"."); | |
86 | TTCN_error_end(); | |
87 | } | |
88 | #ifndef USE_EPOLL | |
89 | short pollEvent = eventToPollEvent(event); | |
90 | #endif | |
91 | if (items2 == 0) { | |
92 | int i = findInsPointInItems1(fd); | |
93 | if (i < nItems && fd == items1[i].fd) { | |
94 | // Already exists... | |
95 | if (items1[i].d.hnd != 0 && items1[i].d.hnd != handler) { | |
96 | TTCN_error_begin("Trying to add file descriptor (%d) " | |
97 | "events (%d) to the set of events handled by \"",fd,event); | |
98 | handler->log(); | |
99 | TTCN_Logger::log_event("\", but the events of the " | |
100 | "file descriptor already have a different handler: \""); | |
101 | if (items1[i].d.hnd != 0) items1[i].d.hnd->log(); | |
102 | TTCN_Logger::log_event("\"."); | |
103 | TTCN_error_end(); | |
104 | } | |
105 | fd_event_type_enum oldEvent; | |
106 | #ifdef USE_EPOLL | |
107 | oldEvent = static_cast<fd_event_type_enum>(items1[i].d.evt); | |
108 | items1[i].d.evt |= static_cast<short>(event); | |
109 | #else | |
110 | items1[i].d.hnd = handler; // in case it is a frozen deleted item | |
111 | oldEvent = pollEventToEvent(pollFds1[items1[i].d.ixPoll].events); | |
112 | pollFds1[items1[i].d.ixPoll].events |= pollEvent; | |
113 | #endif | |
114 | return oldEvent; | |
115 | } | |
116 | if (nItems < ITEM1_CAPACITY) { | |
117 | // Size of the static array is enough | |
118 | for (int j = nItems - 1; j >= i; --j) items1[j + 1] = items1[j]; | |
119 | items1[i].fd = fd; | |
120 | #ifdef USE_EPOLL | |
121 | items1[i].d.evt = static_cast<short>(event); | |
122 | items1[i].d.ixE = -1; | |
123 | #else | |
124 | pollFds1[nItems].fd = fd; | |
125 | pollFds1[nItems].events = pollEvent; | |
126 | pollFds1[nItems].revents = 0; | |
127 | items1[i].d.ixPoll = nItems; | |
128 | #endif | |
129 | items1[i].d.hnd = handler; | |
130 | ++nItems; | |
131 | return static_cast<fd_event_type_enum>(0); | |
132 | } | |
133 | // Copying items to the bigger dynamically allocated array | |
134 | items2 = new Data[capacity]; // items2 is initialized in the constructor | |
135 | for (i = 0; i < nItems; ++i) { | |
136 | items2[items1[i].fd] = items1[i].d; | |
137 | items1[i].init(); // not necessary - for debugging | |
138 | } | |
139 | #ifndef USE_EPOLL | |
140 | pollFds2 = new pollfd[capacity]; | |
141 | i = 0; | |
142 | while (i < nItems) { pollFds2[i] = pollFds1[i]; init(pollFds1[i++]); } | |
143 | while (i < ITEM1_CAPACITY) { init(pollFds2[i]); init(pollFds1[i++]); } | |
144 | while (i < capacity) init(pollFds2[i++]); | |
145 | //Note: From the above three loops: only the copying is needed | |
146 | // The initializations are only for debugging | |
147 | #endif | |
148 | // adding item... | |
149 | } else { | |
150 | if (findInItems2(fd)) { | |
151 | // Already exists... | |
152 | if (items2[fd].hnd != 0 && items2[fd].hnd != handler) { | |
153 | TTCN_error_begin("Trying to add file descriptor (%d) " | |
154 | "events (%d) to the set of events handled by \"",fd,event); | |
155 | handler->log(); | |
156 | TTCN_Logger::log_event("\", but the events of the " | |
157 | "file descriptor already have a different handler: \""); | |
158 | if (items2[fd].hnd != 0) items2[fd].hnd->log(); | |
159 | TTCN_Logger::log_event("\"."); | |
160 | TTCN_error_end(); | |
161 | } | |
162 | fd_event_type_enum oldEvent; | |
163 | #ifdef USE_EPOLL | |
164 | oldEvent = static_cast<fd_event_type_enum>(items2[fd].evt); | |
165 | items2[fd].evt |= static_cast<short>(event); | |
166 | #else | |
167 | items2[fd].hnd = handler; // in case it is a frozen deleted item | |
168 | oldEvent = pollEventToEvent(pollFds2[items2[fd].ixPoll].events); | |
169 | pollFds2[items2[fd].ixPoll].events |= pollEvent; | |
170 | #endif | |
171 | return oldEvent; | |
172 | } | |
173 | // adding item... | |
174 | } | |
175 | // ...adding item | |
176 | #ifdef USE_EPOLL | |
177 | items2[fd].evt = static_cast<short>(event); | |
178 | items2[fd].ixE = -1; | |
179 | #else | |
180 | pollFds2[nItems].fd = fd; | |
181 | pollFds2[nItems].events = pollEvent; | |
182 | pollFds2[nItems].revents = 0; | |
183 | items2[fd].ixPoll = nItems; | |
184 | #endif | |
185 | items2[fd].hnd = handler; | |
186 | ++nItems; | |
187 | return static_cast<fd_event_type_enum>(0); | |
188 | } | |
189 | ||
190 | fd_event_type_enum FdMap::remove(int fd, const Fd_Event_Handler * handler, | |
191 | fd_event_type_enum event) | |
192 | { | |
193 | // Errors in Test Ports may be detected at this point | |
194 | // handler is used only for checking; | |
195 | // handler is 0 to remove a frozen deleted item | |
196 | if (fd < 0 || fd >= capacity) { | |
197 | TTCN_error_begin("Trying to remove events of an invalid file " | |
198 | "descriptor (%d) from the set of events handled by \"", fd); | |
199 | if (handler != 0) handler->log(); | |
200 | TTCN_Logger::log_event("\"."); | |
201 | TTCN_error_end(); | |
202 | } | |
203 | if ((event & ~(FD_EVENT_RD | FD_EVENT_WR | FD_EVENT_ERR)) != 0) { | |
204 | TTCN_error_begin("Trying to remove invalid events (%d) of file " | |
205 | "descriptor (%d) from the set of events handled by \"", event, fd); | |
206 | if (handler != 0) handler->log(); | |
207 | TTCN_Logger::log_event("\"."); | |
208 | TTCN_error_end(); | |
209 | } | |
210 | fd_event_type_enum oldEvent; | |
211 | #ifndef USE_EPOLL | |
212 | short pollEvent = eventToPollEvent(event); | |
213 | #endif | |
214 | if (items2 == 0) { | |
215 | int i = findInItems1(fd); | |
216 | if (i < 0) { | |
217 | TTCN_warning_begin("Trying to remove file descriptor (%d) " | |
218 | "events (%d) from the set of events handled by \"", | |
219 | fd, event); | |
220 | if (handler != 0) handler->log(); | |
221 | TTCN_Logger::log_event("\", but events of the file descriptor " | |
222 | "do not have a handler."); | |
223 | TTCN_warning_end(); | |
224 | // Ignore errors for HP53582. | |
225 | return FD_EVENT_ERR; | |
226 | } | |
227 | if (handler != items1[i].d.hnd) { | |
228 | TTCN_error_begin("Trying to remove file descriptor (%d) " | |
229 | "events (%d) from the set of events handled by \"", fd, event); | |
230 | if (handler != 0) handler->log(); | |
231 | TTCN_Logger::log_event("\", but the events of the " | |
232 | "file descriptor have different handler: \""); | |
233 | if (items1[i].d.hnd != 0) items1[i].d.hnd->log(); | |
234 | TTCN_Logger::log_event("\"."); | |
235 | TTCN_error_end(); | |
236 | } | |
237 | #ifdef USE_EPOLL | |
238 | short ixE = items1[i].d.ixE; | |
239 | if (ixE >= 0) epollEvents[ixE].events &= ~eventToEpollEvent(event); | |
240 | oldEvent = static_cast<fd_event_type_enum>(items1[i].d.evt); | |
241 | if ((items1[i].d.evt &= ~static_cast<short>(event)) == 0) { | |
242 | --nItems; | |
243 | while (i < nItems) { items1[i] = items1[i + 1]; ++i; } | |
244 | items1[nItems].init(); // not necessary - for debugging | |
245 | } | |
246 | #else | |
247 | int j = items1[i].d.ixPoll; | |
248 | oldEvent = pollEventToEvent(pollFds1[j].events); | |
249 | pollFds1[j].revents &= ~pollEvent; | |
250 | if ((pollFds1[j].events &= ~pollEvent) == 0) { | |
251 | if (j >= nPollFdsFrozen) { | |
252 | if (j < nItems - 1) { | |
253 | pollFds1[j] = pollFds1[nItems - 1]; | |
254 | int k = findInItems1(pollFds1[j].fd); // reads nItems | |
255 | items1[k].d.ixPoll = j; | |
256 | } | |
257 | --nItems; | |
258 | while (i < nItems) { items1[i] = items1[i + 1]; ++i; } | |
259 | init(pollFds1[nItems]); // not necessary - for debugging | |
260 | items1[nItems].init(); // not necessary - for debugging | |
261 | } else { // The item is frozen; removal is postponed. | |
262 | items1[i].d.hnd = 0; | |
263 | needUpdate = true; | |
264 | } | |
265 | } | |
266 | #endif | |
267 | } else { | |
268 | if (!findInItems2(fd)) { | |
269 | TTCN_error_begin("Trying to remove file descriptor (%d) " | |
270 | "events (%d) from the set of events handled by \"", | |
271 | fd, event); | |
272 | if (handler != 0) handler->log(); | |
273 | TTCN_Logger::log_event("\", but events of the file descriptor " | |
274 | "do not have a handler."); | |
275 | TTCN_error_end(); | |
276 | } | |
277 | if (items2[fd].hnd != handler) { | |
278 | TTCN_error_begin("Trying to remove file descriptor (%d) " | |
279 | "events (%d) from the set of events handled by \"", fd, event); | |
280 | if (handler != 0) handler->log(); | |
281 | TTCN_Logger::log_event("\", but the events of the " | |
282 | "file descriptor have different handler: \""); | |
283 | items2[fd].hnd->log(); TTCN_Logger::log_event("\"."); | |
284 | TTCN_error_end(); | |
285 | } | |
286 | #ifdef USE_EPOLL | |
287 | short ixE = items2[fd].ixE; | |
288 | if (ixE >= 0) epollEvents[ixE].events &= ~eventToEpollEvent(event); | |
289 | oldEvent = static_cast<fd_event_type_enum>(items2[fd].evt); | |
290 | if ((items2[fd].evt &= ~static_cast<short>(event)) == 0) { | |
291 | --nItems; | |
292 | items2[fd].init(); // necessary to indicate an unused item | |
293 | if (nItems <= ITEM1_CAPACITY_LOW) { | |
294 | // could be improved with additional bookkeeping | |
295 | // items1 has to be kept ordered with fd as key | |
296 | for (int i = 0, n = 0; n < nItems && i < capacity; ++i) { | |
297 | if (findInItems2(i)) { | |
298 | items1[n].fd = i; | |
299 | items1[n++].d = items2[i]; | |
300 | } | |
301 | } | |
302 | delete[] items2; items2 = 0; | |
303 | //if (n < nItems) => error | |
304 | } | |
305 | } | |
306 | #else | |
307 | int i = items2[fd].ixPoll; | |
308 | oldEvent = pollEventToEvent(pollFds2[i].events); | |
309 | pollFds2[i].revents &= ~pollEvent; | |
310 | if ((pollFds2[i].events &= ~pollEvent) == 0) { | |
311 | if (i >= nPollFdsFrozen) { | |
312 | --nItems; | |
313 | if (i < nItems) { | |
314 | pollFds2[i] = pollFds2[nItems]; | |
315 | items2[pollFds2[i].fd].ixPoll = i; | |
316 | } | |
317 | init(pollFds2[nItems]); // not necessary - for debugging | |
318 | items2[fd].init(); // necessary to indicate an unused item | |
319 | if (nItems <= ITEM1_CAPACITY_LOW) { | |
320 | // items1 has to be kept ordered with fd as key | |
321 | copyItems2ToItems1(); | |
322 | delete[] items2; items2 = 0; | |
323 | delete[] pollFds2; pollFds2 = 0; | |
324 | } | |
325 | } else { // The item is frozen; removal is postponed. | |
326 | items2[fd].hnd = 0; | |
327 | needUpdate = true; | |
328 | } | |
329 | } | |
330 | #endif | |
331 | } | |
332 | return oldEvent; | |
333 | } | |
334 | ||
335 | fd_event_type_enum FdMap::find(int fd, Fd_Event_Handler * * handler) | |
336 | { | |
337 | Data * data; | |
338 | if (items2 == 0) { | |
339 | int i = findInItems1(fd); | |
340 | if (i < 0) { *handler = 0; return static_cast<fd_event_type_enum>(0); } | |
341 | data = &items1[i].d; | |
342 | } else { | |
343 | if (!findInItems2(fd)) { | |
344 | *handler = 0; return static_cast<fd_event_type_enum>(0); | |
345 | } | |
346 | data = &items2[fd]; | |
347 | } | |
348 | *handler = data->hnd; | |
349 | #ifdef USE_EPOLL | |
350 | return static_cast<fd_event_type_enum>(data->evt); | |
351 | #else | |
352 | return pollEventToEvent(getPollFds()[data->ixPoll].events); | |
353 | #endif | |
354 | } | |
355 | ||
356 | #ifndef USE_EPOLL | |
357 | void FdMap::copyItems2ToItems1() | |
358 | { | |
359 | if (nItems != ITEM1_CAPACITY_LOW) | |
360 | TTCN_error("FdMap::copyItems2ToItems1: Internal error"); | |
361 | if (ITEM1_CAPACITY_LOW == 0) return; | |
362 | int n = nItems; | |
363 | int i = 0; | |
364 | for (int m = n - 1; m != 0; ++i) m >>= 1; | |
365 | Item * d, itemsTmp[ITEM1_CAPACITY_LOW]; | |
366 | bool f = (i & 1) != 0; | |
367 | d = (i == 0 || f) ? items1 : itemsTmp; | |
368 | for (int j = 0, k = nItems - 1; j < k; j += 2) { | |
369 | pollFds1[j] = pollFds2[j]; pollFds1[j + 1] = pollFds2[j + 1]; | |
370 | int fd1 = pollFds1[j].fd, fd2 = pollFds1[j + 1].fd; | |
371 | if (fd1 <= fd2) { | |
372 | d[j].fd = fd1; d[j].d = items2[fd1]; | |
373 | d[j + 1].fd = fd2; d[j + 1].d = items2[fd2]; | |
374 | } else { | |
375 | d[j].fd = fd2; d[j].d = items2[fd2]; | |
376 | d[j + 1].fd = fd1; d[j + 1].d = items2[fd1]; | |
377 | } | |
378 | } | |
379 | if ((nItems & 1) != 0) { | |
380 | pollFds1[n - 1] = pollFds2[n - 1]; | |
381 | int fd1 = pollFds1[n - 1].fd; | |
382 | d[nItems - 1].fd = fd1; | |
383 | d[nItems - 1].d = items2[fd1]; | |
384 | } | |
385 | for (int j = 1; j < i; ++j) { | |
386 | Item * s = f ? items1 : itemsTmp; | |
387 | f = !f; | |
388 | d = f ? items1 : itemsTmp; | |
389 | int step = 1 << j; | |
390 | int step2 = step * 2; | |
391 | int w = 0; | |
392 | for (int k = 0; k < n; k += step2) { | |
393 | int u = k; | |
394 | int v = k + step; | |
395 | int uN = (n >= v) ? step : (n - u); | |
396 | int vN = (n >= v + step) ? step : (n - v); | |
397 | while (uN != 0 && vN > 0) { | |
398 | if (s[u].fd <= s[v].fd) { | |
399 | d[w++] = s[u++]; --uN; | |
400 | } else { | |
401 | d[w++] = s[v++]; --vN; | |
402 | } | |
403 | } | |
404 | while (uN != 0) { d[w++] = s[u++]; --uN; } | |
405 | while (vN > 0) { d[w++] = s[v++]; --vN; } | |
406 | } | |
407 | } | |
408 | } | |
409 | #endif | |
410 | ||
411 | #ifdef USE_EPOLL | |
412 | bool FdMap::epollMarkFds(int nEvents) | |
413 | { | |
414 | bool all_valid = true; | |
415 | for (int i = 0; i < nEvents; ++i) { | |
416 | int fd = epollEvents[i].data.fd; | |
417 | if (items2 == 0) { | |
418 | int j = findInItems1(fd); | |
419 | if (j >= 0) items1[j].d.ixE = i; | |
420 | else all_valid = false; | |
421 | } else { | |
422 | if (findInItems2(fd)) items2[fd].ixE = i; | |
423 | else all_valid = false; | |
424 | } | |
425 | } | |
426 | return all_valid; | |
427 | } | |
428 | ||
429 | void FdMap::epollUnmarkFds(int nEvents) | |
430 | { | |
431 | for (int i = 0; i < nEvents; ++i) { | |
432 | int fd = epollEvents[i].data.fd; | |
433 | if (items2 == 0) { | |
434 | int j = findInItems1(fd); | |
435 | if (j >= 0) items1[j].d.ixE = -1; | |
436 | } else { | |
437 | if (findInItems2(fd)) items2[fd].ixE = -1; | |
438 | } | |
439 | } | |
440 | } | |
441 | #else | |
442 | void FdMap::pollFreeze() | |
443 | { | |
444 | nPollFdsFrozen = nItems; | |
445 | } | |
446 | ||
447 | void FdMap::pollUnfreeze() | |
448 | { | |
449 | if (!needUpdate) { nPollFdsFrozen = 0; return; } | |
450 | int i = 0, j = nPollFdsFrozen; | |
451 | nPollFdsFrozen = 0; | |
452 | while (i < j) { | |
453 | pollfd & pollFd = getPollFds()[i]; | |
454 | if (pollFd.events == 0) { | |
455 | remove(pollFd.fd, 0, static_cast<fd_event_type_enum>( | |
456 | FD_EVENT_RD | FD_EVENT_WR | FD_EVENT_ERR)); | |
457 | if (nItems < j) j = nItems; | |
458 | // item at index i is changed and has to be checked if exists | |
459 | } else ++i; | |
460 | } | |
461 | needUpdate = false; | |
462 | } | |
463 | ||
464 | fd_event_type_enum FdMap::getPollREvent(int fd) | |
465 | { | |
466 | if (items2 == 0) { | |
467 | int i = findInItems1(fd); | |
468 | if (i >= 0) | |
469 | return pollEventToEvent(pollFds1[items1[i].d.ixPoll].revents); | |
470 | } else { | |
471 | if (findInItems2(fd)) | |
472 | return pollEventToEvent(pollFds2[items2[fd].ixPoll].revents); | |
473 | } | |
474 | return static_cast<fd_event_type_enum>(0); | |
475 | } | |
476 | #endif | |
477 | ||
478 | ||
479 | ||
480 | ||
481 | ||
482 | /****************************************************************************** | |
483 | * class Fd_Event_Handler * | |
484 | ******************************************************************************/ | |
485 | ||
486 | void Fd_Event_Handler::log() const | |
487 | { | |
488 | TTCN_Logger::log_event("handler <invalid>"); | |
489 | } | |
490 | ||
491 | ||
492 | ||
493 | ||
494 | ||
495 | ||
496 | /****************************************************************************** | |
497 | * class Fd_And_Timeout_Event_Handler * | |
498 | ******************************************************************************/ | |
499 | ||
500 | void Fd_And_Timeout_Event_Handler::Handle_Fd_Event(int, | |
501 | boolean, boolean, boolean) | |
502 | { | |
503 | TTCN_error("Fd_And_Timeout_Event_Handler::Handle_Fd_Event: " | |
504 | "Erroneous usage of class Fd_And_Timeout_Event_Handler"); | |
505 | // This method cannot be pure virtual because of necessary instantiation | |
506 | // inside TITAN | |
507 | } | |
508 | ||
509 | void Fd_And_Timeout_Event_Handler::Handle_Timeout(double) | |
510 | { | |
511 | TTCN_error("Fd_And_Timeout_Event_Handler::Handle_Timeout: " | |
512 | "Erroneous usage of class Fd_And_Timeout_Event_Handler"); | |
513 | // This method cannot be pure virtual because of necessary instantiation | |
514 | // inside TITAN | |
515 | } | |
516 | ||
517 | void Fd_And_Timeout_Event_Handler::Event_Handler(const fd_set *, | |
518 | const fd_set *, const fd_set *, double) | |
519 | { | |
520 | TTCN_error("Fd_And_Timeout_Event_Handler::Event_Handler: " | |
521 | "Erroneous usage of class Fd_And_Timeout_Event_Handler"); | |
522 | // This method cannot be pure virtual because of necessary instantiation | |
523 | // inside TITAN | |
524 | } | |
525 | ||
526 | Fd_And_Timeout_Event_Handler::~Fd_And_Timeout_Event_Handler() | |
527 | { | |
528 | // In case the event handler forgot to stop its timer, | |
529 | // stop it at this point. | |
530 | Fd_And_Timeout_User::set_timer(this, 0.0); | |
531 | // In case the event handler forgot to remove all of its file descriptor | |
532 | // events, remove those at this point. | |
533 | Fd_And_Timeout_User::remove_all_fds(this); | |
534 | } | |
535 | ||
536 | void Fd_And_Timeout_Event_Handler::log() const | |
537 | { | |
538 | TTCN_Logger::log_event("handler <unknown>"); | |
539 | } | |
540 | ||
541 | ||
542 | ||
543 | ||
544 | ||
545 | /* The maximal blocking time to be used in poll, epoll and select (in seconds). | |
546 | * On some systems (e.g. Solaris) the select call is a libc wrapper for | |
547 | * poll(2). In the wrapper the overflows are not always handled thus select | |
548 | * may return EINVAL in some cases. | |
549 | * This value is derived from the maximal possible value for the last argument | |
550 | * of poll, which is measured in milliseconds. */ | |
551 | #define MAX_BLOCK_TIME (~(0x80 << ((sizeof(int) - 1) * 8)) / 1000) | |
552 | ||
553 | ||
554 | ||
555 | ||
556 | ||
557 | /****************************************************************************** | |
558 | * class Fd_And_Timeout_User * | |
559 | ******************************************************************************/ | |
560 | ||
561 | Handler_List Fd_And_Timeout_User::timedList, | |
562 | Fd_And_Timeout_User::oldApiCallList; | |
563 | FdSets * Fd_And_Timeout_User::fdSetsReceived; | |
564 | FdSets * Fd_And_Timeout_User::fdSetsToHnds; | |
565 | int Fd_And_Timeout_User::nOldHandlers; | |
566 | bool Fd_And_Timeout_User::is_in_call_handlers; | |
567 | int Fd_And_Timeout_User::curRcvdEvtIx; | |
568 | ||
569 | inline void Fd_And_Timeout_User::checkFd(int fd) | |
570 | { | |
571 | if ( | |
572 | #ifdef USE_EPOLL | |
573 | fd == FdMap::epollFd || | |
574 | #endif | |
575 | fcntl(fd, F_GETFD, FD_CLOEXEC) < 0) | |
576 | TTCN_error("Trying to add events of an invalid file descriptor (%d)", fd); | |
577 | } | |
578 | ||
579 | void Fd_And_Timeout_User::add_fd(int fd, Fd_Event_Handler * handler, | |
580 | fd_event_type_enum event) | |
581 | { | |
582 | fd_event_type_enum oldEvent = FdMap::add(fd, handler, event); | |
583 | Fd_And_Timeout_Event_Handler * tmHnd = | |
584 | dynamic_cast<Fd_And_Timeout_Event_Handler *>(handler); | |
585 | if (tmHnd != 0) { | |
586 | if (tmHnd->fdSets != 0) { | |
587 | if (fd >= (int)FD_SETSIZE) | |
588 | TTCN_error("The file descriptor (%d) to be added is too big " | |
589 | "to be handled by Event_Handler. FD_SETSIZE is %d", | |
590 | fd, FD_SETSIZE); | |
591 | tmHnd->fdSets->add(fd, event); | |
592 | } | |
593 | if (oldEvent == 0) ++tmHnd->fdCount; | |
594 | } | |
595 | #ifdef USE_EPOLL | |
596 | epoll_event epollEvent; | |
597 | memset(&epollEvent, 0, sizeof(epollEvent)); | |
598 | epollEvent.events = FdMap::eventToEpollEvent(oldEvent | event); | |
599 | epollEvent.data.fd = fd; | |
600 | if ( ( (oldEvent == 0) ? | |
601 | epoll_ctl(FdMap::epollFd, EPOLL_CTL_ADD, fd, &epollEvent) : | |
602 | epoll_ctl(FdMap::epollFd, EPOLL_CTL_MOD, fd, &epollEvent) ) < 0 ) { | |
603 | checkFd(fd); | |
604 | TTCN_error("Fd_And_Timeout_User::add_fd: System call epoll_ctl failed " | |
605 | "when adding fd: %d, errno: %d", fd, errno); | |
606 | } | |
607 | #else | |
608 | checkFd(fd); | |
609 | #endif | |
610 | } | |
611 | ||
612 | void Fd_And_Timeout_User::remove_fd(int fd, Fd_Event_Handler * handler, | |
613 | fd_event_type_enum event) | |
614 | { | |
615 | if (handler == 0) | |
616 | TTCN_error("Fd_And_Timeout_User::remove_fd: Internal error"); // debug | |
617 | fd_event_type_enum oldEvent = FdMap::remove(fd, handler, event); | |
618 | // Ignore errors for HP53582. | |
619 | if (oldEvent == FD_EVENT_ERR) return; | |
620 | fd_event_type_enum newEvent = | |
621 | static_cast<fd_event_type_enum>(oldEvent & ~event); | |
622 | Fd_And_Timeout_Event_Handler * tmHnd = | |
623 | dynamic_cast<Fd_And_Timeout_Event_Handler *>(handler); | |
624 | if (tmHnd != 0) { | |
625 | if (newEvent == 0) --tmHnd->fdCount; | |
626 | if (tmHnd->getIsOldApi()) { | |
627 | fdSetsReceived->remove(fd, event); | |
628 | tmHnd->fdSets->remove(fd, event); | |
629 | } | |
630 | } | |
631 | #ifdef USE_EPOLL | |
632 | epoll_event epollEvent; | |
633 | memset(&epollEvent, 0, sizeof(epollEvent)); | |
634 | epollEvent.data.fd = fd; | |
635 | if (newEvent == 0) { | |
636 | if (epoll_ctl(FdMap::epollFd, EPOLL_CTL_DEL, fd, &epollEvent) < 0) { | |
637 | // Check if fd exists. If not, assume, that it was closed earlier. | |
638 | int errno_tmp = errno; | |
639 | if (fcntl(fd, F_GETFD, FD_CLOEXEC) >= 0) { | |
640 | errno = errno_tmp; | |
641 | TTCN_error("System call epoll_ctl failed when deleting fd: %d, " | |
642 | "errno: %d", fd, errno); | |
643 | } | |
644 | // fd was closed before removing it from the database of TITAN. | |
645 | // This removes fd from the epoll set causing epoll_ctl to fail. | |
646 | errno = 0; // It is not an error if epoll_ctl fails here | |
647 | } | |
648 | } else { | |
649 | // There is still event type to wait for. - This is the unusual case. | |
650 | epollEvent.events = FdMap::eventToEpollEvent(newEvent); | |
651 | if (epoll_ctl(FdMap::epollFd, EPOLL_CTL_MOD, fd, &epollEvent) < 0) { | |
652 | TTCN_error("System call epoll_ctl failed when removing fd: %d, " | |
653 | "errno: %d", fd, errno); | |
654 | } | |
655 | } | |
656 | #endif | |
657 | } | |
658 | ||
659 | void Fd_And_Timeout_User::set_timer(Fd_And_Timeout_Event_Handler * handler, | |
660 | double call_interval, | |
661 | boolean is_timeout, boolean call_anyway, boolean is_periodic) | |
662 | { | |
663 | if (call_interval != 0.0) { | |
664 | if (handler->list == 0) timedList.add(handler); | |
665 | handler->callInterval = call_interval; | |
666 | handler->last_called = TTCN_Snapshot::time_now(); | |
667 | handler->isTimeout = is_timeout; | |
668 | handler->callAnyway = call_anyway; | |
669 | handler->isPeriodic = is_periodic; | |
670 | } else { | |
671 | if (handler->list == &timedList) timedList.remove(handler); | |
672 | // Note: Normally the port may be only in timedList or in no list. | |
673 | // - The port is put in oldApiCallList only temporarily while calling | |
674 | // event handlers. | |
675 | // The set_timer method may be called from outside snapshot evaluation | |
676 | // or in the event handler. In both cases the port is removed from | |
677 | // oldApiCallList beforehand. | |
678 | // - However when MC requests a port to be unmapped: the request is | |
679 | // processed in the event handler of MC_Connection. In this event | |
680 | // handler a port may be unmapped which has been put in oldApiCallList | |
681 | // temporarily. | |
682 | handler->callInterval = 0.0; | |
683 | } | |
684 | } | |
685 | ||
686 | void Fd_And_Timeout_User::set_fds_with_fd_sets( | |
687 | Fd_And_Timeout_Event_Handler * handler, | |
688 | const fd_set *read_fds, const fd_set *write_fds, const fd_set *error_fds) | |
689 | { | |
690 | // Probaly in class PORT: Install_Handler should not be possible to be | |
691 | // called from new event handlers | |
692 | int fdLimit = FdMap::getFdLimit(); | |
693 | if ((int)FD_SETSIZE < fdLimit) fdLimit = FD_SETSIZE; | |
694 | if (handler->fdSets == 0) { | |
695 | if (handler->fdCount != 0) { | |
696 | // Usage of the new and old API is mixed. - It should not happen. | |
697 | // It is handled, but not most efficiently. | |
698 | remove_all_fds(handler); | |
699 | } | |
700 | handler->fdSets = new FdSets; | |
701 | ++nOldHandlers; | |
702 | if (fdSetsReceived == 0) fdSetsReceived = new FdSets; | |
703 | if (fdSetsToHnds == 0) fdSetsToHnds = new FdSets; | |
704 | } | |
705 | FdSets * fdSets = handler->fdSets; | |
706 | fd_event_type_enum eventOld, eventNew; | |
707 | #ifdef USE_EPOLL | |
708 | // Removing fds which refer to different descriptors than | |
709 | // in the previous call. (closed and other fd created with same id) | |
710 | epoll_event epollEvent; | |
711 | for (int fd = 0; ; ++fd) { | |
712 | fd = fdSets->getIxBothAnySet(read_fds, write_fds, error_fds,fd,fdLimit); | |
713 | if (fd >= fdLimit) break; | |
714 | memset(&epollEvent, 0, sizeof(epollEvent)); | |
715 | epollEvent.data.fd = fd; | |
716 | // Check (inverse) if fd is still in the epoll set | |
717 | if (epoll_ctl(FdMap::epollFd, EPOLL_CTL_ADD, fd, &epollEvent) >= 0 ) { | |
718 | // fd was not in the epoll set as fd was closed and a new | |
719 | // descriptor was created with the same id. | |
720 | eventOld = fdSets->getEvent(fd); | |
721 | Fd_And_Timeout_User::remove_fd(fd, handler, eventOld); | |
722 | } else { | |
723 | errno = 0; // fd is already in the epoll set - it is not an error | |
724 | } | |
725 | } | |
726 | #endif | |
727 | fd_event_type_enum event; | |
728 | for (int fd = 0; ; ++fd) { | |
729 | fd = fdSets->getIxDiff(read_fds, write_fds, error_fds, fd, fdLimit); | |
730 | if (fd >= fdLimit) break; | |
731 | eventOld = fdSets->getEvent(fd); | |
732 | eventNew = FdSets::getEvent(read_fds, write_fds, error_fds, fd); | |
733 | event = static_cast<fd_event_type_enum>(eventNew & ~eventOld); | |
734 | if (event != 0) Fd_And_Timeout_User::add_fd(fd, handler, event); | |
735 | event = static_cast<fd_event_type_enum>(eventOld & ~eventNew); | |
736 | if (event != 0) Fd_And_Timeout_User::remove_fd(fd, handler, event); | |
737 | } | |
738 | } | |
739 | ||
740 | void Fd_And_Timeout_User::remove_all_fds(Fd_And_Timeout_Event_Handler * handler) | |
741 | { | |
742 | if (handler->fdSets != 0 && | |
743 | #ifdef USE_EPOLL | |
744 | !FdMap::isItems1Used() | |
745 | #else | |
746 | (unsigned) FdMap::getSize() > FD_SETSIZE / sizeof(long) | |
747 | #endif | |
748 | ) { | |
749 | // FdSets is used to enumerate and remove all file descriptor of | |
750 | // the specified handler | |
751 | FdSets * fdSets = handler->fdSets; | |
752 | for (int fd = 0; handler->fdCount != 0; ++fd) { | |
753 | fd = fdSets->getIxSet(fd, FD_SETSIZE); | |
754 | if (fd >= (int)FD_SETSIZE) | |
755 | TTCN_error("Fd_And_Timeout_User::remove_all_fds Internal " | |
756 | "error 1: fdCount: %i", handler->fdCount);//debug | |
757 | Fd_And_Timeout_User::remove_fd(fd, handler, fdSets->getEvent(fd)); | |
758 | } | |
759 | } else { | |
760 | // FdMap is used to enumerate and remove all file descriptor of | |
761 | // the specified handler | |
762 | Fd_Event_Handler * hnd = 0; | |
763 | fd_event_type_enum event = static_cast<fd_event_type_enum>(0); | |
764 | int i; | |
765 | int fd = -1; | |
766 | #ifdef USE_EPOLL | |
767 | int fdLimit = FdMap::getFdLimit(); | |
768 | while (handler->fdCount != 0 && !FdMap::isItems1Used()) { | |
769 | do { | |
770 | if (++fd >= fdLimit) | |
771 | TTCN_error("Fd_And_Timeout_User::remove_all_fds Internal " | |
772 | "error 2: fdCount: %i", handler->fdCount);//debug | |
773 | event = FdMap::item2atFd(fd, &hnd); | |
774 | } while (event == 0 || hnd != handler); | |
775 | Fd_And_Timeout_User::remove_fd(fd, handler, event); | |
776 | } | |
777 | #else | |
778 | i = -1; | |
779 | while (handler->fdCount != 0 && !FdMap::isItems1Used()) { | |
780 | pollfd * pollFds = FdMap::getPollFds(); | |
781 | do { | |
782 | ++i; | |
783 | if (i >= FdMap::getSize()) | |
784 | TTCN_error("Fd_And_Timeout_User::remove_all_fds Internal " | |
785 | "error 3: fdCount: %i", handler->fdCount);//debug | |
786 | fd = pollFds[i].fd; | |
787 | event = FdMap::item2atFd(fd, &hnd); | |
788 | } while (event == 0 || hnd != handler); | |
789 | Fd_And_Timeout_User::remove_fd(fd, handler, event); | |
790 | --i; // recheck pollfd item at index i | |
791 | } | |
792 | #endif | |
793 | i = -1; | |
794 | while (handler->fdCount != 0) { | |
795 | do { | |
796 | if (++i >= FdMap::getSize()) | |
797 | TTCN_error("Fd_And_Timeout_User::remove_all_fds Internal " | |
798 | "error 4: fdCount: %i", handler->fdCount);//debug | |
799 | event = FdMap::item1atIndex(i, &fd, &hnd); | |
800 | } while (event == 0 || hnd != handler); | |
801 | Fd_And_Timeout_User::remove_fd(fd, handler, event); | |
802 | --i; // recheck item at index i | |
803 | } | |
804 | } | |
805 | if (handler->fdSets != 0) { | |
806 | delete handler->fdSets; handler->fdSets = 0; | |
807 | --nOldHandlers; | |
808 | if (nOldHandlers == 0) { | |
809 | delete fdSetsReceived; fdSetsReceived = 0; | |
810 | delete fdSetsToHnds; fdSetsToHnds = 0; | |
811 | } | |
812 | } | |
813 | } | |
814 | ||
815 | bool Fd_And_Timeout_User::getTimeout(double * timeout) | |
816 | { | |
817 | timedList.first(); | |
818 | if (timedList.finished()) return false; | |
819 | ||
820 | Fd_And_Timeout_Event_Handler * handler = timedList.current(); | |
821 | double earliestTimeout = handler->last_called + handler->callInterval; | |
822 | timedList.next(); | |
823 | ||
824 | while (!timedList.finished()) { | |
825 | handler = timedList.current(); | |
826 | timedList.next(); | |
827 | double nextCall = handler->last_called + handler->callInterval; | |
828 | if (nextCall < earliestTimeout) earliestTimeout = nextCall; | |
829 | } | |
830 | *timeout = earliestTimeout; | |
831 | return true; | |
832 | } | |
833 | ||
834 | void Fd_And_Timeout_User::call_handlers(int nEvents) | |
835 | { | |
836 | try { // To keep consistency in case of exceptions | |
837 | is_in_call_handlers = true; | |
838 | if (nOldHandlers != 0) { fdSetsReceived->clear(); } | |
839 | if (nEvents > 0) { | |
840 | // Note: FdMap may be modified during event handler calls | |
841 | #ifdef USE_EPOLL | |
842 | FdMap::epollMarkFds(nEvents); | |
843 | int ixLimit = nEvents; | |
844 | #else | |
845 | FdMap::pollFreeze(); | |
846 | int ixLimit = FdMap::getSize(); | |
847 | // Below this index pollfd array items are not removed | |
848 | // If an item should have been removed, then the events field is 0 | |
849 | #endif | |
850 | try { // To keep consistency in case of exceptions | |
851 | for (int ix = 0; ix != ixLimit; ++ix) { | |
852 | #ifdef USE_EPOLL | |
853 | int fd = FdMap::epollEvents[ix].data.fd; | |
854 | fd_event_type_enum event = | |
855 | FdMap::epollEventToEvent(FdMap::epollEvents[ix].events); | |
856 | #else | |
857 | pollfd * pollFd = &(FdMap::getPollFds()[ix]); | |
858 | if ((pollFd->revents & FdMap::pollEventMask) == 0) continue; | |
859 | int fd = pollFd->fd; | |
860 | fd_event_type_enum event = FdMap::pollEventToEvent(pollFd->revents); | |
861 | // The event handler may need pollFd.revents or epoll .events | |
862 | #endif | |
863 | Fd_Event_Handler * handler = 0; | |
864 | fd_event_type_enum wEvent = FdMap::find(fd, &handler); | |
865 | if (wEvent != 0) { | |
866 | event = static_cast<fd_event_type_enum>( | |
867 | event & (wEvent | FD_EVENT_ERR)); | |
868 | if (event != 0) { | |
869 | curRcvdEvtIx = ix; // see getCurReceivedEvent() | |
870 | Fd_And_Timeout_Event_Handler * tmHnd = | |
871 | dynamic_cast<Fd_And_Timeout_Event_Handler*>(handler); | |
872 | if (tmHnd != 0 && tmHnd->getIsOldApi()) { | |
873 | fdSetsReceived->add(fd, event); | |
874 | if (tmHnd->list == 0) oldApiCallList.add(tmHnd); | |
875 | } else | |
876 | handler->Handle_Fd_Event(fd, (event & FD_EVENT_RD) != 0, | |
877 | (event & FD_EVENT_WR) != 0, | |
878 | (event & FD_EVENT_ERR) != 0); | |
879 | if (tmHnd != 0 && tmHnd->list == &timedList) | |
880 | tmHnd->hasEvent = TRUE; | |
881 | } | |
882 | } | |
883 | #ifndef USE_EPOLL | |
884 | pollFd->revents = 0; | |
885 | #endif | |
886 | } | |
887 | } catch(...) { | |
888 | #ifdef USE_EPOLL | |
889 | FdMap::epollUnmarkFds(nEvents); | |
890 | #else | |
891 | FdMap::pollUnfreeze(); | |
892 | #endif | |
893 | throw; | |
894 | } | |
895 | #ifdef USE_EPOLL | |
896 | FdMap::epollUnmarkFds(nEvents); | |
897 | #else | |
898 | FdMap::pollUnfreeze(); | |
899 | #endif | |
900 | // Call handlers with old API without timer | |
901 | for (oldApiCallList.first(); !oldApiCallList.finished(); ) { | |
902 | Fd_And_Timeout_Event_Handler * handler = oldApiCallList.current(); | |
903 | oldApiCallList.next(); | |
904 | oldApiCallList.remove(handler); | |
905 | // Check if the event handler was uninstalled in the meanwhile | |
906 | // (Currently the check is superfluous as an other event handler | |
907 | // may not uninstall this event handler.) | |
908 | if (handler->fdSets == 0) continue; | |
909 | // Get the common set of received and waited events | |
910 | // Check if the set contains any element | |
911 | if ( fdSetsToHnds->setAnd(*fdSetsReceived, *handler->fdSets) ) { | |
912 | double current_time = TTCN_Snapshot::time_now(); | |
913 | double time_since_last_call = current_time - | |
914 | handler->last_called; | |
915 | handler->last_called = current_time; | |
916 | handler->Event_Handler(fdSetsToHnds->getReadFds(), | |
917 | fdSetsToHnds->getWriteFds(), fdSetsToHnds->getErrorFds(), | |
918 | time_since_last_call); | |
919 | } | |
920 | } | |
921 | } | |
922 | // Call timeout handlers (also handlers with old API with timer) | |
923 | double current_time = TTCN_Snapshot::time_now(); | |
924 | for (timedList.first(); !timedList.finished(); ) { | |
925 | Fd_And_Timeout_Event_Handler * handler = timedList.current(); | |
926 | timedList.next(); // The handler may be removed from the list | |
927 | if (handler->getIsOldApi()) | |
928 | handler->hasEvent = | |
929 | fdSetsToHnds->setAnd(*fdSetsReceived, *handler->fdSets); | |
930 | bool callHandler = (handler->hasEvent && handler->isTimeout) ? | |
931 | handler->callAnyway : | |
932 | current_time > (handler->last_called + handler->callInterval); | |
933 | if ( !handler->isPeriodic && | |
934 | (callHandler || (handler->hasEvent && handler->isTimeout)) ) { | |
935 | handler->callInterval = 0.0; | |
936 | timedList.remove(handler); | |
937 | } | |
938 | handler->hasEvent = FALSE; | |
939 | if (callHandler) { | |
940 | double time_since_last_call = current_time - handler->last_called; | |
941 | handler->last_called = current_time; | |
942 | if (!handler->getIsOldApi()) | |
943 | handler->Handle_Timeout(time_since_last_call); | |
944 | else if (handler->fdSets != 0) | |
945 | handler->Event_Handler(fdSetsToHnds->getReadFds(), | |
946 | fdSetsToHnds->getWriteFds(), fdSetsToHnds->getErrorFds(), | |
947 | time_since_last_call); | |
948 | current_time = TTCN_Snapshot::time_now(); | |
949 | } | |
950 | } | |
951 | is_in_call_handlers = false; | |
952 | } catch (...) { oldApiCallList.clear(); is_in_call_handlers = false; throw; } | |
953 | } | |
954 | ||
955 | int Fd_And_Timeout_User::receiveEvents(int pollTimeout) | |
956 | { | |
957 | int ret_val; | |
958 | #ifdef USE_EPOLL | |
959 | ret_val = epoll_wait(FdMap::epollFd, | |
960 | FdMap::epollEvents, FdMap::MAX_EPOLL_EVENTS, pollTimeout); | |
961 | if (ret_val < 0 && errno != EINTR) | |
962 | TTCN_error("System call epoll_wait() failed when taking a new snapshot."); | |
963 | #else | |
964 | ret_val = poll(FdMap::getPollFds(), FdMap::getSize(), pollTimeout); | |
965 | if (ret_val < 0 && errno != EINTR) | |
966 | TTCN_error("System call poll() failed when taking a new snapshot."); | |
967 | #endif | |
968 | return ret_val; | |
969 | } | |
970 | ||
971 | #ifdef USE_EPOLL | |
972 | void Fd_And_Timeout_User::reopenEpollFd() | |
973 | { | |
974 | if (FdMap::epollFd != -1) { close (FdMap::epollFd); FdMap::epollFd = -1; } | |
975 | FdMap::epollFd = epoll_create(16 /* epoll size hint */); | |
976 | // FIXME: method to determine the optimal epoll size hint | |
977 | // epoll size hint is ignored in newer kernels | |
978 | // for older kernels: it should be big enough not to be too slow | |
979 | // and it should not be very big to limit memory usage | |
980 | if (FdMap::epollFd < 0) | |
981 | TTCN_error("System call epoll_create() failed in child process."); | |
982 | ||
983 | if (FdMap::getSize() != 1) | |
984 | TTCN_error("Fd_And_Timeout_User::reopenEpollFd: Internal error"); | |
985 | } | |
986 | #endif | |
987 | ||
988 | ||
989 | ||
990 | ||
991 | ||
992 | /****************************************************************************** | |
993 | * class TTCN_Snapshot * | |
994 | ******************************************************************************/ | |
995 | ||
996 | boolean TTCN_Snapshot::else_branch_found; | |
997 | double TTCN_Snapshot::alt_begin; | |
998 | ||
999 | void TTCN_Snapshot::initialize() | |
1000 | { | |
1001 | long openMax = sysconf(_SC_OPEN_MAX); | |
1002 | int fdLimit = (openMax <= (long) MAX_INT_VAL) ? (int) openMax : MAX_INT_VAL; | |
1003 | ||
1004 | FdMap::initialize(fdLimit); | |
1005 | Fd_And_Timeout_User::initialize(); | |
1006 | #ifdef USE_EPOLL | |
1007 | FdMap::epollFd = epoll_create(16 /* epoll size hint */); | |
1008 | // FIXME: method to determine the optimal epoll size hint | |
1009 | // epoll size hint is ignored in newer kernels | |
1010 | // for older kernels: it should be big enough not to be too slow | |
1011 | // and it should not be very big to limit memory usage | |
1012 | if (FdMap::epollFd < 0) | |
1013 | TTCN_error("TTCN_Snapshot::initialize: " | |
1014 | "System call epoll_create() failed."); | |
1015 | #endif | |
1016 | else_branch_found = FALSE; | |
1017 | alt_begin = time_now(); | |
1018 | } | |
1019 | ||
1020 | void TTCN_Snapshot::check_fd_setsize() | |
1021 | { | |
1022 | if ((long) FdMap::getFdLimit() > (long) FD_SETSIZE) | |
1023 | TTCN_Logger::log_fd_limits(FdMap::getFdLimit(), (long) FD_SETSIZE); | |
1024 | } | |
1025 | ||
1026 | void TTCN_Snapshot::terminate() | |
1027 | { | |
1028 | #ifdef USE_EPOLL | |
1029 | if (FdMap::epollFd != -1) { close (FdMap::epollFd); FdMap::epollFd = -1; } | |
1030 | #endif | |
1031 | Fd_And_Timeout_User::terminate(); | |
1032 | FdMap::terminate(); | |
1033 | } | |
1034 | ||
1035 | void TTCN_Snapshot::else_branch_reached() | |
1036 | { | |
1037 | if (!else_branch_found) { | |
1038 | else_branch_found = TRUE; | |
1039 | TTCN_warning("An [else] branch of an alt construct has been reached. " | |
1040 | "Re-configuring the snapshot manager to call the event handlers " | |
1041 | "even when taking the first snapshot."); | |
1042 | } | |
1043 | } | |
1044 | ||
1045 | double TTCN_Snapshot::time_now() | |
1046 | { | |
1047 | static time_t start_time; | |
1048 | static boolean first_call = TRUE; | |
1049 | struct timeval tv; | |
1050 | if (gettimeofday(&tv, NULL) == -1) | |
1051 | TTCN_error("gettimeofday() system call failed."); | |
1052 | if (first_call) { | |
1053 | start_time = tv.tv_sec; | |
1054 | first_call = FALSE; | |
1055 | } | |
1056 | return (double)(tv.tv_sec - start_time) + 1e-6 * (double)tv.tv_usec; | |
1057 | } | |
1058 | ||
1059 | ||
1060 | void TTCN_Snapshot::take_new(boolean block_execution) | |
1061 | { | |
1062 | if (block_execution || else_branch_found) { | |
1063 | ||
1064 | // jump here if epoll()/poll()/select() was interrupted by a signal | |
1065 | again: | |
1066 | // determine the timeout value for epoll()/poll()/select() | |
1067 | double timeout = 0.0; | |
1068 | int pollTimeout = 0; // timeout for poll/epoll | |
1069 | bool handleTimer = false; | |
1070 | if (block_execution) { | |
1071 | // find the earliest timeout | |
1072 | double timer_timeout, handler_timeout = 0.0; | |
1073 | boolean is_timer_timeout = TIMER::get_min_expiration(timer_timeout); | |
1074 | bool is_handler_timeout = | |
1075 | Fd_And_Timeout_User::getTimeout(&handler_timeout); | |
1076 | if (is_timer_timeout) { | |
1077 | if (is_handler_timeout && handler_timeout < timer_timeout) | |
1078 | timeout = handler_timeout; | |
1079 | else timeout = timer_timeout; | |
1080 | } else if (is_handler_timeout) timeout = handler_timeout; | |
1081 | if (is_timer_timeout || is_handler_timeout) { | |
1082 | // there are active TTCN-3 or test port timers | |
1083 | double current_time = time_now(); | |
1084 | double block_time = timeout - current_time; | |
1085 | if (block_time > 0.0) { | |
1086 | // the first timeout is in the future: blocking is needed | |
1087 | // filling up tv with appropriate values | |
1088 | if (block_time < (double)MAX_BLOCK_TIME) { | |
1089 | pollTimeout = static_cast<int>(floor(block_time*1000)); | |
1090 | handleTimer = true; | |
1091 | } else { | |
1092 | // issue a warning: the user probably does not want such | |
1093 | // long waiting | |
1094 | TTCN_warning("The time needed for the first timer " | |
1095 | "expiry is %g seconds. The operating system does " | |
1096 | "not support such long waiting at once. The " | |
1097 | "maximum time of blocking was set to %d seconds " | |
1098 | "(ca. %d days).", block_time, MAX_BLOCK_TIME, | |
1099 | MAX_BLOCK_TIME / 86400); | |
1100 | // also modify the the timeout value to get out | |
1101 | // immediately from the while() loop below | |
1102 | timeout = current_time + (double)MAX_BLOCK_TIME; | |
1103 | pollTimeout = MAX_BLOCK_TIME * 1000; | |
1104 | handleTimer = true; | |
1105 | } | |
1106 | } else { | |
1107 | // first timer is already expired: do not block | |
1108 | // pollTimeout is 0 | |
1109 | handleTimer = true; | |
1110 | } | |
1111 | } else { | |
1112 | // no active timers: infinite timeout | |
1113 | pollTimeout = -1; | |
1114 | } | |
1115 | } else { | |
1116 | // blocking was not requested (we are in a first snapshot after an | |
1117 | // [else] branch): do not block - pollTimeout is 0 | |
1118 | } | |
1119 | ||
1120 | if (FdMap::getSize() == 0 && pollTimeout < 0) | |
1121 | TTCN_error("There are no active timers and no installed event " | |
1122 | "handlers. Execution would block forever."); | |
1123 | ||
1124 | int ret_val = 0; | |
1125 | if (FdMap::getSize() != 0) { | |
1126 | ret_val = Fd_And_Timeout_User::receiveEvents(pollTimeout); | |
1127 | // call epoll_wait() / poll() | |
1128 | if (ret_val < 0) { /* EINTR - signal */ errno = 0; goto again; } | |
1129 | } else { | |
1130 | // There isn't any file descriptor to check | |
1131 | if (pollTimeout > 0) { | |
1132 | // waiting only for a timeout in the future | |
1133 | timeval tv; | |
1134 | tv.tv_sec = pollTimeout / 1000; | |
1135 | tv.tv_usec = (pollTimeout % 1000) * 1000; | |
1136 | ret_val = select(0, NULL, NULL, NULL, &tv); | |
1137 | if (ret_val < 0 && errno == EINTR) { | |
1138 | /* signal */ errno = 0; goto again; | |
1139 | } | |
1140 | if ((ret_val < 0 && errno != EINTR) || ret_val > 0) | |
1141 | TTCN_error("System call select() failed when taking a new " | |
1142 | "snapshot."); | |
1143 | } else if (pollTimeout < 0) | |
1144 | TTCN_error("There are no active timers or installed event " | |
1145 | "handlers. Execution would block forever."); | |
1146 | } | |
1147 | ||
1148 | if (ret_val > 0) { | |
1149 | Fd_And_Timeout_User::call_handlers(ret_val); | |
1150 | } else if (ret_val == 0 && handleTimer) { | |
1151 | // if select() returned because of the timeout, but too early | |
1152 | // do an other round if it has to wait much, | |
1153 | // or do a busy wait if only a few cycles are needed | |
1154 | if (pollTimeout > 0){ | |
1155 | double difference = time_now() - timeout; | |
1156 | if(difference < 0.0){ | |
1157 | if(difference < -0.001){ | |
1158 | errno = 0; | |
1159 | goto again; | |
1160 | } else { | |
1161 | while (time_now() < timeout) ; | |
1162 | } | |
1163 | } | |
1164 | } | |
1165 | Fd_And_Timeout_User::call_handlers(0); // Call timeout handlers | |
1166 | } | |
1167 | } | |
1168 | // just update the time and check the testcase guard timer if blocking was | |
1169 | // not requested and there is no [else] branch in the test suite | |
1170 | ||
1171 | alt_begin = time_now(); | |
1172 | ||
1173 | if (testcase_timer.timeout() == ALT_YES) | |
1174 | TTCN_error("Guard timer has expired. Execution of current test case " | |
1175 | "will be interrupted."); | |
1176 | } | |
1177 | ||
1178 | void TTCN_Snapshot::block_for_sending(int send_fd, Fd_Event_Handler * handler) | |
1179 | { | |
1180 | // To be backward compatible: handler is optional | |
1181 | if (Fd_And_Timeout_User::get_is_in_call_handlers()) | |
1182 | TTCN_error("TTCN_Snapshot::block_for_sending: The function may not be " | |
1183 | "called from event handler"); | |
1184 | Fd_Event_Handler * hnd = 0; | |
1185 | if ((FdMap::find(send_fd, &hnd) & FD_EVENT_WR) != 0) | |
1186 | TTCN_error("TTCN_Snapshot::block_for_sending: An event handler already " | |
1187 | "waits for file descriptor %d to be writable", send_fd); | |
1188 | if (handler != 0 && hnd != 0 && handler != hnd) | |
1189 | TTCN_error("TTCN_Snapshot::block_for_sending: File descriptor %d " | |
1190 | "already has a handler, which is different from the currently " | |
1191 | "specified.", send_fd); | |
1192 | static Fd_And_Timeout_Event_Handler dummyHandler; | |
1193 | if (hnd == 0) { hnd = (handler != 0) ? handler : &dummyHandler; } | |
1194 | Fd_And_Timeout_User::add_fd(send_fd, hnd, FD_EVENT_WR); | |
1195 | for ( ; ; ) { | |
1196 | int ret_val = Fd_And_Timeout_User::receiveEvents(-1); // epoll / poll | |
1197 | if (ret_val >= 0) { | |
1198 | bool writable = false; | |
1199 | bool readable = false; | |
1200 | #ifdef USE_EPOLL | |
1201 | ||
1202 | for (int i = 0; i < ret_val; ++i) { | |
1203 | if (FdMap::epollEvents[i].data.fd == send_fd) { | |
1204 | readable = true; | |
1205 | if ((FdMap::epollEvents[i].events & EPOLLOUT) != 0){ | |
1206 | writable = true; | |
1207 | } | |
1208 | break; | |
1209 | } | |
1210 | } | |
1211 | #else | |
1212 | if (FdMap::getPollREvent(send_fd) != 0) {readable = true;} | |
1213 | if ((FdMap::getPollREvent(send_fd) & FD_EVENT_WR) != 0) {writable = true;} | |
1214 | #endif | |
1215 | if (writable) break; | |
1216 | Fd_And_Timeout_User::call_handlers(ret_val); | |
1217 | if (readable) break; | |
1218 | } | |
1219 | // interrupted - EINTR | |
1220 | } | |
1221 | Fd_And_Timeout_User::remove_fd(send_fd, hnd, FD_EVENT_WR); | |
1222 | #ifndef USE_EPOLL | |
1223 | // As Fd_And_Timeout_User::call_handlers is not called: | |
1224 | // received events should be cleared at this point | |
1225 | // (Most probably the behavior would be correct without clearing it.) | |
1226 | int nPollFds = FdMap::getSize(); | |
1227 | pollfd * pollFds = FdMap::getPollFds(); | |
1228 | for (int i = 0; i < nPollFds; ++i) pollFds[i].revents = 0; | |
1229 | #endif | |
1230 | } |