560 lines
18 KiB
C
560 lines
18 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)
|
|
* Copyright 2021 (c) Fraunhofer IOSB (Author: Jan Hermes)
|
|
*/
|
|
|
|
#include "eventloop_posix.h"
|
|
#include "open62541/plugin/eventloop.h"
|
|
|
|
/*********/
|
|
/* Timer */
|
|
/*********/
|
|
|
|
static UA_DateTime
|
|
UA_EventLoopPOSIX_nextCyclicTime(UA_EventLoop *public_el) {
|
|
UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el;
|
|
return UA_Timer_nextRepeatedTime(&el->timer);
|
|
}
|
|
|
|
static UA_StatusCode
|
|
UA_EventLoopPOSIX_addTimedCallback(UA_EventLoop *public_el,
|
|
UA_Callback callback,
|
|
void *application, void *data,
|
|
UA_DateTime date,
|
|
UA_UInt64 *callbackId) {
|
|
UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el;
|
|
return UA_Timer_addTimedCallback(&el->timer, callback, application,
|
|
data, date, callbackId);
|
|
}
|
|
|
|
static UA_StatusCode
|
|
UA_EventLoopPOSIX_addCyclicCallback(UA_EventLoop *public_el,
|
|
UA_Callback cb,
|
|
void *application, void *data,
|
|
UA_Double interval_ms,
|
|
UA_DateTime *baseTime,
|
|
UA_TimerPolicy timerPolicy,
|
|
UA_UInt64 *callbackId) {
|
|
UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el;
|
|
return UA_Timer_addRepeatedCallback(&el->timer, cb, application,
|
|
data, interval_ms, baseTime,
|
|
timerPolicy, callbackId);
|
|
}
|
|
|
|
static UA_StatusCode
|
|
UA_EventLoopPOSIX_modifyCyclicCallback(UA_EventLoop *public_el,
|
|
UA_UInt64 callbackId,
|
|
UA_Double interval_ms,
|
|
UA_DateTime *baseTime,
|
|
UA_TimerPolicy timerPolicy) {
|
|
UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el;
|
|
return UA_Timer_changeRepeatedCallback(&el->timer, callbackId,
|
|
interval_ms, baseTime,
|
|
timerPolicy);
|
|
}
|
|
|
|
static void
|
|
UA_EventLoopPOSIX_removeCyclicCallback(UA_EventLoop *public_el,
|
|
UA_UInt64 callbackId) {
|
|
UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el;
|
|
UA_Timer_removeCallback(&el->timer, callbackId);
|
|
}
|
|
|
|
static void
|
|
UA_EventLoopPOSIX_addDelayedCallback(UA_EventLoop *public_el,
|
|
UA_DelayedCallback *dc) {
|
|
UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el;
|
|
UA_LOCK(&el->elMutex);
|
|
dc->next = el->delayedCallbacks;
|
|
el->delayedCallbacks = dc;
|
|
UA_UNLOCK(&el->elMutex);
|
|
}
|
|
|
|
static void
|
|
UA_EventLoopPOSIX_removeDelayedCallback(UA_EventLoop *public_el,
|
|
UA_DelayedCallback *dc) {
|
|
UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)public_el;
|
|
UA_LOCK(&el->elMutex);
|
|
UA_DelayedCallback **prev = &el->delayedCallbacks;
|
|
while(*prev) {
|
|
if(*prev == dc) {
|
|
*prev = (*prev)->next;
|
|
UA_UNLOCK(&el->elMutex);
|
|
return;
|
|
}
|
|
prev = &(*prev)->next;
|
|
}
|
|
UA_UNLOCK(&el->elMutex);
|
|
}
|
|
|
|
/* Process and then free registered delayed callbacks */
|
|
static void
|
|
processDelayed(UA_EventLoopPOSIX *el) {
|
|
UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Process delayed callbacks");
|
|
|
|
UA_LOCK_ASSERT(&el->elMutex, 1);
|
|
|
|
/* First empty the linked list in the el. So a delayed callback can add
|
|
* (itself) to the list. New entries are then processed during the next
|
|
* iteration. */
|
|
UA_DelayedCallback *dc = el->delayedCallbacks, *next = NULL;
|
|
el->delayedCallbacks = NULL;
|
|
|
|
for(; dc; dc = next) {
|
|
next = dc->next;
|
|
/* Delayed Callbacks might have no callback set. We don't return a
|
|
* StatusCode during "add" and don't validate. So test here. */
|
|
if(!dc->callback)
|
|
continue;
|
|
UA_UNLOCK(&el->elMutex);
|
|
dc->callback(dc->application, dc->context);
|
|
UA_LOCK(&el->elMutex);
|
|
}
|
|
}
|
|
|
|
/***********************/
|
|
/* EventLoop Lifecycle */
|
|
/***********************/
|
|
|
|
static UA_StatusCode
|
|
UA_EventLoopPOSIX_start(UA_EventLoopPOSIX *el) {
|
|
UA_LOCK(&el->elMutex);
|
|
|
|
if(el->eventLoop.state != UA_EVENTLOOPSTATE_FRESH &&
|
|
el->eventLoop.state != UA_EVENTLOOPSTATE_STOPPED) {
|
|
UA_UNLOCK(&el->elMutex);
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
}
|
|
|
|
UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Starting the EventLoop");
|
|
|
|
#ifdef UA_HAVE_EPOLL
|
|
el->epollfd = epoll_create1(0);
|
|
if(el->epollfd == -1) {
|
|
UA_LOG_SOCKET_ERRNO_WRAP(
|
|
UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
|
|
"Eventloop\t| Could not create the epoll socket (%s)",
|
|
errno_str));
|
|
UA_UNLOCK(&el->elMutex);
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
}
|
|
#endif
|
|
|
|
UA_StatusCode res = UA_STATUSCODE_GOOD;
|
|
UA_EventSource *es = el->eventLoop.eventSources;
|
|
while(es) {
|
|
UA_UNLOCK(&el->elMutex);
|
|
res |= es->start(es);
|
|
UA_LOCK(&el->elMutex);
|
|
es = es->next;
|
|
}
|
|
|
|
/* Dirty-write the state that is const "from the outside" */
|
|
*(UA_EventLoopState*)(uintptr_t)&el->eventLoop.state =
|
|
UA_EVENTLOOPSTATE_STARTED;
|
|
|
|
UA_UNLOCK(&el->elMutex);
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
checkClosed(UA_EventLoopPOSIX *el) {
|
|
UA_LOCK_ASSERT(&el->elMutex, 1);
|
|
|
|
UA_EventSource *es = el->eventLoop.eventSources;
|
|
while(es) {
|
|
if(es->state != UA_EVENTSOURCESTATE_STOPPED)
|
|
return;
|
|
es = es->next;
|
|
}
|
|
|
|
/* Not closed until all delayed callbacks are processed */
|
|
if(el->delayedCallbacks != NULL)
|
|
return;
|
|
|
|
/* Dirty-write the state that is const "from the outside" */
|
|
*(UA_EventLoopState*)(uintptr_t)&el->eventLoop.state =
|
|
UA_EVENTLOOPSTATE_STOPPED;
|
|
|
|
/* Close the epoll/IOCP socket once all EventSources have shut down */
|
|
#ifdef UA_HAVE_EPOLL
|
|
close(el->epollfd);
|
|
#endif
|
|
|
|
UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"The EventLoop has stopped");
|
|
}
|
|
|
|
static void
|
|
UA_EventLoopPOSIX_stop(UA_EventLoopPOSIX *el) {
|
|
UA_LOCK(&el->elMutex);
|
|
|
|
if(el->eventLoop.state != UA_EVENTLOOPSTATE_STARTED) {
|
|
UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"The EventLoop is not running, cannot be stopped");
|
|
UA_UNLOCK(&el->elMutex);
|
|
return;
|
|
}
|
|
|
|
UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Stopping the EventLoop");
|
|
|
|
/* Set to STOPPING to prevent "normal use" */
|
|
*(UA_EventLoopState*)(uintptr_t)&el->eventLoop.state =
|
|
UA_EVENTLOOPSTATE_STOPPING;
|
|
|
|
/* Stop all event sources (asynchronous) */
|
|
UA_EventSource *es = el->eventLoop.eventSources;
|
|
for(; es; es = es->next) {
|
|
if(es->state == UA_EVENTSOURCESTATE_STARTING ||
|
|
es->state == UA_EVENTSOURCESTATE_STARTED) {
|
|
UA_UNLOCK(&el->elMutex);
|
|
es->stop(es);
|
|
UA_LOCK(&el->elMutex);
|
|
}
|
|
}
|
|
|
|
/* Set to STOPPED if all EventSources are STOPPED */
|
|
checkClosed(el);
|
|
|
|
UA_UNLOCK(&el->elMutex);
|
|
}
|
|
|
|
static UA_StatusCode
|
|
UA_EventLoopPOSIX_run(UA_EventLoopPOSIX *el, UA_UInt32 timeout) {
|
|
UA_LOCK(&el->elMutex);
|
|
|
|
if(el->executing) {
|
|
UA_LOG_ERROR(el->eventLoop.logger,
|
|
UA_LOGCATEGORY_EVENTLOOP,
|
|
"Cannot run EventLoop from the run method itself");
|
|
UA_UNLOCK(&el->elMutex);
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
}
|
|
|
|
el->executing = true;
|
|
|
|
if(el->eventLoop.state == UA_EVENTLOOPSTATE_FRESH ||
|
|
el->eventLoop.state == UA_EVENTLOOPSTATE_STOPPED) {
|
|
UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Cannot iterate a stopped EventLoop");
|
|
el->executing = false;
|
|
UA_UNLOCK(&el->elMutex);
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
}
|
|
|
|
UA_LOG_TRACE(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Iterate the EventLoop");
|
|
|
|
/* Process cyclic callbacks */
|
|
UA_DateTime dateBefore =
|
|
el->eventLoop.dateTime_nowMonotonic(&el->eventLoop);
|
|
|
|
UA_UNLOCK(&el->elMutex);
|
|
UA_DateTime dateNext = UA_Timer_process(&el->timer, dateBefore);
|
|
UA_LOCK(&el->elMutex);
|
|
|
|
/* Process delayed callbacks here:
|
|
* - Removes closed sockets already here instead of polling them again.
|
|
* - The timeout for polling is selected to be ready in time for the next
|
|
* cyclic callback. So we want to do little work between the timeout
|
|
* running out and executing the due cyclic callbacks. */
|
|
processDelayed(el);
|
|
|
|
/* A delayed callback could create another delayed callback (or re-add
|
|
* itself). In that case we don't want to wait (indefinitely) for an event
|
|
* to happen. Process queued events but don't sleep. Then process the
|
|
* delayed callbacks in the next iteration. */
|
|
if(el->delayedCallbacks != NULL)
|
|
timeout = 0;
|
|
|
|
/* Compute the remaining time */
|
|
UA_DateTime maxDate = dateBefore + (timeout * UA_DATETIME_MSEC);
|
|
if(dateNext > maxDate)
|
|
dateNext = maxDate;
|
|
UA_DateTime listenTimeout =
|
|
dateNext - el->eventLoop.dateTime_nowMonotonic(&el->eventLoop);
|
|
if(listenTimeout < 0)
|
|
listenTimeout = 0;
|
|
|
|
/* Listen on the active file-descriptors (sockets) from the
|
|
* ConnectionManagers */
|
|
UA_StatusCode rv = UA_EventLoopPOSIX_pollFDs(el, listenTimeout);
|
|
|
|
/* Check if the last EventSource was successfully stopped */
|
|
if(el->eventLoop.state == UA_EVENTLOOPSTATE_STOPPING)
|
|
checkClosed(el);
|
|
|
|
el->executing = false;
|
|
UA_UNLOCK(&el->elMutex);
|
|
return rv;
|
|
}
|
|
|
|
/*****************************/
|
|
/* Registering Event Sources */
|
|
/*****************************/
|
|
|
|
static UA_StatusCode
|
|
UA_EventLoopPOSIX_registerEventSource(UA_EventLoopPOSIX *el,
|
|
UA_EventSource *es) {
|
|
UA_LOCK(&el->elMutex);
|
|
|
|
/* Already registered? */
|
|
if(es->state != UA_EVENTSOURCESTATE_FRESH) {
|
|
UA_LOG_ERROR(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK,
|
|
"Cannot register the EventSource \"%.*s\": "
|
|
"already registered",
|
|
(int)es->name.length, (char*)es->name.data);
|
|
UA_UNLOCK(&el->elMutex);
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
}
|
|
|
|
/* Add to linked list */
|
|
es->next = el->eventLoop.eventSources;
|
|
el->eventLoop.eventSources = es;
|
|
|
|
es->eventLoop = &el->eventLoop;
|
|
es->state = UA_EVENTSOURCESTATE_STOPPED;
|
|
|
|
/* Start if the entire EventLoop is started */
|
|
UA_StatusCode res = UA_STATUSCODE_GOOD;
|
|
if(el->eventLoop.state == UA_EVENTLOOPSTATE_STARTED)
|
|
res = es->start(es);
|
|
|
|
UA_UNLOCK(&el->elMutex);
|
|
return res;
|
|
}
|
|
|
|
static UA_StatusCode
|
|
UA_EventLoopPOSIX_deregisterEventSource(UA_EventLoopPOSIX *el,
|
|
UA_EventSource *es) {
|
|
UA_LOCK(&el->elMutex);
|
|
|
|
if(es->state != UA_EVENTSOURCESTATE_STOPPED) {
|
|
UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Cannot deregister the EventSource %.*s: "
|
|
"Has to be stopped first",
|
|
(int)es->name.length, es->name.data);
|
|
UA_UNLOCK(&el->elMutex);
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
}
|
|
|
|
/* Remove from the linked list */
|
|
UA_EventSource **s = &el->eventLoop.eventSources;
|
|
while(*s) {
|
|
if(*s == es) {
|
|
*s = es->next;
|
|
break;
|
|
}
|
|
s = &(*s)->next;
|
|
}
|
|
|
|
/* Set the state to non-registered */
|
|
es->state = UA_EVENTSOURCESTATE_FRESH;
|
|
|
|
UA_UNLOCK(&el->elMutex);
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
/***************/
|
|
/* Time Domain */
|
|
/***************/
|
|
|
|
/* No special synchronization with an external source, just use the globally
|
|
* defined functions. */
|
|
|
|
static UA_DateTime
|
|
UA_EventLoopPOSIX_DateTime_now(UA_EventLoop *el) {
|
|
return UA_DateTime_now();
|
|
}
|
|
|
|
static UA_DateTime
|
|
UA_EventLoopPOSIX_DateTime_nowMonotonic(UA_EventLoop *el) {
|
|
return UA_DateTime_nowMonotonic();
|
|
}
|
|
|
|
static UA_Int64
|
|
UA_EventLoopPOSIX_DateTime_localTimeUtcOffset(UA_EventLoop *el) {
|
|
return UA_DateTime_localTimeUtcOffset();
|
|
}
|
|
|
|
/*************************/
|
|
/* Initialize and Delete */
|
|
/*************************/
|
|
|
|
static UA_StatusCode
|
|
UA_EventLoopPOSIX_free(UA_EventLoopPOSIX *el) {
|
|
UA_LOCK(&el->elMutex);
|
|
|
|
/* Check if the EventLoop can be deleted */
|
|
if(el->eventLoop.state != UA_EVENTLOOPSTATE_STOPPED &&
|
|
el->eventLoop.state != UA_EVENTLOOPSTATE_FRESH) {
|
|
UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP,
|
|
"Cannot delete a running EventLoop");
|
|
UA_UNLOCK(&el->elMutex);
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
}
|
|
|
|
/* Deregister and delete all the EventSources */
|
|
while(el->eventLoop.eventSources) {
|
|
UA_EventSource *es = el->eventLoop.eventSources;
|
|
UA_UNLOCK(&el->elMutex);
|
|
UA_EventLoopPOSIX_deregisterEventSource(el, es);
|
|
UA_LOCK(&el->elMutex);
|
|
es->free(es);
|
|
}
|
|
|
|
/* Remove the repeated timed callbacks */
|
|
UA_Timer_clear(&el->timer);
|
|
|
|
/* Process remaining delayed callbacks */
|
|
processDelayed(el);
|
|
|
|
#ifdef _WIN32
|
|
/* Stop the Windows networking subsystem */
|
|
WSACleanup();
|
|
#endif
|
|
|
|
/* Clean up */
|
|
UA_UNLOCK(&el->elMutex);
|
|
UA_LOCK_DESTROY(&el->elMutex);
|
|
UA_free(el);
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
UA_EventLoop *
|
|
UA_EventLoop_new_POSIX(const UA_Logger *logger) {
|
|
UA_EventLoopPOSIX *el = (UA_EventLoopPOSIX*)
|
|
UA_calloc(1, sizeof(UA_EventLoopPOSIX));
|
|
if(!el)
|
|
return NULL;
|
|
|
|
UA_LOCK_INIT(&el->elMutex);
|
|
UA_Timer_init(&el->timer);
|
|
|
|
#ifdef _WIN32
|
|
/* Start the WSA networking subsystem on Windows */
|
|
WSADATA wsaData;
|
|
WSAStartup(MAKEWORD(2, 2), &wsaData);
|
|
#endif
|
|
|
|
/* Set the public EventLoop content */
|
|
el->eventLoop.logger = logger;
|
|
|
|
el->eventLoop.start = (UA_StatusCode (*)(UA_EventLoop*))UA_EventLoopPOSIX_start;
|
|
el->eventLoop.stop = (void (*)(UA_EventLoop*))UA_EventLoopPOSIX_stop;
|
|
el->eventLoop.run = (UA_StatusCode (*)(UA_EventLoop*, UA_UInt32))UA_EventLoopPOSIX_run;
|
|
el->eventLoop.free = (UA_StatusCode (*)(UA_EventLoop*))UA_EventLoopPOSIX_free;
|
|
|
|
el->eventLoop.dateTime_now = UA_EventLoopPOSIX_DateTime_now;
|
|
el->eventLoop.dateTime_nowMonotonic =
|
|
UA_EventLoopPOSIX_DateTime_nowMonotonic;
|
|
el->eventLoop.dateTime_localTimeUtcOffset =
|
|
UA_EventLoopPOSIX_DateTime_localTimeUtcOffset;
|
|
|
|
el->eventLoop.nextCyclicTime = UA_EventLoopPOSIX_nextCyclicTime;
|
|
el->eventLoop.addCyclicCallback = UA_EventLoopPOSIX_addCyclicCallback;
|
|
el->eventLoop.modifyCyclicCallback = UA_EventLoopPOSIX_modifyCyclicCallback;
|
|
el->eventLoop.removeCyclicCallback = UA_EventLoopPOSIX_removeCyclicCallback;
|
|
el->eventLoop.addTimedCallback = UA_EventLoopPOSIX_addTimedCallback;
|
|
el->eventLoop.addDelayedCallback = UA_EventLoopPOSIX_addDelayedCallback;
|
|
el->eventLoop.removeDelayedCallback = UA_EventLoopPOSIX_removeDelayedCallback;
|
|
|
|
el->eventLoop.registerEventSource =
|
|
(UA_StatusCode (*)(UA_EventLoop*, UA_EventSource*))
|
|
UA_EventLoopPOSIX_registerEventSource;
|
|
el->eventLoop.deregisterEventSource =
|
|
(UA_StatusCode (*)(UA_EventLoop*, UA_EventSource*))
|
|
UA_EventLoopPOSIX_deregisterEventSource;
|
|
|
|
return &el->eventLoop;
|
|
}
|
|
|
|
/* Reusable EventSource functionality */
|
|
|
|
UA_StatusCode
|
|
UA_EventLoopPOSIX_allocNetworkBuffer(UA_ConnectionManager *cm,
|
|
uintptr_t connectionId,
|
|
UA_ByteString *buf,
|
|
size_t bufSize) {
|
|
return UA_ByteString_allocBuffer(buf, bufSize);
|
|
}
|
|
|
|
void
|
|
UA_EventLoopPOSIX_freeNetworkBuffer(UA_ConnectionManager *cm,
|
|
uintptr_t connectionId,
|
|
UA_ByteString *buf) {
|
|
UA_ByteString_clear(buf);
|
|
}
|
|
|
|
UA_StatusCode
|
|
UA_EventLoopPOSIX_allocateRXBuffer(UA_POSIXConnectionManager *pcm) {
|
|
UA_UInt32 rxBufSize = 2u << 16; /* The default is 64kb */
|
|
const UA_UInt32 *configRxBufSize = (const UA_UInt32 *)
|
|
UA_KeyValueMap_getScalar(&pcm->cm.eventSource.params,
|
|
UA_QUALIFIEDNAME(0, "recv-bufsize"),
|
|
&UA_TYPES[UA_TYPES_UINT32]);
|
|
if(configRxBufSize)
|
|
rxBufSize = *configRxBufSize;
|
|
if(pcm->rxBuffer.length != rxBufSize) {
|
|
UA_ByteString_clear(&pcm->rxBuffer);
|
|
return UA_ByteString_allocBuffer(&pcm->rxBuffer, rxBufSize);
|
|
}
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
/* Socket Handling */
|
|
|
|
enum ZIP_CMP
|
|
cmpFD(const UA_FD *a, const UA_FD *b) {
|
|
if(*a == *b)
|
|
return ZIP_CMP_EQ;
|
|
return (*a < *b) ? ZIP_CMP_LESS : ZIP_CMP_MORE;
|
|
}
|
|
|
|
UA_StatusCode
|
|
UA_EventLoopPOSIX_setNonBlocking(UA_FD sockfd) {
|
|
#ifndef _WIN32
|
|
int opts = fcntl(sockfd, F_GETFL);
|
|
if(opts < 0 || fcntl(sockfd, F_SETFL, opts | O_NONBLOCK) < 0)
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
#else
|
|
u_long iMode = 1;
|
|
if(ioctlsocket(sockfd, FIONBIO, &iMode) != NO_ERROR)
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
#endif
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
UA_StatusCode
|
|
UA_EventLoopPOSIX_setNoSigPipe(UA_FD sockfd) {
|
|
#ifdef SO_NOSIGPIPE
|
|
int val = 1;
|
|
int res = UA_setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val));
|
|
if(res < 0)
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
#endif
|
|
return UA_STATUSCODE_GOOD;
|
|
}
|
|
|
|
UA_StatusCode
|
|
UA_EventLoopPOSIX_setReusable(UA_FD sockfd) {
|
|
int enableReuseVal = 1;
|
|
#ifndef _WIN32
|
|
int res = UA_setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
|
|
(const char*)&enableReuseVal, sizeof(enableReuseVal));
|
|
res |= UA_setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,
|
|
(const char*)&enableReuseVal, sizeof(enableReuseVal));
|
|
return (res == 0) ? UA_STATUSCODE_GOOD : UA_STATUSCODE_BADINTERNALERROR;
|
|
#else
|
|
int res = UA_setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
|
|
(const char*)&enableReuseVal, sizeof(enableReuseVal));
|
|
return (res == 0) ? UA_STATUSCODE_GOOD : UA_STATUSCODE_BADINTERNALERROR;
|
|
#endif
|
|
}
|