174 lines
5.3 KiB
C
174 lines
5.3 KiB
C
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* Copyright 2021 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
|
|
*/
|
|
|
|
#include "eventloop_posix.h"
|
|
|
|
#if !defined(UA_HAVE_EPOLL)
|
|
|
|
UA_StatusCode
|
|
UA_EventLoopPOSIX_registerFD(UA_EventLoopPOSIX *el, UA_RegisteredFD *rfd) {
|
|
UA_LOCK_ASSERT(&el->elMutex, 1);
|
|
UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Registering fd: %u", (unsigned)rfd->fd);
|
|
|
|
/* Realloc */
|
|
UA_RegisteredFD **fds_tmp = (UA_RegisteredFD**)
|
|
UA_realloc(el->fds, sizeof(UA_RegisteredFD*) * (el->fdsSize + 1));
|
|
if(!fds_tmp) {
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
}
|
|
el->fds = fds_tmp;
|
|
|
|
/* Add to the last entry */
|
|
el->fds[el->fdsSize] = rfd;
|
|
el->fdsSize++;
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
UA_StatusCode
|
|
UA_EventLoopPOSIX_modifyFD(UA_EventLoopPOSIX *el, UA_RegisteredFD *rfd) {
|
|
/* Do nothing, it is enough if the data was changed in the rfd */
|
|
UA_LOCK_ASSERT(&el->elMutex, 1);
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
void
|
|
UA_EventLoopPOSIX_deregisterFD(UA_EventLoopPOSIX *el, UA_RegisteredFD *rfd) {
|
|
UA_LOCK_ASSERT(&el->elMutex, 1);
|
|
UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Unregistering fd: %u", (unsigned)rfd->fd);
|
|
|
|
/* Find the entry */
|
|
size_t i = 0;
|
|
for(; i < el->fdsSize; i++) {
|
|
if(el->fds[i] == rfd)
|
|
break;
|
|
}
|
|
|
|
/* Not found? */
|
|
if(i == el->fdsSize)
|
|
return;
|
|
|
|
if(el->fdsSize > 1) {
|
|
/* Move the last entry in the ith slot and realloc. */
|
|
el->fdsSize--;
|
|
el->fds[i] = el->fds[el->fdsSize];
|
|
UA_RegisteredFD **fds_tmp = (UA_RegisteredFD**)
|
|
UA_realloc(el->fds, sizeof(UA_RegisteredFD*) * el->fdsSize);
|
|
/* if realloc fails the fds are still in a correct state with
|
|
* possibly lost memory, so failing silently here is ok */
|
|
if(fds_tmp)
|
|
el->fds = fds_tmp;
|
|
} else {
|
|
/* Remove the last entry */
|
|
UA_free(el->fds);
|
|
el->fds = NULL;
|
|
el->fdsSize = 0;
|
|
}
|
|
}
|
|
|
|
static UA_FD
|
|
setFDSets(UA_EventLoopPOSIX *el, fd_set *readset, fd_set *writeset, fd_set *errset) {
|
|
UA_LOCK_ASSERT(&el->elMutex, 1);
|
|
|
|
FD_ZERO(readset);
|
|
FD_ZERO(writeset);
|
|
FD_ZERO(errset);
|
|
UA_FD highestfd = UA_INVALID_FD;
|
|
for(size_t i = 0; i < el->fdsSize; i++) {
|
|
UA_FD currentFD = el->fds[i]->fd;
|
|
|
|
/* Add to the fd_sets */
|
|
if(el->fds[i]->listenEvents & UA_FDEVENT_IN)
|
|
FD_SET(currentFD, readset);
|
|
if(el->fds[i]->listenEvents & UA_FDEVENT_OUT)
|
|
FD_SET(currentFD, writeset);
|
|
|
|
/* Always return errors */
|
|
FD_SET(currentFD, errset);
|
|
|
|
/* Highest fd? */
|
|
if(currentFD > highestfd || highestfd == UA_INVALID_FD)
|
|
highestfd = currentFD;
|
|
}
|
|
return highestfd;
|
|
}
|
|
|
|
UA_StatusCode
|
|
UA_EventLoopPOSIX_pollFDs(UA_EventLoopPOSIX *el, UA_DateTime listenTimeout) {
|
|
UA_assert(listenTimeout >= 0);
|
|
UA_LOCK_ASSERT(&el->elMutex, 1);
|
|
|
|
fd_set readset, writeset, errset;
|
|
UA_FD highestfd = setFDSets(el, &readset, &writeset, &errset);
|
|
|
|
/* Nothing to do? */
|
|
if(highestfd == UA_INVALID_FD) {
|
|
UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"No valid FDs for processing");
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
struct timeval tmptv = {
|
|
#ifndef _WIN32
|
|
(time_t)(listenTimeout / UA_DATETIME_SEC),
|
|
(suseconds_t)((listenTimeout % UA_DATETIME_SEC) / UA_DATETIME_USEC)
|
|
#else
|
|
(long)(listenTimeout / UA_DATETIME_SEC),
|
|
(long)((listenTimeout % UA_DATETIME_SEC) / UA_DATETIME_USEC)
|
|
#endif
|
|
};
|
|
|
|
UA_UNLOCK(&el->elMutex);
|
|
int selectStatus = UA_select(highestfd+1, &readset, &writeset, &errset, &tmptv);
|
|
UA_LOCK(&el->elMutex);
|
|
if(selectStatus < 0) {
|
|
/* We will retry, only log the error */
|
|
UA_LOG_SOCKET_ERRNO_WRAP(
|
|
UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Error during select: %s", errno_str));
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
/* Loop over all registered FD to see if an event arrived. Yes, this is why
|
|
* select is slow for many open sockets. */
|
|
for(size_t i = 0; i < el->fdsSize; i++) {
|
|
UA_RegisteredFD *rfd = el->fds[i];
|
|
|
|
/* The rfd is already registered for removal. Don't process incoming
|
|
* events any longer. */
|
|
if(rfd->dc.callback)
|
|
continue;
|
|
|
|
/* Event signaled for the fd? */
|
|
short event = 0;
|
|
if(FD_ISSET(rfd->fd, &readset)) {
|
|
event = UA_FDEVENT_IN;
|
|
} else if(FD_ISSET(rfd->fd, &writeset)) {
|
|
event = UA_FDEVENT_OUT;
|
|
} else if(FD_ISSET(rfd->fd, &errset)) {
|
|
event = UA_FDEVENT_ERR;
|
|
} else {
|
|
continue;
|
|
}
|
|
|
|
UA_LOG_DEBUG(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Processing event %u on fd %u", (unsigned)event,
|
|
(unsigned)rfd->fd);
|
|
|
|
/* Call the EventSource callback */
|
|
rfd->eventSourceCB(rfd->es, rfd, event);
|
|
|
|
/* The fd has removed itself */
|
|
if(i == el->fdsSize || rfd != el->fds[i])
|
|
i--;
|
|
}
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
#endif /* !defined(UA_HAVE_EPOLL) */
|