diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html
index 78964d5ee..20ff8f460 100644
--- a/documentation/RELEASE_NOTES.html
+++ b/documentation/RELEASE_NOTES.html
@@ -13,6 +13,16 @@
+
Extend maximum Posix epicsEventWaitWithTimeout() delay
+
+The Posix implementation of epicsEventWaitWithTimeout() was limiting the
+timeout delay to at most 60 minutes (3600.0 seconds). This has been changed to
+10 years; significantly longer maximum delays cause problems on systems where
+time_t is still a signed 32-bit integer so cannot represent absolute
+time-stamps after 2038-01-19. Our assumption is that such 32-bit systems will
+have been retired before the year 2028, but some additional tests have been
+added to the epicsTimeTest program to detect and fail if this assumption is
+violated.
New test-related make targets
diff --git a/src/libCom/osi/os/Darwin/osdTime.cpp b/src/libCom/osi/os/Darwin/osdTime.cpp
index 03c549cad..17e9cead9 100644
--- a/src/libCom/osi/os/Darwin/osdTime.cpp
+++ b/src/libCom/osi/os/Darwin/osdTime.cpp
@@ -67,10 +67,12 @@ convertDoubleToWakeTime(double timeout, struct timespec *wakeTime)
mach_timespec_t now;
struct timespec wait;
- clock_get_time(host_clock, &now);
-
if (timeout < 0.0)
timeout = 0.0;
+ else if (timeout > 60 * 60 * 24 * 3652.5)
+ timeout = 60 * 60 * 24 * 3652.5; /* 10 years */
+
+ clock_get_time(host_clock, &now);
wait.tv_sec = static_cast< time_t >(timeout);
wait.tv_nsec = static_cast< long >((timeout - (double)wait.tv_sec) * 1e9);
diff --git a/src/libCom/osi/os/posix/osdTime.cpp b/src/libCom/osi/os/posix/osdTime.cpp
index b0d288969..82b464937 100644
--- a/src/libCom/osi/os/posix/osdTime.cpp
+++ b/src/libCom/osi/os/posix/osdTime.cpp
@@ -87,30 +87,35 @@ int epicsTime_localtime ( const time_t *clock, // X aCC 361
extern "C" epicsShareFunc void
convertDoubleToWakeTime(double timeout,struct timespec *wakeTime)
{
- struct timespec wait;
+ struct timespec now, wait;
int status;
if (timeout < 0.0)
timeout = 0.0;
+ else if (timeout > 60 * 60 * 24 * 3652.5)
+ timeout = 60 * 60 * 24 * 3652.5; /* 10 years */
+
#ifdef CLOCK_REALTIME
- status = clock_gettime(CLOCK_REALTIME, wakeTime);
+ status = clock_gettime(CLOCK_REALTIME, &now);
#else
{
struct timeval tv;
struct timezone tz;
status = gettimeofday(&tv, &tz);
- wakeTime->tv_sec = tv.tv_sec;
- wakeTime->tv_nsec = tv.tv_usec * 1000;
+ now.tv_sec = tv.tv_sec;
+ now.tv_nsec = tv.tv_usec * 1000;
}
#endif
if (status) {
perror("convertDoubleToWakeTime");
cantProceed("convertDoubleToWakeTime");
}
+
wait.tv_sec = static_cast< time_t >(timeout);
wait.tv_nsec = static_cast< long >((timeout - (double)wait.tv_sec) * 1e9);
- wakeTime->tv_sec += wait.tv_sec;
- wakeTime->tv_nsec += wait.tv_nsec;
+
+ wakeTime->tv_sec = now.tv_sec + wait.tv_sec;
+ wakeTime->tv_nsec = now.tv_nsec + wait.tv_nsec;
if (wakeTime->tv_nsec >= 1000000000L) {
wakeTime->tv_nsec -= 1000000000L;
++wakeTime->tv_sec;
diff --git a/src/libCom/test/epicsTimeTest.cpp b/src/libCom/test/epicsTimeTest.cpp
index 823f57b6f..110ae21a4 100644
--- a/src/libCom/test/epicsTimeTest.cpp
+++ b/src/libCom/test/epicsTimeTest.cpp
@@ -48,7 +48,7 @@ MAIN(epicsTimeTest)
const int wasteTime = 100000;
const int nTimes = 10;
- testPlan(15 + nTimes * 19);
+ testPlan(17 + nTimes * 19);
try {
const epicsTimeStamp epochTS = {0, 0};
@@ -216,5 +216,35 @@ MAIN(epicsTimeTest)
testOk1(beginTS + diff == now);
}
+ epicsTime ten_years_hence;
+ try {
+ now = epicsTime::getCurrent();
+ ten_years_hence = now + 60 * 60 * 24 * 3652.5;
+ testPass("epicsTime can represent 10 years hence");
+ }
+ catch ( ... ) {
+ testFail("epicsTime exception for value 10 years hence");
+ }
+
+ try {
+ /* This test exists because in libCom/osi/os/posix/osdTime.cpp
+ * the convertDoubleToWakeTime() routine limits the timeout delay
+ * to 10 years. libCom/timer/timerQueue.cpp returns DBL_MAX for
+ * queues with no timers present, and convertDoubleToWakeTime()
+ * has to return an absolute Posix timestamp. On 2028-01-19 any
+ * systems that still implement time_t as a signed 32-bit integer
+ * will be unable to represent that timestamp, so this will fail.
+ */
+ time_t_wrapper os_time_t = ten_years_hence;
+ epicsTime then = os_time_t; // No fractional seconds
+ double delta = ten_years_hence - then;
+
+ testOk(delta >= 0 && delta < 1.0,
+ "OS time_t can represent 10 years hence");
+ }
+ catch ( ... ) {
+ testFail("OS time_t conversion exception for value 10 years hence");
+ }
+
return testDone();
}