1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) 2000-2015 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 ///////////////////////////////////////////////////////////////////////////////
17 #include <sys/select.h>
18 #include <sys/types.h>
21 #include <sys/epoll.h>
26 #include "Snapshot.hh"
27 #include "Fd_And_Timeout_User.hh"
31 #include "Event_Handler.hh"
34 static const int MAX_INT_VAL
= (int) ((unsigned int) -2 >> 1u);
36 /******************************************************************************
38 ******************************************************************************/
42 FdMap::Item
FdMap::items1
[ITEM1_CAPACITY
];
43 FdMap::Data
* FdMap::items2
;
45 pollfd
FdMap::pollFds1
[ITEM1_CAPACITY
];
46 pollfd
* FdMap::pollFds2
;
47 bool FdMap::needUpdate
;
48 int FdMap::nPollFdsFrozen
;
52 epoll_event
FdMap::epollEvents
[MAX_EPOLL_EVENTS
];
55 fd_event_type_enum
FdMap::add(int fd
, Fd_Event_Handler
* handler
,
56 fd_event_type_enum event
)
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
);
64 TTCN_Logger::log_event("\".");
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
);
71 TTCN_Logger::log_event("\".");
75 short pollEvent
= eventToPollEvent(event
);
78 int i
= findInsPointInItems1(fd
);
79 if (i
< nItems
&& fd
== items1
[i
].fd
) {
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
);
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("\".");
91 fd_event_type_enum oldEvent
;
93 oldEvent
= static_cast<fd_event_type_enum
>(items1
[i
].d
.evt
);
94 items1
[i
].d
.evt
|= static_cast<short>(event
);
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
;
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
];
107 items1
[i
].d
.evt
= static_cast<short>(event
);
108 items1
[i
].d
.ixE
= -1;
110 pollFds1
[nItems
].fd
= fd
;
111 pollFds1
[nItems
].events
= pollEvent
;
112 pollFds1
[nItems
].revents
= 0;
113 items1
[i
].d
.ixPoll
= nItems
;
115 items1
[i
].d
.hnd
= handler
;
117 return static_cast<fd_event_type_enum
>(0);
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
126 pollFds2
= new pollfd
[capacity
];
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
136 if (findInItems2(fd
)) {
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
);
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("\".");
148 fd_event_type_enum oldEvent
;
150 oldEvent
= static_cast<fd_event_type_enum
>(items2
[fd
].evt
);
151 items2
[fd
].evt
|= static_cast<short>(event
);
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
;
163 items2
[fd
].evt
= static_cast<short>(event
);
166 pollFds2
[nItems
].fd
= fd
;
167 pollFds2
[nItems
].events
= pollEvent
;
168 pollFds2
[nItems
].revents
= 0;
169 items2
[fd
].ixPoll
= nItems
;
171 items2
[fd
].hnd
= handler
;
173 return static_cast<fd_event_type_enum
>(0);
176 fd_event_type_enum
FdMap::remove(int fd
, const Fd_Event_Handler
* handler
,
177 fd_event_type_enum event
)
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("\".");
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("\".");
196 fd_event_type_enum oldEvent
;
198 short pollEvent
= eventToPollEvent(event
);
201 int i
= findInItems1(fd
);
203 TTCN_warning_begin("Trying to remove file descriptor (%d) "
204 "events (%d) from the set of events handled by \"",
206 if (handler
!= 0) handler
->log();
207 TTCN_Logger::log_event("\", but events of the file descriptor "
208 "do not have a handler.");
210 // Ignore errors for HP53582.
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("\".");
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) {
229 while (i
< nItems
) { items1
[i
] = items1
[i
+ 1]; ++i
; }
230 items1
[nItems
].init(); // not necessary - for debugging
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
;
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.
254 if (!findInItems2(fd
)) {
255 TTCN_error_begin("Trying to remove file descriptor (%d) "
256 "events (%d) from the set of events handled by \"",
258 if (handler
!= 0) handler
->log();
259 TTCN_Logger::log_event("\", but events of the file descriptor "
260 "do not have a handler.");
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("\".");
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) {
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
)) {
285 items1
[n
++].d
= items2
[i
];
288 delete[] items2
; items2
= 0;
289 //if (n < nItems) => error
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
) {
300 pollFds2
[i
] = pollFds2
[nItems
];
301 items2
[pollFds2
[i
].fd
].ixPoll
= i
;
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;
311 } else { // The item is frozen; removal is postponed.
321 fd_event_type_enum
FdMap::find(int fd
, Fd_Event_Handler
* * handler
)
325 int i
= findInItems1(fd
);
326 if (i
< 0) { *handler
= 0; return static_cast<fd_event_type_enum
>(0); }
329 if (!findInItems2(fd
)) {
330 *handler
= 0; return static_cast<fd_event_type_enum
>(0);
334 *handler
= data
->hnd
;
336 return static_cast<fd_event_type_enum
>(data
->evt
);
338 return pollEventToEvent(getPollFds()[data
->ixPoll
].events
);
343 void FdMap::copyItems2ToItems1()
345 if (nItems
!= ITEM1_CAPACITY_LOW
)
346 TTCN_error("FdMap::copyItems2ToItems1: Internal error");
347 if (ITEM1_CAPACITY_LOW
== 0) return;
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
;
358 d
[j
].fd
= fd1
; d
[j
].d
= items2
[fd1
];
359 d
[j
+ 1].fd
= fd2
; d
[j
+ 1].d
= items2
[fd2
];
361 d
[j
].fd
= fd2
; d
[j
].d
= items2
[fd2
];
362 d
[j
+ 1].fd
= fd1
; d
[j
+ 1].d
= items2
[fd1
];
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
];
371 for (int j
= 1; j
< i
; ++j
) {
372 Item
* s
= f
? items1
: itemsTmp
;
374 d
= f
? items1
: itemsTmp
;
376 int step2
= step
* 2;
378 for (int k
= 0; k
< n
; k
+= step2
) {
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
;
387 d
[w
++] = s
[v
++]; --vN
;
390 while (uN
!= 0) { d
[w
++] = s
[u
++]; --uN
; }
391 while (vN
> 0) { d
[w
++] = s
[v
++]; --vN
; }
398 bool FdMap::epollMarkFds(int nEvents
)
400 bool all_valid
= true;
401 for (int i
= 0; i
< nEvents
; ++i
) {
402 int fd
= epollEvents
[i
].data
.fd
;
404 int j
= findInItems1(fd
);
405 if (j
>= 0) items1
[j
].d
.ixE
= i
;
406 else all_valid
= false;
408 if (findInItems2(fd
)) items2
[fd
].ixE
= i
;
409 else all_valid
= false;
415 void FdMap::epollUnmarkFds(int nEvents
)
417 for (int i
= 0; i
< nEvents
; ++i
) {
418 int fd
= epollEvents
[i
].data
.fd
;
420 int j
= findInItems1(fd
);
421 if (j
>= 0) items1
[j
].d
.ixE
= -1;
423 if (findInItems2(fd
)) items2
[fd
].ixE
= -1;
428 void FdMap::pollFreeze()
430 nPollFdsFrozen
= nItems
;
433 void FdMap::pollUnfreeze()
435 if (!needUpdate
) { nPollFdsFrozen
= 0; return; }
436 int i
= 0, j
= nPollFdsFrozen
;
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
450 fd_event_type_enum
FdMap::getPollREvent(int fd
)
453 int i
= findInItems1(fd
);
455 return pollEventToEvent(pollFds1
[items1
[i
].d
.ixPoll
].revents
);
457 if (findInItems2(fd
))
458 return pollEventToEvent(pollFds2
[items2
[fd
].ixPoll
].revents
);
460 return static_cast<fd_event_type_enum
>(0);
468 /******************************************************************************
469 * class Fd_Event_Handler *
470 ******************************************************************************/
472 void Fd_Event_Handler::log() const
474 TTCN_Logger::log_event("handler <invalid>");
482 /******************************************************************************
483 * class Fd_And_Timeout_Event_Handler *
484 ******************************************************************************/
486 void Fd_And_Timeout_Event_Handler::Handle_Fd_Event(int,
487 boolean
, boolean
, boolean
)
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
495 void Fd_And_Timeout_Event_Handler::Handle_Timeout(double)
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
503 void Fd_And_Timeout_Event_Handler::Event_Handler(const fd_set
*,
504 const fd_set
*, const fd_set
*, double)
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
512 Fd_And_Timeout_Event_Handler::~Fd_And_Timeout_Event_Handler()
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);
522 void Fd_And_Timeout_Event_Handler::log() const
524 TTCN_Logger::log_event("handler <unknown>");
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)
543 /******************************************************************************
544 * class Fd_And_Timeout_User *
545 ******************************************************************************/
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
;
555 inline void Fd_And_Timeout_User::checkFd(int fd
)
559 fd
== FdMap::epollFd
||
561 fcntl(fd
, F_GETFD
, FD_CLOEXEC
) < 0)
562 TTCN_error("Trying to add events of an invalid file descriptor (%d)", fd
);
565 void Fd_And_Timeout_User::add_fd(int fd
, Fd_Event_Handler
* handler
,
566 fd_event_type_enum event
)
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
);
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",
577 tmHnd
->fdSets
->add(fd
, event
);
579 if (oldEvent
== 0) ++tmHnd
->fdCount
;
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 ) {
590 TTCN_error("Fd_And_Timeout_User::add_fd: System call epoll_ctl failed "
591 "when adding fd: %d, errno: %d", fd
, errno
);
598 void Fd_And_Timeout_User::remove_fd(int fd
, Fd_Event_Handler
* handler
,
599 fd_event_type_enum event
)
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
);
611 if (newEvent
== 0) --tmHnd
->fdCount
;
612 if (tmHnd
->getIsOldApi()) {
613 fdSetsReceived
->remove(fd
, event
);
614 tmHnd
->fdSets
->remove(fd
, event
);
618 epoll_event epollEvent
;
619 memset(&epollEvent
, 0, sizeof(epollEvent
));
620 epollEvent
.data
.fd
= fd
;
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) {
627 TTCN_error("System call epoll_ctl failed when deleting fd: %d, "
628 "errno: %d", fd
, errno
);
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
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
);
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
)
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
;
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
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
668 handler
->callInterval
= 0.0;
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
)
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
);
686 handler
->fdSets
= new FdSets
;
688 if (fdSetsReceived
== 0) fdSetsReceived
= new FdSets
;
689 if (fdSetsToHnds
== 0) fdSetsToHnds
= new FdSets
;
691 FdSets
* fdSets
= handler
->fdSets
;
692 fd_event_type_enum eventOld
, eventNew
;
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
);
709 errno
= 0; // fd is already in the epoll set - it is not an error
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
);
726 void Fd_And_Timeout_User::remove_all_fds(Fd_And_Timeout_Event_Handler
* handler
)
728 if (handler
->fdSets
!= 0 &&
730 !FdMap::isItems1Used()
732 (unsigned) FdMap::getSize() > FD_SETSIZE
/ sizeof(long)
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
));
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);
753 int fdLimit
= FdMap::getFdLimit();
754 while (handler
->fdCount
!= 0 && !FdMap::isItems1Used()) {
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
);
765 while (handler
->fdCount
!= 0 && !FdMap::isItems1Used()) {
766 pollfd
* pollFds
= FdMap::getPollFds();
769 if (i
>= FdMap::getSize())
770 TTCN_error("Fd_And_Timeout_User::remove_all_fds Internal "
771 "error 3: fdCount: %i", handler
->fdCount
);//debug
773 event
= FdMap::item2atFd(fd
, &hnd
);
774 } while (event
== 0 || hnd
!= handler
);
775 Fd_And_Timeout_User::remove_fd(fd
, handler
, event
);
776 --i
; // recheck pollfd item at index i
780 while (handler
->fdCount
!= 0) {
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
791 if (handler
->fdSets
!= 0) {
792 delete handler
->fdSets
; handler
->fdSets
= 0;
794 if (nOldHandlers
== 0) {
795 delete fdSetsReceived
; fdSetsReceived
= 0;
796 delete fdSetsToHnds
; fdSetsToHnds
= 0;
801 bool Fd_And_Timeout_User::getTimeout(double * timeout
)
804 if (timedList
.finished()) return false;
806 Fd_And_Timeout_Event_Handler
* handler
= timedList
.current();
807 double earliestTimeout
= handler
->last_called
+ handler
->callInterval
;
810 while (!timedList
.finished()) {
811 handler
= timedList
.current();
813 double nextCall
= handler
->last_called
+ handler
->callInterval
;
814 if (nextCall
< earliestTimeout
) earliestTimeout
= nextCall
;
816 *timeout
= earliestTimeout
;
820 void Fd_And_Timeout_User::call_handlers(int nEvents
)
822 try { // To keep consistency in case of exceptions
823 is_in_call_handlers
= true;
824 if (nOldHandlers
!= 0) { fdSetsReceived
->clear(); }
826 // Note: FdMap may be modified during event handler calls
828 FdMap::epollMarkFds(nEvents
);
829 int ixLimit
= nEvents
;
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
836 try { // To keep consistency in case of exceptions
837 for (int ix
= 0; ix
!= ixLimit
; ++ix
) {
839 int fd
= FdMap::epollEvents
[ix
].data
.fd
;
840 fd_event_type_enum event
=
841 FdMap::epollEventToEvent(FdMap::epollEvents
[ix
].events
);
843 pollfd
* pollFd
= &(FdMap::getPollFds()[ix
]);
844 if ((pollFd
->revents
& FdMap::pollEventMask
) == 0) continue;
846 fd_event_type_enum event
= FdMap::pollEventToEvent(pollFd
->revents
);
847 // The event handler may need pollFd.revents or epoll .events
849 Fd_Event_Handler
* handler
= 0;
850 fd_event_type_enum wEvent
= FdMap::find(fd
, &handler
);
852 event
= static_cast<fd_event_type_enum
>(
853 event
& (wEvent
| FD_EVENT_ERR
));
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
);
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
;
875 FdMap::epollUnmarkFds(nEvents
);
877 FdMap::pollUnfreeze();
882 FdMap::epollUnmarkFds(nEvents
);
884 FdMap::pollUnfreeze();
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
);
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())
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
);
924 handler
->hasEvent
= FALSE
;
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();
937 is_in_call_handlers
= false;
938 } catch (...) { oldApiCallList
.clear(); is_in_call_handlers
= false; throw; }
941 int Fd_And_Timeout_User::receiveEvents(int pollTimeout
)
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.");
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.");
958 void Fd_And_Timeout_User::reopenEpollFd()
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.");
969 if (FdMap::getSize() != 1)
970 TTCN_error("Fd_And_Timeout_User::reopenEpollFd: Internal error");
978 /******************************************************************************
979 * class TTCN_Snapshot *
980 ******************************************************************************/
982 boolean
TTCN_Snapshot::else_branch_found
;
983 double TTCN_Snapshot::alt_begin
;
985 void TTCN_Snapshot::initialize()
987 long openMax
= sysconf(_SC_OPEN_MAX
);
988 int fdLimit
= (openMax
<= (long) MAX_INT_VAL
) ? (int) openMax
: MAX_INT_VAL
;
990 FdMap::initialize(fdLimit
);
991 Fd_And_Timeout_User::initialize();
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.");
1002 else_branch_found
= FALSE
;
1003 alt_begin
= time_now();
1006 void TTCN_Snapshot::check_fd_setsize()
1008 if ((long) FdMap::getFdLimit() > (long) FD_SETSIZE
)
1009 TTCN_Logger::log_fd_limits(FdMap::getFdLimit(), (long) FD_SETSIZE
);
1012 void TTCN_Snapshot::terminate()
1015 if (FdMap::epollFd
!= -1) { close (FdMap::epollFd
); FdMap::epollFd
= -1; }
1017 Fd_And_Timeout_User::terminate();
1021 void TTCN_Snapshot::else_branch_reached()
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.");
1031 double TTCN_Snapshot::time_now()
1033 static time_t start_time
;
1034 static boolean first_call
= TRUE
;
1036 if (gettimeofday(&tv
, NULL
) == -1)
1037 TTCN_error("gettimeofday() system call failed.");
1039 start_time
= tv
.tv_sec
;
1042 return (double)(tv
.tv_sec
- start_time
) + 1e-6 * (double)tv
.tv_usec
;
1046 void TTCN_Snapshot::take_new(boolean block_execution
)
1048 if (block_execution
|| else_branch_found
) {
1050 // jump here if epoll()/poll()/select() was interrupted by a signal
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));
1078 // issue a warning: the user probably does not want such
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;
1093 // first timer is already expired: do not block
1098 // no active timers: infinite timeout
1102 // blocking was not requested (we are in a first snapshot after an
1103 // [else] branch): do not block - pollTimeout is 0
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.");
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
; }
1116 // There isn't any file descriptor to check
1117 if (pollTimeout
> 0) {
1118 // waiting only for a timeout in the future
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
;
1126 if ((ret_val
< 0 && errno
!= EINTR
) || ret_val
> 0)
1127 TTCN_error("System call select() failed when taking a new "
1129 } else if (pollTimeout
< 0)
1130 TTCN_error("There are no active timers or installed event "
1131 "handlers. Execution would block forever.");
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){
1147 while (time_now() < timeout
) ;
1151 Fd_And_Timeout_User::call_handlers(0); // Call timeout handlers
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
1157 alt_begin
= time_now();
1159 if (testcase_timer
.timeout() == ALT_YES
)
1160 TTCN_error("Guard timer has expired. Execution of current test case "
1161 "will be interrupted.");
1164 void TTCN_Snapshot::block_for_sending(int send_fd
, Fd_Event_Handler
* handler
)
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
);
1182 int ret_val
= Fd_And_Timeout_User::receiveEvents(-1); // epoll / poll
1184 bool writable
= false;
1185 bool readable
= false;
1188 for (int i
= 0; i
< ret_val
; ++i
) {
1189 if (FdMap::epollEvents
[i
].data
.fd
== send_fd
) {
1191 if ((FdMap::epollEvents
[i
].events
& EPOLLOUT
) != 0){
1198 if (FdMap::getPollREvent(send_fd
) != 0) {readable
= true;}
1199 if ((FdMap::getPollREvent(send_fd
) & FD_EVENT_WR
) != 0) {writable
= true;}
1201 if (writable
) break;
1202 Fd_And_Timeout_User::call_handlers(ret_val
);
1203 if (readable
) break;
1205 // interrupted - EINTR
1207 Fd_And_Timeout_User::remove_fd(send_fd
, hnd
, FD_EVENT_WR
);
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;