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