From cc18714f2d547e03cbce7cb01b16562e33ee0ef8 Mon Sep 17 00:00:00 2001 From: Julius Pfrommer Date: Wed, 30 Aug 2023 14:23:17 +0200 Subject: [PATCH] feat(el): Allow setting custom clock sources for each EventLoop --- arch/eventloop_posix.c | 52 ++++++++++++++++++++++++++-- arch/eventloop_posix.h | 6 ++++ include/open62541/plugin/eventloop.h | 42 +++++++++++++++------- 3 files changed, 84 insertions(+), 16 deletions(-) diff --git a/arch/eventloop_posix.c b/arch/eventloop_posix.c index dfe84bca628..b3e63599d33 100644 --- a/arch/eventloop_posix.c +++ b/arch/eventloop_posix.c @@ -134,6 +134,34 @@ UA_EventLoopPOSIX_start(UA_EventLoopPOSIX *el) { UA_LOG_INFO(el->eventLoop.logger, UA_LOGCATEGORY_EVENTLOOP, "Starting the EventLoop"); + /* Setting the clock source */ + const UA_Int32 *cs = (const UA_Int32*) + UA_KeyValueMap_getScalar(&el->eventLoop.params, + UA_QUALIFIEDNAME(0, "clock-source"), + &UA_TYPES[UA_TYPES_INT32]); + const UA_Int32 *csm = (const UA_Int32*) + UA_KeyValueMap_getScalar(&el->eventLoop.params, + UA_QUALIFIEDNAME(0, "clock-source-monotonic"), + &UA_TYPES[UA_TYPES_INT32]); +#if defined(UA_ARCHITECTURE_POSIX) && !defined(__APPLE__) && !defined(__MACH__) + el->clockSource = CLOCK_REALTIME; + if(cs) + el->clockSource = *cs; + +# ifdef CLOCK_MONOTONIC_RAW + el->clockSourceMonotonic = CLOCK_MONOTONIC_RAW; +# else + el->clockSourceMonotonic = CLOCK_MONOTONIC; +# endif + if(csm) + el->clockSourceMonotonic = *csm; +#else + if(cs || csm) { + UA_LOG_WARNING(el->eventLoop.logger, UA_LOGCATEGORY_NETWORK, + "Eventloop\t| Cannot set a custom clock source"); + } +#endif + #ifdef UA_HAVE_EPOLL el->epollfd = epoll_create1(0); if(el->epollfd == -1) { @@ -366,21 +394,39 @@ UA_EventLoopPOSIX_deregisterEventSource(UA_EventLoopPOSIX *el, /* 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) { +#if defined(UA_ARCHITECTURE_POSIX) && !defined(__APPLE__) && !defined(__MACH__) + UA_EventLoopPOSIX *pel = (UA_EventLoopPOSIX*)el; + struct timespec ts; + int res = clock_gettime(pel->clockSource, &ts); + if(UA_UNLIKELY(res != 0)) + return 0; + return (ts.tv_sec * UA_DATETIME_SEC) + (ts.tv_nsec / 100) + UA_DATETIME_UNIX_EPOCH; +#else return UA_DateTime_now(); +#endif } static UA_DateTime UA_EventLoopPOSIX_DateTime_nowMonotonic(UA_EventLoop *el) { +#if defined(UA_ARCHITECTURE_POSIX) && !defined(__APPLE__) && !defined(__MACH__) + UA_EventLoopPOSIX *pel = (UA_EventLoopPOSIX*)el; + struct timespec ts; + int res = clock_gettime(pel->clockSourceMonotonic, &ts); + if(UA_UNLIKELY(res != 0)) + return 0; + /* Also add the unix epoch for the monotonic clock. So we get a "normal" + * output when a "normal" source is configured. */ + return (ts.tv_sec * UA_DATETIME_SEC) + (ts.tv_nsec / 100) + UA_DATETIME_UNIX_EPOCH; +#else return UA_DateTime_nowMonotonic(); +#endif } static UA_Int64 UA_EventLoopPOSIX_DateTime_localTimeUtcOffset(UA_EventLoop *el) { + /* TODO: Fix for custom clock sources */ return UA_DateTime_localTimeUtcOffset(); } diff --git a/arch/eventloop_posix.h b/arch/eventloop_posix.h index 4c296b29e95..7b296805336 100644 --- a/arch/eventloop_posix.h +++ b/arch/eventloop_posix.h @@ -109,6 +109,12 @@ typedef struct { * "run" method */ UA_Boolean executing; +#if defined(UA_ARCHITECTURE_POSIX) && !defined(__APPLE__) && !defined(__MACH__) + /* Clocks for the eventloop's time domain */ + UA_Int32 clockSource; + UA_Int32 clockSourceMonotonic; +#endif + #if defined(UA_HAVE_EPOLL) UA_FD epollfd; #else diff --git a/include/open62541/plugin/eventloop.h b/include/open62541/plugin/eventloop.h index e14326f8468..6bcea314d09 100644 --- a/include/open62541/plugin/eventloop.h +++ b/include/open62541/plugin/eventloop.h @@ -125,18 +125,25 @@ struct UA_EventLoop { /* EventLoop Time Domain * ~~~~~~~~~~~~~~~~~~~~~ * Each EventLoop instance can manage its own time domain. This affects the - * execution of timed/cyclic callbacks and time-based sending of network - * packets (if this is implemented). Managing independent time domains is - * important when different parts of a system a synchronized to different - * external (network-wide) clocks. + * execution of timed callbacks and time-based sending of network packets. + * Managing independent time domains is important when different parts of + * the same system are synchronized to different external master clocks. + * + * Each EventLoop uses a "normal" and a "monotonic" clock. The monotonic + * clock does not (necessarily) conform to the current wallclock date. But + * its time intervals are more precise. So it is used for all internally + * scheduled events of the EventLoop (e.g. timed callbacks and time-based + * sending of network packets). The normal and monotonic clock sources can + * be configured via parameters before starting the EventLoop. See the + * architecture-specific documentation for that. * * Note that the logger configured in the EventLoop generates timestamps - * internally as well. If the logger uses a different time domain than the + * independently. If the logger uses a different time domain than the * EventLoop, discrepancies may appear in the logs. * - * The time domain of the EventLoop is exposed via the following functons. - * See `open62541/types.h` for the documentation of their equivalent - * globally defined functions. */ + * The EventLoop clocks can be read via the following functons. See + * `open62541/types.h` for the documentation of their equivalent globally + * defined functions. */ UA_DateTime (*dateTime_now)(UA_EventLoop *el); UA_DateTime (*dateTime_nowMonotonic)(UA_EventLoop *el); @@ -420,13 +427,22 @@ struct UA_InterruptManager { (*deregisterInterrupt)(UA_InterruptManager *im, uintptr_t interruptHandle); }; +#if defined(UA_ARCHITECTURE_POSIX) || defined(UA_ARCHITECTURE_WIN32) + /** - * POSIX-Specific Implementation + * POSIX EventLop Implementation * ----------------------------- - * The POSIX compatibility of WIN32 is 'close enough'. So a joint implementation - * is provided. */ - -#if defined(UA_ARCHITECTURE_POSIX) || defined(UA_ARCHITECTURE_WIN32) + * The POSIX compatibility of Win32 is 'close enough'. So a joint implementation + * is provided. + * + * Configuration parameters (only Linux and BSDs, must be set before start to + * take effect): + * - 0:clock-source [int32]: Clock source (default: CLOCK_REALTIME). + * - 0:clock-source-monotonic [int32]: Clock source used for time intervals. A + * non-monotonic source can be used as well. But expect accordingly longer + * sleep-times for timed events when the clock is set to the past. See the + * man-page of "clock_gettime" on how to get a clock source id for a + * character-device such as /dev/ptp0. (default: CLOCK_MONOTONIC_RAW)*/ UA_EXPORT UA_EventLoop * UA_EventLoop_new_POSIX(const UA_Logger *logger);