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