1 /******************************************************************************
2 * Copyright (c) 2000-2016 Ericsson Telecom AB
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
17 * Szabo, Janos Zoltan – initial implementation
21 ******************************************************************************/
31 #include <sys/select.h>
32 #include <sys/types.h>
35 #include <sys/epoll.h>
40 #include "Snapshot.hh"
41 #include "Fd_And_Timeout_User.hh"
45 #include "Event_Handler.hh"
48 static const int MAX_INT_VAL
= (int) ((unsigned int) -2 >> 1u);
50 /******************************************************************************
52 ******************************************************************************/
56 FdMap::Item
FdMap::items1
[ITEM1_CAPACITY
];
57 FdMap::Data
* FdMap::items2
;
59 pollfd
FdMap::pollFds1
[ITEM1_CAPACITY
];
60 pollfd
* FdMap::pollFds2
;
61 bool FdMap::needUpdate
;
62 int FdMap::nPollFdsFrozen
;
66 epoll_event
FdMap::epollEvents
[MAX_EPOLL_EVENTS
];
69 fd_event_type_enum
FdMap::add(int fd
, Fd_Event_Handler
* handler
,
70 fd_event_type_enum event
)
73 TTCN_error("FdMap::add: Internal error"); // debug
74 if (fd
< 0 || fd
>= capacity
) {
75 TTCN_error_begin("Trying to add events of an invalid file descriptor "
76 "(%d) to the set of events handled by \"", fd
);
78 TTCN_Logger::log_event("\".");
81 if ((event
& ~(FD_EVENT_RD
| FD_EVENT_WR
| FD_EVENT_ERR
)) != 0) {
82 TTCN_error_begin("Trying to add invalid events (%d) of file descriptor "
83 "(%d) to the set of events handled by \"", event
, fd
);
85 TTCN_Logger::log_event("\".");
89 short pollEvent
= eventToPollEvent(event
);
92 int i
= findInsPointInItems1(fd
);
93 if (i
< nItems
&& fd
== items1
[i
].fd
) {
95 if (items1
[i
].d
.hnd
!= 0 && items1
[i
].d
.hnd
!= handler
) {
96 TTCN_error_begin("Trying to add file descriptor (%d) "
97 "events (%d) to the set of events handled by \"",fd
,event
);
99 TTCN_Logger::log_event("\", but the events of the "
100 "file descriptor already have a different handler: \"");
101 if (items1
[i
].d
.hnd
!= 0) items1
[i
].d
.hnd
->log();
102 TTCN_Logger::log_event("\".");
105 fd_event_type_enum oldEvent
;
107 oldEvent
= static_cast<fd_event_type_enum
>(items1
[i
].d
.evt
);
108 items1
[i
].d
.evt
|= static_cast<short>(event
);
110 items1
[i
].d
.hnd
= handler
; // in case it is a frozen deleted item
111 oldEvent
= pollEventToEvent(pollFds1
[items1
[i
].d
.ixPoll
].events
);
112 pollFds1
[items1
[i
].d
.ixPoll
].events
|= pollEvent
;
116 if (nItems
< ITEM1_CAPACITY
) {
117 // Size of the static array is enough
118 for (int j
= nItems
- 1; j
>= i
; --j
) items1
[j
+ 1] = items1
[j
];
121 items1
[i
].d
.evt
= static_cast<short>(event
);
122 items1
[i
].d
.ixE
= -1;
124 pollFds1
[nItems
].fd
= fd
;
125 pollFds1
[nItems
].events
= pollEvent
;
126 pollFds1
[nItems
].revents
= 0;
127 items1
[i
].d
.ixPoll
= nItems
;
129 items1
[i
].d
.hnd
= handler
;
131 return static_cast<fd_event_type_enum
>(0);
133 // Copying items to the bigger dynamically allocated array
134 items2
= new Data
[capacity
]; // items2 is initialized in the constructor
135 for (i
= 0; i
< nItems
; ++i
) {
136 items2
[items1
[i
].fd
] = items1
[i
].d
;
137 items1
[i
].init(); // not necessary - for debugging
140 pollFds2
= new pollfd
[capacity
];
142 while (i
< nItems
) { pollFds2
[i
] = pollFds1
[i
]; init(pollFds1
[i
++]); }
143 while (i
< ITEM1_CAPACITY
) { init(pollFds2
[i
]); init(pollFds1
[i
++]); }
144 while (i
< capacity
) init(pollFds2
[i
++]);
145 //Note: From the above three loops: only the copying is needed
146 // The initializations are only for debugging
150 if (findInItems2(fd
)) {
152 if (items2
[fd
].hnd
!= 0 && items2
[fd
].hnd
!= handler
) {
153 TTCN_error_begin("Trying to add file descriptor (%d) "
154 "events (%d) to the set of events handled by \"",fd
,event
);
156 TTCN_Logger::log_event("\", but the events of the "
157 "file descriptor already have a different handler: \"");
158 if (items2
[fd
].hnd
!= 0) items2
[fd
].hnd
->log();
159 TTCN_Logger::log_event("\".");
162 fd_event_type_enum oldEvent
;
164 oldEvent
= static_cast<fd_event_type_enum
>(items2
[fd
].evt
);
165 items2
[fd
].evt
|= static_cast<short>(event
);
167 items2
[fd
].hnd
= handler
; // in case it is a frozen deleted item
168 oldEvent
= pollEventToEvent(pollFds2
[items2
[fd
].ixPoll
].events
);
169 pollFds2
[items2
[fd
].ixPoll
].events
|= pollEvent
;
177 items2
[fd
].evt
= static_cast<short>(event
);
180 pollFds2
[nItems
].fd
= fd
;
181 pollFds2
[nItems
].events
= pollEvent
;
182 pollFds2
[nItems
].revents
= 0;
183 items2
[fd
].ixPoll
= nItems
;
185 items2
[fd
].hnd
= handler
;
187 return static_cast<fd_event_type_enum
>(0);
190 fd_event_type_enum
FdMap::remove(int fd
, const Fd_Event_Handler
* handler
,
191 fd_event_type_enum event
)
193 // Errors in Test Ports may be detected at this point
194 // handler is used only for checking;
195 // handler is 0 to remove a frozen deleted item
196 if (fd
< 0 || fd
>= capacity
) {
197 TTCN_error_begin("Trying to remove events of an invalid file "
198 "descriptor (%d) from the set of events handled by \"", fd
);
199 if (handler
!= 0) handler
->log();
200 TTCN_Logger::log_event("\".");
203 if ((event
& ~(FD_EVENT_RD
| FD_EVENT_WR
| FD_EVENT_ERR
)) != 0) {
204 TTCN_error_begin("Trying to remove invalid events (%d) of file "
205 "descriptor (%d) from the set of events handled by \"", event
, fd
);
206 if (handler
!= 0) handler
->log();
207 TTCN_Logger::log_event("\".");
210 fd_event_type_enum oldEvent
;
212 short pollEvent
= eventToPollEvent(event
);
215 int i
= findInItems1(fd
);
217 TTCN_warning_begin("Trying to remove file descriptor (%d) "
218 "events (%d) from the set of events handled by \"",
220 if (handler
!= 0) handler
->log();
221 TTCN_Logger::log_event("\", but events of the file descriptor "
222 "do not have a handler.");
224 // Ignore errors for HP53582.
227 if (handler
!= items1
[i
].d
.hnd
) {
228 TTCN_error_begin("Trying to remove file descriptor (%d) "
229 "events (%d) from the set of events handled by \"", fd
, event
);
230 if (handler
!= 0) handler
->log();
231 TTCN_Logger::log_event("\", but the events of the "
232 "file descriptor have different handler: \"");
233 if (items1
[i
].d
.hnd
!= 0) items1
[i
].d
.hnd
->log();
234 TTCN_Logger::log_event("\".");
238 short ixE
= items1
[i
].d
.ixE
;
239 if (ixE
>= 0) epollEvents
[ixE
].events
&= ~eventToEpollEvent(event
);
240 oldEvent
= static_cast<fd_event_type_enum
>(items1
[i
].d
.evt
);
241 if ((items1
[i
].d
.evt
&= ~static_cast<short>(event
)) == 0) {
243 while (i
< nItems
) { items1
[i
] = items1
[i
+ 1]; ++i
; }
244 items1
[nItems
].init(); // not necessary - for debugging
247 int j
= items1
[i
].d
.ixPoll
;
248 oldEvent
= pollEventToEvent(pollFds1
[j
].events
);
249 pollFds1
[j
].revents
&= ~pollEvent
;
250 if ((pollFds1
[j
].events
&= ~pollEvent
) == 0) {
251 if (j
>= nPollFdsFrozen
) {
252 if (j
< nItems
- 1) {
253 pollFds1
[j
] = pollFds1
[nItems
- 1];
254 int k
= findInItems1(pollFds1
[j
].fd
); // reads nItems
255 items1
[k
].d
.ixPoll
= j
;
258 while (i
< nItems
) { items1
[i
] = items1
[i
+ 1]; ++i
; }
259 init(pollFds1
[nItems
]); // not necessary - for debugging
260 items1
[nItems
].init(); // not necessary - for debugging
261 } else { // The item is frozen; removal is postponed.
268 if (!findInItems2(fd
)) {
269 TTCN_error_begin("Trying to remove file descriptor (%d) "
270 "events (%d) from the set of events handled by \"",
272 if (handler
!= 0) handler
->log();
273 TTCN_Logger::log_event("\", but events of the file descriptor "
274 "do not have a handler.");
277 if (items2
[fd
].hnd
!= handler
) {
278 TTCN_error_begin("Trying to remove file descriptor (%d) "
279 "events (%d) from the set of events handled by \"", fd
, event
);
280 if (handler
!= 0) handler
->log();
281 TTCN_Logger::log_event("\", but the events of the "
282 "file descriptor have different handler: \"");
283 items2
[fd
].hnd
->log(); TTCN_Logger::log_event("\".");
287 short ixE
= items2
[fd
].ixE
;
288 if (ixE
>= 0) epollEvents
[ixE
].events
&= ~eventToEpollEvent(event
);
289 oldEvent
= static_cast<fd_event_type_enum
>(items2
[fd
].evt
);
290 if ((items2
[fd
].evt
&= ~static_cast<short>(event
)) == 0) {
292 items2
[fd
].init(); // necessary to indicate an unused item
293 if (nItems
<= ITEM1_CAPACITY_LOW
) {
294 // could be improved with additional bookkeeping
295 // items1 has to be kept ordered with fd as key
296 for (int i
= 0, n
= 0; n
< nItems
&& i
< capacity
; ++i
) {
297 if (findInItems2(i
)) {
299 items1
[n
++].d
= items2
[i
];
302 delete[] items2
; items2
= 0;
303 //if (n < nItems) => error
307 int i
= items2
[fd
].ixPoll
;
308 oldEvent
= pollEventToEvent(pollFds2
[i
].events
);
309 pollFds2
[i
].revents
&= ~pollEvent
;
310 if ((pollFds2
[i
].events
&= ~pollEvent
) == 0) {
311 if (i
>= nPollFdsFrozen
) {
314 pollFds2
[i
] = pollFds2
[nItems
];
315 items2
[pollFds2
[i
].fd
].ixPoll
= i
;
317 init(pollFds2
[nItems
]); // not necessary - for debugging
318 items2
[fd
].init(); // necessary to indicate an unused item
319 if (nItems
<= ITEM1_CAPACITY_LOW
) {
320 // items1 has to be kept ordered with fd as key
321 copyItems2ToItems1();
322 delete[] items2
; items2
= 0;
323 delete[] pollFds2
; pollFds2
= 0;
325 } else { // The item is frozen; removal is postponed.
335 fd_event_type_enum
FdMap::find(int fd
, Fd_Event_Handler
* * handler
)
339 int i
= findInItems1(fd
);
340 if (i
< 0) { *handler
= 0; return static_cast<fd_event_type_enum
>(0); }
343 if (!findInItems2(fd
)) {
344 *handler
= 0; return static_cast<fd_event_type_enum
>(0);
348 *handler
= data
->hnd
;
350 return static_cast<fd_event_type_enum
>(data
->evt
);
352 return pollEventToEvent(getPollFds()[data
->ixPoll
].events
);
357 void FdMap::copyItems2ToItems1()
359 if (nItems
!= ITEM1_CAPACITY_LOW
)
360 TTCN_error("FdMap::copyItems2ToItems1: Internal error");
361 if (ITEM1_CAPACITY_LOW
== 0) return;
364 for (int m
= n
- 1; m
!= 0; ++i
) m
>>= 1;
365 Item
* d
, itemsTmp
[ITEM1_CAPACITY_LOW
];
366 bool f
= (i
& 1) != 0;
367 d
= (i
== 0 || f
) ? items1
: itemsTmp
;
368 for (int j
= 0, k
= nItems
- 1; j
< k
; j
+= 2) {
369 pollFds1
[j
] = pollFds2
[j
]; pollFds1
[j
+ 1] = pollFds2
[j
+ 1];
370 int fd1
= pollFds1
[j
].fd
, fd2
= pollFds1
[j
+ 1].fd
;
372 d
[j
].fd
= fd1
; d
[j
].d
= items2
[fd1
];
373 d
[j
+ 1].fd
= fd2
; d
[j
+ 1].d
= items2
[fd2
];
375 d
[j
].fd
= fd2
; d
[j
].d
= items2
[fd2
];
376 d
[j
+ 1].fd
= fd1
; d
[j
+ 1].d
= items2
[fd1
];
379 if ((nItems
& 1) != 0) {
380 pollFds1
[n
- 1] = pollFds2
[n
- 1];
381 int fd1
= pollFds1
[n
- 1].fd
;
382 d
[nItems
- 1].fd
= fd1
;
383 d
[nItems
- 1].d
= items2
[fd1
];
385 for (int j
= 1; j
< i
; ++j
) {
386 Item
* s
= f
? items1
: itemsTmp
;
388 d
= f
? items1
: itemsTmp
;
390 int step2
= step
* 2;
392 for (int k
= 0; k
< n
; k
+= step2
) {
395 int uN
= (n
>= v
) ? step
: (n
- u
);
396 int vN
= (n
>= v
+ step
) ? step
: (n
- v
);
397 while (uN
!= 0 && vN
> 0) {
398 if (s
[u
].fd
<= s
[v
].fd
) {
399 d
[w
++] = s
[u
++]; --uN
;
401 d
[w
++] = s
[v
++]; --vN
;
404 while (uN
!= 0) { d
[w
++] = s
[u
++]; --uN
; }
405 while (vN
> 0) { d
[w
++] = s
[v
++]; --vN
; }
412 bool FdMap::epollMarkFds(int nEvents
)
414 bool all_valid
= true;
415 for (int i
= 0; i
< nEvents
; ++i
) {
416 int fd
= epollEvents
[i
].data
.fd
;
418 int j
= findInItems1(fd
);
419 if (j
>= 0) items1
[j
].d
.ixE
= i
;
420 else all_valid
= false;
422 if (findInItems2(fd
)) items2
[fd
].ixE
= i
;
423 else all_valid
= false;
429 void FdMap::epollUnmarkFds(int nEvents
)
431 for (int i
= 0; i
< nEvents
; ++i
) {
432 int fd
= epollEvents
[i
].data
.fd
;
434 int j
= findInItems1(fd
);
435 if (j
>= 0) items1
[j
].d
.ixE
= -1;
437 if (findInItems2(fd
)) items2
[fd
].ixE
= -1;
442 void FdMap::pollFreeze()
444 nPollFdsFrozen
= nItems
;
447 void FdMap::pollUnfreeze()
449 if (!needUpdate
) { nPollFdsFrozen
= 0; return; }
450 int i
= 0, j
= nPollFdsFrozen
;
453 pollfd
& pollFd
= getPollFds()[i
];
454 if (pollFd
.events
== 0) {
455 remove(pollFd
.fd
, 0, static_cast<fd_event_type_enum
>(
456 FD_EVENT_RD
| FD_EVENT_WR
| FD_EVENT_ERR
));
457 if (nItems
< j
) j
= nItems
;
458 // item at index i is changed and has to be checked if exists
464 fd_event_type_enum
FdMap::getPollREvent(int fd
)
467 int i
= findInItems1(fd
);
469 return pollEventToEvent(pollFds1
[items1
[i
].d
.ixPoll
].revents
);
471 if (findInItems2(fd
))
472 return pollEventToEvent(pollFds2
[items2
[fd
].ixPoll
].revents
);
474 return static_cast<fd_event_type_enum
>(0);
482 /******************************************************************************
483 * class Fd_Event_Handler *
484 ******************************************************************************/
486 void Fd_Event_Handler::log() const
488 TTCN_Logger::log_event("handler <invalid>");
496 /******************************************************************************
497 * class Fd_And_Timeout_Event_Handler *
498 ******************************************************************************/
500 void Fd_And_Timeout_Event_Handler::Handle_Fd_Event(int,
501 boolean
, boolean
, boolean
)
503 TTCN_error("Fd_And_Timeout_Event_Handler::Handle_Fd_Event: "
504 "Erroneous usage of class Fd_And_Timeout_Event_Handler");
505 // This method cannot be pure virtual because of necessary instantiation
509 void Fd_And_Timeout_Event_Handler::Handle_Timeout(double)
511 TTCN_error("Fd_And_Timeout_Event_Handler::Handle_Timeout: "
512 "Erroneous usage of class Fd_And_Timeout_Event_Handler");
513 // This method cannot be pure virtual because of necessary instantiation
517 void Fd_And_Timeout_Event_Handler::Event_Handler(const fd_set
*,
518 const fd_set
*, const fd_set
*, double)
520 TTCN_error("Fd_And_Timeout_Event_Handler::Event_Handler: "
521 "Erroneous usage of class Fd_And_Timeout_Event_Handler");
522 // This method cannot be pure virtual because of necessary instantiation
526 Fd_And_Timeout_Event_Handler::~Fd_And_Timeout_Event_Handler()
528 // In case the event handler forgot to stop its timer,
529 // stop it at this point.
530 Fd_And_Timeout_User::set_timer(this, 0.0);
531 // In case the event handler forgot to remove all of its file descriptor
532 // events, remove those at this point.
533 Fd_And_Timeout_User::remove_all_fds(this);
536 void Fd_And_Timeout_Event_Handler::log() const
538 TTCN_Logger::log_event("handler <unknown>");
545 /* The maximal blocking time to be used in poll, epoll and select (in seconds).
546 * On some systems (e.g. Solaris) the select call is a libc wrapper for
547 * poll(2). In the wrapper the overflows are not always handled thus select
548 * may return EINVAL in some cases.
549 * This value is derived from the maximal possible value for the last argument
550 * of poll, which is measured in milliseconds. */
551 #define MAX_BLOCK_TIME (~(0x80 << ((sizeof(int) - 1) * 8)) / 1000)
557 /******************************************************************************
558 * class Fd_And_Timeout_User *
559 ******************************************************************************/
561 Handler_List
Fd_And_Timeout_User::timedList
,
562 Fd_And_Timeout_User::oldApiCallList
;
563 FdSets
* Fd_And_Timeout_User::fdSetsReceived
;
564 FdSets
* Fd_And_Timeout_User::fdSetsToHnds
;
565 int Fd_And_Timeout_User::nOldHandlers
;
566 bool Fd_And_Timeout_User::is_in_call_handlers
;
567 int Fd_And_Timeout_User::curRcvdEvtIx
;
569 inline void Fd_And_Timeout_User::checkFd(int fd
)
573 fd
== FdMap::epollFd
||
575 fcntl(fd
, F_GETFD
, FD_CLOEXEC
) < 0)
576 TTCN_error("Trying to add events of an invalid file descriptor (%d)", fd
);
579 void Fd_And_Timeout_User::add_fd(int fd
, Fd_Event_Handler
* handler
,
580 fd_event_type_enum event
)
582 fd_event_type_enum oldEvent
= FdMap::add(fd
, handler
, event
);
583 Fd_And_Timeout_Event_Handler
* tmHnd
=
584 dynamic_cast<Fd_And_Timeout_Event_Handler
*>(handler
);
586 if (tmHnd
->fdSets
!= 0) {
587 if (fd
>= (int)FD_SETSIZE
)
588 TTCN_error("The file descriptor (%d) to be added is too big "
589 "to be handled by Event_Handler. FD_SETSIZE is %d",
591 tmHnd
->fdSets
->add(fd
, event
);
593 if (oldEvent
== 0) ++tmHnd
->fdCount
;
596 epoll_event epollEvent
;
597 memset(&epollEvent
, 0, sizeof(epollEvent
));
598 epollEvent
.events
= FdMap::eventToEpollEvent(oldEvent
| event
);
599 epollEvent
.data
.fd
= fd
;
600 if ( ( (oldEvent
== 0) ?
601 epoll_ctl(FdMap::epollFd
, EPOLL_CTL_ADD
, fd
, &epollEvent
) :
602 epoll_ctl(FdMap::epollFd
, EPOLL_CTL_MOD
, fd
, &epollEvent
) ) < 0 ) {
604 TTCN_error("Fd_And_Timeout_User::add_fd: System call epoll_ctl failed "
605 "when adding fd: %d, errno: %d", fd
, errno
);
612 void Fd_And_Timeout_User::remove_fd(int fd
, Fd_Event_Handler
* handler
,
613 fd_event_type_enum event
)
616 TTCN_error("Fd_And_Timeout_User::remove_fd: Internal error"); // debug
617 fd_event_type_enum oldEvent
= FdMap::remove(fd
, handler
, event
);
618 // Ignore errors for HP53582.
619 if (oldEvent
== FD_EVENT_ERR
) return;
620 fd_event_type_enum newEvent
=
621 static_cast<fd_event_type_enum
>(oldEvent
& ~event
);
622 Fd_And_Timeout_Event_Handler
* tmHnd
=
623 dynamic_cast<Fd_And_Timeout_Event_Handler
*>(handler
);
625 if (newEvent
== 0) --tmHnd
->fdCount
;
626 if (tmHnd
->getIsOldApi()) {
627 fdSetsReceived
->remove(fd
, event
);
628 tmHnd
->fdSets
->remove(fd
, event
);
632 epoll_event epollEvent
;
633 memset(&epollEvent
, 0, sizeof(epollEvent
));
634 epollEvent
.data
.fd
= fd
;
636 if (epoll_ctl(FdMap::epollFd
, EPOLL_CTL_DEL
, fd
, &epollEvent
) < 0) {
637 // Check if fd exists. If not, assume, that it was closed earlier.
638 int errno_tmp
= errno
;
639 if (fcntl(fd
, F_GETFD
, FD_CLOEXEC
) >= 0) {
641 TTCN_error("System call epoll_ctl failed when deleting fd: %d, "
642 "errno: %d", fd
, errno
);
644 // fd was closed before removing it from the database of TITAN.
645 // This removes fd from the epoll set causing epoll_ctl to fail.
646 errno
= 0; // It is not an error if epoll_ctl fails here
649 // There is still event type to wait for. - This is the unusual case.
650 epollEvent
.events
= FdMap::eventToEpollEvent(newEvent
);
651 if (epoll_ctl(FdMap::epollFd
, EPOLL_CTL_MOD
, fd
, &epollEvent
) < 0) {
652 TTCN_error("System call epoll_ctl failed when removing fd: %d, "
653 "errno: %d", fd
, errno
);
659 void Fd_And_Timeout_User::set_timer(Fd_And_Timeout_Event_Handler
* handler
,
660 double call_interval
,
661 boolean is_timeout
, boolean call_anyway
, boolean is_periodic
)
663 if (call_interval
!= 0.0) {
664 if (handler
->list
== 0) timedList
.add(handler
);
665 handler
->callInterval
= call_interval
;
666 handler
->last_called
= TTCN_Snapshot::time_now();
667 handler
->isTimeout
= is_timeout
;
668 handler
->callAnyway
= call_anyway
;
669 handler
->isPeriodic
= is_periodic
;
671 if (handler
->list
== &timedList
) timedList
.remove(handler
);
672 // Note: Normally the port may be only in timedList or in no list.
673 // - The port is put in oldApiCallList only temporarily while calling
675 // The set_timer method may be called from outside snapshot evaluation
676 // or in the event handler. In both cases the port is removed from
677 // oldApiCallList beforehand.
678 // - However when MC requests a port to be unmapped: the request is
679 // processed in the event handler of MC_Connection. In this event
680 // handler a port may be unmapped which has been put in oldApiCallList
682 handler
->callInterval
= 0.0;
686 void Fd_And_Timeout_User::set_fds_with_fd_sets(
687 Fd_And_Timeout_Event_Handler
* handler
,
688 const fd_set
*read_fds
, const fd_set
*write_fds
, const fd_set
*error_fds
)
690 // Probaly in class PORT: Install_Handler should not be possible to be
691 // called from new event handlers
692 int fdLimit
= FdMap::getFdLimit();
693 if ((int)FD_SETSIZE
< fdLimit
) fdLimit
= FD_SETSIZE
;
694 if (handler
->fdSets
== 0) {
695 if (handler
->fdCount
!= 0) {
696 // Usage of the new and old API is mixed. - It should not happen.
697 // It is handled, but not most efficiently.
698 remove_all_fds(handler
);
700 handler
->fdSets
= new FdSets
;
702 if (fdSetsReceived
== 0) fdSetsReceived
= new FdSets
;
703 if (fdSetsToHnds
== 0) fdSetsToHnds
= new FdSets
;
705 FdSets
* fdSets
= handler
->fdSets
;
706 fd_event_type_enum eventOld
, eventNew
;
708 // Removing fds which refer to different descriptors than
709 // in the previous call. (closed and other fd created with same id)
710 epoll_event epollEvent
;
711 for (int fd
= 0; ; ++fd
) {
712 fd
= fdSets
->getIxBothAnySet(read_fds
, write_fds
, error_fds
,fd
,fdLimit
);
713 if (fd
>= fdLimit
) break;
714 memset(&epollEvent
, 0, sizeof(epollEvent
));
715 epollEvent
.data
.fd
= fd
;
716 // Check (inverse) if fd is still in the epoll set
717 if (epoll_ctl(FdMap::epollFd
, EPOLL_CTL_ADD
, fd
, &epollEvent
) >= 0 ) {
718 // fd was not in the epoll set as fd was closed and a new
719 // descriptor was created with the same id.
720 eventOld
= fdSets
->getEvent(fd
);
721 Fd_And_Timeout_User::remove_fd(fd
, handler
, eventOld
);
723 errno
= 0; // fd is already in the epoll set - it is not an error
727 fd_event_type_enum event
;
728 for (int fd
= 0; ; ++fd
) {
729 fd
= fdSets
->getIxDiff(read_fds
, write_fds
, error_fds
, fd
, fdLimit
);
730 if (fd
>= fdLimit
) break;
731 eventOld
= fdSets
->getEvent(fd
);
732 eventNew
= FdSets::getEvent(read_fds
, write_fds
, error_fds
, fd
);
733 event
= static_cast<fd_event_type_enum
>(eventNew
& ~eventOld
);
734 if (event
!= 0) Fd_And_Timeout_User::add_fd(fd
, handler
, event
);
735 event
= static_cast<fd_event_type_enum
>(eventOld
& ~eventNew
);
736 if (event
!= 0) Fd_And_Timeout_User::remove_fd(fd
, handler
, event
);
740 void Fd_And_Timeout_User::remove_all_fds(Fd_And_Timeout_Event_Handler
* handler
)
742 if (handler
->fdSets
!= 0 &&
744 !FdMap::isItems1Used()
746 (unsigned) FdMap::getSize() > FD_SETSIZE
/ sizeof(long)
749 // FdSets is used to enumerate and remove all file descriptor of
750 // the specified handler
751 FdSets
* fdSets
= handler
->fdSets
;
752 for (int fd
= 0; handler
->fdCount
!= 0; ++fd
) {
753 fd
= fdSets
->getIxSet(fd
, FD_SETSIZE
);
754 if (fd
>= (int)FD_SETSIZE
)
755 TTCN_error("Fd_And_Timeout_User::remove_all_fds Internal "
756 "error 1: fdCount: %i", handler
->fdCount
);//debug
757 Fd_And_Timeout_User::remove_fd(fd
, handler
, fdSets
->getEvent(fd
));
760 // FdMap is used to enumerate and remove all file descriptor of
761 // the specified handler
762 Fd_Event_Handler
* hnd
= 0;
763 fd_event_type_enum event
= static_cast<fd_event_type_enum
>(0);
767 int fdLimit
= FdMap::getFdLimit();
768 while (handler
->fdCount
!= 0 && !FdMap::isItems1Used()) {
771 TTCN_error("Fd_And_Timeout_User::remove_all_fds Internal "
772 "error 2: fdCount: %i", handler
->fdCount
);//debug
773 event
= FdMap::item2atFd(fd
, &hnd
);
774 } while (event
== 0 || hnd
!= handler
);
775 Fd_And_Timeout_User::remove_fd(fd
, handler
, event
);
779 while (handler
->fdCount
!= 0 && !FdMap::isItems1Used()) {
780 pollfd
* pollFds
= FdMap::getPollFds();
783 if (i
>= FdMap::getSize())
784 TTCN_error("Fd_And_Timeout_User::remove_all_fds Internal "
785 "error 3: fdCount: %i", handler
->fdCount
);//debug
787 event
= FdMap::item2atFd(fd
, &hnd
);
788 } while (event
== 0 || hnd
!= handler
);
789 Fd_And_Timeout_User::remove_fd(fd
, handler
, event
);
790 --i
; // recheck pollfd item at index i
794 while (handler
->fdCount
!= 0) {
796 if (++i
>= FdMap::getSize())
797 TTCN_error("Fd_And_Timeout_User::remove_all_fds Internal "
798 "error 4: fdCount: %i", handler
->fdCount
);//debug
799 event
= FdMap::item1atIndex(i
, &fd
, &hnd
);
800 } while (event
== 0 || hnd
!= handler
);
801 Fd_And_Timeout_User::remove_fd(fd
, handler
, event
);
802 --i
; // recheck item at index i
805 if (handler
->fdSets
!= 0) {
806 delete handler
->fdSets
; handler
->fdSets
= 0;
808 if (nOldHandlers
== 0) {
809 delete fdSetsReceived
; fdSetsReceived
= 0;
810 delete fdSetsToHnds
; fdSetsToHnds
= 0;
815 bool Fd_And_Timeout_User::getTimeout(double * timeout
)
818 if (timedList
.finished()) return false;
820 Fd_And_Timeout_Event_Handler
* handler
= timedList
.current();
821 double earliestTimeout
= handler
->last_called
+ handler
->callInterval
;
824 while (!timedList
.finished()) {
825 handler
= timedList
.current();
827 double nextCall
= handler
->last_called
+ handler
->callInterval
;
828 if (nextCall
< earliestTimeout
) earliestTimeout
= nextCall
;
830 *timeout
= earliestTimeout
;
834 void Fd_And_Timeout_User::call_handlers(int nEvents
)
836 try { // To keep consistency in case of exceptions
837 is_in_call_handlers
= true;
838 if (nOldHandlers
!= 0) { fdSetsReceived
->clear(); }
840 // Note: FdMap may be modified during event handler calls
842 FdMap::epollMarkFds(nEvents
);
843 int ixLimit
= nEvents
;
846 int ixLimit
= FdMap::getSize();
847 // Below this index pollfd array items are not removed
848 // If an item should have been removed, then the events field is 0
850 try { // To keep consistency in case of exceptions
851 for (int ix
= 0; ix
!= ixLimit
; ++ix
) {
853 int fd
= FdMap::epollEvents
[ix
].data
.fd
;
854 fd_event_type_enum event
=
855 FdMap::epollEventToEvent(FdMap::epollEvents
[ix
].events
);
857 pollfd
* pollFd
= &(FdMap::getPollFds()[ix
]);
858 if ((pollFd
->revents
& FdMap::pollEventMask
) == 0) continue;
860 fd_event_type_enum event
= FdMap::pollEventToEvent(pollFd
->revents
);
861 // The event handler may need pollFd.revents or epoll .events
863 Fd_Event_Handler
* handler
= 0;
864 fd_event_type_enum wEvent
= FdMap::find(fd
, &handler
);
866 event
= static_cast<fd_event_type_enum
>(
867 event
& (wEvent
| FD_EVENT_ERR
));
869 curRcvdEvtIx
= ix
; // see getCurReceivedEvent()
870 Fd_And_Timeout_Event_Handler
* tmHnd
=
871 dynamic_cast<Fd_And_Timeout_Event_Handler
*>(handler
);
872 if (tmHnd
!= 0 && tmHnd
->getIsOldApi()) {
873 fdSetsReceived
->add(fd
, event
);
874 if (tmHnd
->list
== 0) oldApiCallList
.add(tmHnd
);
876 handler
->Handle_Fd_Event(fd
, (event
& FD_EVENT_RD
) != 0,
877 (event
& FD_EVENT_WR
) != 0,
878 (event
& FD_EVENT_ERR
) != 0);
879 if (tmHnd
!= 0 && tmHnd
->list
== &timedList
)
880 tmHnd
->hasEvent
= TRUE
;
889 FdMap::epollUnmarkFds(nEvents
);
891 FdMap::pollUnfreeze();
896 FdMap::epollUnmarkFds(nEvents
);
898 FdMap::pollUnfreeze();
900 // Call handlers with old API without timer
901 for (oldApiCallList
.first(); !oldApiCallList
.finished(); ) {
902 Fd_And_Timeout_Event_Handler
* handler
= oldApiCallList
.current();
903 oldApiCallList
.next();
904 oldApiCallList
.remove(handler
);
905 // Check if the event handler was uninstalled in the meanwhile
906 // (Currently the check is superfluous as an other event handler
907 // may not uninstall this event handler.)
908 if (handler
->fdSets
== 0) continue;
909 // Get the common set of received and waited events
910 // Check if the set contains any element
911 if ( fdSetsToHnds
->setAnd(*fdSetsReceived
, *handler
->fdSets
) ) {
912 double current_time
= TTCN_Snapshot::time_now();
913 double time_since_last_call
= current_time
-
914 handler
->last_called
;
915 handler
->last_called
= current_time
;
916 handler
->Event_Handler(fdSetsToHnds
->getReadFds(),
917 fdSetsToHnds
->getWriteFds(), fdSetsToHnds
->getErrorFds(),
918 time_since_last_call
);
922 // Call timeout handlers (also handlers with old API with timer)
923 double current_time
= TTCN_Snapshot::time_now();
924 for (timedList
.first(); !timedList
.finished(); ) {
925 Fd_And_Timeout_Event_Handler
* handler
= timedList
.current();
926 timedList
.next(); // The handler may be removed from the list
927 if (handler
->getIsOldApi())
929 fdSetsToHnds
->setAnd(*fdSetsReceived
, *handler
->fdSets
);
930 bool callHandler
= (handler
->hasEvent
&& handler
->isTimeout
) ?
931 handler
->callAnyway
:
932 current_time
> (handler
->last_called
+ handler
->callInterval
);
933 if ( !handler
->isPeriodic
&&
934 (callHandler
|| (handler
->hasEvent
&& handler
->isTimeout
)) ) {
935 handler
->callInterval
= 0.0;
936 timedList
.remove(handler
);
938 handler
->hasEvent
= FALSE
;
940 double time_since_last_call
= current_time
- handler
->last_called
;
941 handler
->last_called
= current_time
;
942 if (!handler
->getIsOldApi())
943 handler
->Handle_Timeout(time_since_last_call
);
944 else if (handler
->fdSets
!= 0)
945 handler
->Event_Handler(fdSetsToHnds
->getReadFds(),
946 fdSetsToHnds
->getWriteFds(), fdSetsToHnds
->getErrorFds(),
947 time_since_last_call
);
948 current_time
= TTCN_Snapshot::time_now();
951 is_in_call_handlers
= false;
952 } catch (...) { oldApiCallList
.clear(); is_in_call_handlers
= false; throw; }
955 int Fd_And_Timeout_User::receiveEvents(int pollTimeout
)
959 ret_val
= epoll_wait(FdMap::epollFd
,
960 FdMap::epollEvents
, FdMap::MAX_EPOLL_EVENTS
, pollTimeout
);
961 if (ret_val
< 0 && errno
!= EINTR
)
962 TTCN_error("System call epoll_wait() failed when taking a new snapshot.");
964 ret_val
= poll(FdMap::getPollFds(), FdMap::getSize(), pollTimeout
);
965 if (ret_val
< 0 && errno
!= EINTR
)
966 TTCN_error("System call poll() failed when taking a new snapshot.");
972 void Fd_And_Timeout_User::reopenEpollFd()
974 if (FdMap::epollFd
!= -1) { close (FdMap::epollFd
); FdMap::epollFd
= -1; }
975 FdMap::epollFd
= epoll_create(16 /* epoll size hint */);
976 // FIXME: method to determine the optimal epoll size hint
977 // epoll size hint is ignored in newer kernels
978 // for older kernels: it should be big enough not to be too slow
979 // and it should not be very big to limit memory usage
980 if (FdMap::epollFd
< 0)
981 TTCN_error("System call epoll_create() failed in child process.");
983 if (FdMap::getSize() != 1)
984 TTCN_error("Fd_And_Timeout_User::reopenEpollFd: Internal error");
992 /******************************************************************************
993 * class TTCN_Snapshot *
994 ******************************************************************************/
996 boolean
TTCN_Snapshot::else_branch_found
;
997 double TTCN_Snapshot::alt_begin
;
999 void TTCN_Snapshot::initialize()
1001 long openMax
= sysconf(_SC_OPEN_MAX
);
1002 int fdLimit
= (openMax
<= (long) MAX_INT_VAL
) ? (int) openMax
: MAX_INT_VAL
;
1004 FdMap::initialize(fdLimit
);
1005 Fd_And_Timeout_User::initialize();
1007 FdMap::epollFd
= epoll_create(16 /* epoll size hint */);
1008 // FIXME: method to determine the optimal epoll size hint
1009 // epoll size hint is ignored in newer kernels
1010 // for older kernels: it should be big enough not to be too slow
1011 // and it should not be very big to limit memory usage
1012 if (FdMap::epollFd
< 0)
1013 TTCN_error("TTCN_Snapshot::initialize: "
1014 "System call epoll_create() failed.");
1016 else_branch_found
= FALSE
;
1017 alt_begin
= time_now();
1020 void TTCN_Snapshot::check_fd_setsize()
1022 if ((long) FdMap::getFdLimit() > (long) FD_SETSIZE
)
1023 TTCN_Logger::log_fd_limits(FdMap::getFdLimit(), (long) FD_SETSIZE
);
1026 void TTCN_Snapshot::terminate()
1029 if (FdMap::epollFd
!= -1) { close (FdMap::epollFd
); FdMap::epollFd
= -1; }
1031 Fd_And_Timeout_User::terminate();
1035 void TTCN_Snapshot::else_branch_reached()
1037 if (!else_branch_found
) {
1038 else_branch_found
= TRUE
;
1039 TTCN_warning("An [else] branch of an alt construct has been reached. "
1040 "Re-configuring the snapshot manager to call the event handlers "
1041 "even when taking the first snapshot.");
1045 double TTCN_Snapshot::time_now()
1047 static time_t start_time
;
1048 static boolean first_call
= TRUE
;
1050 if (gettimeofday(&tv
, NULL
) == -1)
1051 TTCN_error("gettimeofday() system call failed.");
1053 start_time
= tv
.tv_sec
;
1056 return (double)(tv
.tv_sec
- start_time
) + 1e-6 * (double)tv
.tv_usec
;
1060 void TTCN_Snapshot::take_new(boolean block_execution
)
1062 if (block_execution
|| else_branch_found
) {
1064 // jump here if epoll()/poll()/select() was interrupted by a signal
1066 // determine the timeout value for epoll()/poll()/select()
1067 double timeout
= 0.0;
1068 int pollTimeout
= 0; // timeout for poll/epoll
1069 bool handleTimer
= false;
1070 if (block_execution
) {
1071 // find the earliest timeout
1072 double timer_timeout
, handler_timeout
= 0.0;
1073 boolean is_timer_timeout
= TIMER::get_min_expiration(timer_timeout
);
1074 bool is_handler_timeout
=
1075 Fd_And_Timeout_User::getTimeout(&handler_timeout
);
1076 if (is_timer_timeout
) {
1077 if (is_handler_timeout
&& handler_timeout
< timer_timeout
)
1078 timeout
= handler_timeout
;
1079 else timeout
= timer_timeout
;
1080 } else if (is_handler_timeout
) timeout
= handler_timeout
;
1081 if (is_timer_timeout
|| is_handler_timeout
) {
1082 // there are active TTCN-3 or test port timers
1083 double current_time
= time_now();
1084 double block_time
= timeout
- current_time
;
1085 if (block_time
> 0.0) {
1086 // the first timeout is in the future: blocking is needed
1087 // filling up tv with appropriate values
1088 if (block_time
< (double)MAX_BLOCK_TIME
) {
1089 pollTimeout
= static_cast<int>(floor(block_time
*1000));
1092 // issue a warning: the user probably does not want such
1094 TTCN_warning("The time needed for the first timer "
1095 "expiry is %g seconds. The operating system does "
1096 "not support such long waiting at once. The "
1097 "maximum time of blocking was set to %d seconds "
1098 "(ca. %d days).", block_time
, MAX_BLOCK_TIME
,
1099 MAX_BLOCK_TIME
/ 86400);
1100 // also modify the the timeout value to get out
1101 // immediately from the while() loop below
1102 timeout
= current_time
+ (double)MAX_BLOCK_TIME
;
1103 pollTimeout
= MAX_BLOCK_TIME
* 1000;
1107 // first timer is already expired: do not block
1112 // no active timers: infinite timeout
1116 // blocking was not requested (we are in a first snapshot after an
1117 // [else] branch): do not block - pollTimeout is 0
1120 if (FdMap::getSize() == 0 && pollTimeout
< 0)
1121 TTCN_error("There are no active timers and no installed event "
1122 "handlers. Execution would block forever.");
1125 if (FdMap::getSize() != 0) {
1126 ret_val
= Fd_And_Timeout_User::receiveEvents(pollTimeout
);
1127 // call epoll_wait() / poll()
1128 if (ret_val
< 0) { /* EINTR - signal */ errno
= 0; goto again
; }
1130 // There isn't any file descriptor to check
1131 if (pollTimeout
> 0) {
1132 // waiting only for a timeout in the future
1134 tv
.tv_sec
= pollTimeout
/ 1000;
1135 tv
.tv_usec
= (pollTimeout
% 1000) * 1000;
1136 ret_val
= select(0, NULL
, NULL
, NULL
, &tv
);
1137 if (ret_val
< 0 && errno
== EINTR
) {
1138 /* signal */ errno
= 0; goto again
;
1140 if ((ret_val
< 0 && errno
!= EINTR
) || ret_val
> 0)
1141 TTCN_error("System call select() failed when taking a new "
1143 } else if (pollTimeout
< 0)
1144 TTCN_error("There are no active timers or installed event "
1145 "handlers. Execution would block forever.");
1149 Fd_And_Timeout_User::call_handlers(ret_val
);
1150 } else if (ret_val
== 0 && handleTimer
) {
1151 // if select() returned because of the timeout, but too early
1152 // do an other round if it has to wait much,
1153 // or do a busy wait if only a few cycles are needed
1154 if (pollTimeout
> 0){
1155 double difference
= time_now() - timeout
;
1156 if(difference
< 0.0){
1157 if(difference
< -0.001){
1161 while (time_now() < timeout
) ;
1165 Fd_And_Timeout_User::call_handlers(0); // Call timeout handlers
1168 // just update the time and check the testcase guard timer if blocking was
1169 // not requested and there is no [else] branch in the test suite
1171 alt_begin
= time_now();
1173 if (testcase_timer
.timeout() == ALT_YES
)
1174 TTCN_error("Guard timer has expired. Execution of current test case "
1175 "will be interrupted.");
1178 void TTCN_Snapshot::block_for_sending(int send_fd
, Fd_Event_Handler
* handler
)
1180 // To be backward compatible: handler is optional
1181 if (Fd_And_Timeout_User::get_is_in_call_handlers())
1182 TTCN_error("TTCN_Snapshot::block_for_sending: The function may not be "
1183 "called from event handler");
1184 Fd_Event_Handler
* hnd
= 0;
1185 if ((FdMap::find(send_fd
, &hnd
) & FD_EVENT_WR
) != 0)
1186 TTCN_error("TTCN_Snapshot::block_for_sending: An event handler already "
1187 "waits for file descriptor %d to be writable", send_fd
);
1188 if (handler
!= 0 && hnd
!= 0 && handler
!= hnd
)
1189 TTCN_error("TTCN_Snapshot::block_for_sending: File descriptor %d "
1190 "already has a handler, which is different from the currently "
1191 "specified.", send_fd
);
1192 static Fd_And_Timeout_Event_Handler dummyHandler
;
1193 if (hnd
== 0) { hnd
= (handler
!= 0) ? handler
: &dummyHandler
; }
1194 Fd_And_Timeout_User::add_fd(send_fd
, hnd
, FD_EVENT_WR
);
1196 int ret_val
= Fd_And_Timeout_User::receiveEvents(-1); // epoll / poll
1198 bool writable
= false;
1199 bool readable
= false;
1202 for (int i
= 0; i
< ret_val
; ++i
) {
1203 if (FdMap::epollEvents
[i
].data
.fd
== send_fd
) {
1205 if ((FdMap::epollEvents
[i
].events
& EPOLLOUT
) != 0){
1212 if (FdMap::getPollREvent(send_fd
) != 0) {readable
= true;}
1213 if ((FdMap::getPollREvent(send_fd
) & FD_EVENT_WR
) != 0) {writable
= true;}
1215 if (writable
) break;
1216 Fd_And_Timeout_User::call_handlers(ret_val
);
1217 if (readable
) break;
1219 // interrupted - EINTR
1221 Fd_And_Timeout_User::remove_fd(send_fd
, hnd
, FD_EVENT_WR
);
1223 // As Fd_And_Timeout_User::call_handlers is not called:
1224 // received events should be cleared at this point
1225 // (Most probably the behavior would be correct without clearing it.)
1226 int nPollFds
= FdMap::getSize();
1227 pollfd
* pollFds
= FdMap::getPollFds();
1228 for (int i
= 0; i
< nPollFds
; ++i
) pollFds
[i
].revents
= 0;