diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html index 8c9cef1aa..609d05689 100644 --- a/documentation/RELEASE_NOTES.html +++ b/documentation/RELEASE_NOTES.html @@ -14,6 +14,29 @@
The bitwise operators in the CALC engine have been modified to work properly +with values that have bit 31 (0x80000000) set. This modification involved +back-porting some earlier changes from the 3.15 branch, and fixes +Launchpad bug +#1514520.
+ +On process exit, don't try to stop the worker thread that makes DNS lookups +asynchronous. Previously this would wait for any lookups still in progress, +delaying the exit unnecessarily. This was most obvious with catools (eg. +cainfo). +lp:1527636
+ +Simpler versions of the epicsTime_gmtime() and epicsTime_localtime() +routines have been included in the Windows implementations, and a new test +program added. The original versions do not report DST status properly. Fixes +Launchpad bug 1528284.
+Since EPICS Base 3.15.0.2 on Posix OSs the initialization of the epicsThread
diff --git a/src/ioc/rsrv/cast_server.c b/src/ioc/rsrv/cast_server.c
index 2932f2b62..e5f5ad495 100644
--- a/src/ioc/rsrv/cast_server.c
+++ b/src/ioc/rsrv/cast_server.c
@@ -264,19 +264,27 @@ void cast_server(void *pParm)
if(prsrv_cast_client->recv.cnt !=
prsrv_cast_client->recv.stk){
char buf[40];
-
+
ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
epicsPrintf ("CAS: partial (damaged?) UDP msg of %d bytes from %s ?\n",
prsrv_cast_client->recv.cnt-prsrv_cast_client->recv.stk, buf);
+
+ epicsTimeToStrftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S",
+ &prsrv_cast_client->time_at_last_recv);
+ epicsPrintf ("CAS: message received at %s\n", buf);
}
}
else {
char buf[40];
-
+
ipAddrToDottedIP (&prsrv_cast_client->addr, buf, sizeof(buf));
epicsPrintf ("CAS: invalid (damaged?) UDP request from %s ?\n", buf);
+
+ epicsTimeToStrftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S",
+ &prsrv_cast_client->time_at_last_recv);
+ epicsPrintf ("CAS: message received at %s\n", buf);
}
if (CASDEBUG>2) {
diff --git a/src/libCom/calc/calcPerform.c b/src/libCom/calc/calcPerform.c
index 0e8fcd5e9..e53d860e2 100644
--- a/src/libCom/calc/calcPerform.c
+++ b/src/libCom/calc/calcPerform.c
@@ -33,6 +33,10 @@ static int cond_search(const char **ppinst, int match);
#define PI 3.14159265358979323
#endif
+/* Turn off global optimization for 64-bit MSVC builds */
+#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW)
+# pragma optimize("g", off)
+#endif
/* calcPerform
*
@@ -45,6 +49,7 @@ epicsShareFunc long
double *ptop; /* stack pointer */
double top; /* value from top of stack */
epicsInt32 itop; /* integer from top of stack */
+ epicsUInt32 utop; /* unsigned integer from top of stack */
int op;
int nargs;
@@ -262,7 +267,7 @@ epicsShareFunc long
case NINT:
top = *ptop;
- *ptop = (double)(epicsInt32)(top >= 0 ? top + 0.5 : top - 0.5);
+ *ptop = (epicsInt32) (top >= 0 ? top + 0.5 : top - 0.5);
break;
case RANDOM:
@@ -283,34 +288,45 @@ epicsShareFunc long
*ptop = ! *ptop;
break;
+ /* For bitwise operations on values with bit 31 set, double values
+ * must first be cast to unsigned to correctly set that bit; the
+ * double value must be negative in that case. The result must be
+ * cast to a signed integer before converting to the double result.
+ */
+
case BIT_OR:
- itop = (epicsInt32) *ptop--;
- *ptop = (epicsInt32) *ptop | itop;
+ utop = *ptop--;
+ *ptop = (epicsInt32) ((epicsUInt32) *ptop | utop);
break;
case BIT_AND:
- itop = (epicsInt32) *ptop--;
- *ptop = (epicsInt32) *ptop & itop;
+ utop = *ptop--;
+ *ptop = (epicsInt32) ((epicsUInt32) *ptop & utop);
break;
case BIT_EXCL_OR:
- itop = (epicsInt32) *ptop--;
- *ptop = (epicsInt32) *ptop ^ itop;
+ utop = *ptop--;
+ *ptop = (epicsInt32) ((epicsUInt32) *ptop ^ utop);
break;
case BIT_NOT:
- itop = (epicsInt32) *ptop;
- *ptop = ~itop;
+ utop = *ptop;
+ *ptop = (epicsInt32) ~utop;
break;
+ /* The shift operators use signed integers, so a right-shift will
+ * extend the sign bit into the left-hand end of the value. The
+ * double-casting through unsigned here is important, see above.
+ */
+
case RIGHT_SHIFT:
- itop = (epicsInt32) *ptop--;
- *ptop = (epicsInt32) *ptop >> itop;
+ utop = *ptop--;
+ *ptop = ((epicsInt32) (epicsUInt32) *ptop) >> (utop & 31);
break;
case LEFT_SHIFT:
- itop = (epicsInt32) *ptop--;
- *ptop = (epicsInt32) *ptop << itop;
+ utop = *ptop--;
+ *ptop = ((epicsInt32) (epicsUInt32) *ptop) << (utop & 31);
break;
case NOT_EQ:
@@ -367,6 +383,9 @@ epicsShareFunc long
*presult = *ptop;
return 0;
}
+#if defined(_WIN32) && defined(_M_X64) && !defined(_MINGW)
+# pragma optimize("", on)
+#endif
epicsShareFunc long
diff --git a/src/libCom/calc/postfix.c b/src/libCom/calc/postfix.c
index 2a7030b69..2f51549d0 100644
--- a/src/libCom/calc/postfix.c
+++ b/src/libCom/calc/postfix.c
@@ -251,7 +251,7 @@ epicsShareFunc long
goto bad;
}
psrc = pnext;
- lit_i = (int) lit_d;
+ lit_i = (epicsInt32) lit_d;
if (lit_d != (double) lit_i) {
*pout++ = pel->code;
memcpy(pout, &lit_d, sizeof(double));
@@ -272,8 +272,8 @@ epicsShareFunc long
}
psrc = pnext;
*pout++ = LITERAL_INT;
- memcpy(pout, &lit_ui, sizeof(epicsInt32));
- pout += sizeof(epicsInt32);
+ memcpy(pout, &lit_ui, sizeof(epicsUInt32));
+ pout += sizeof(epicsUInt32);
}
operand_needed = FALSE;
diff --git a/src/libCom/misc/ipAddrToAsciiAsynchronous.cpp b/src/libCom/misc/ipAddrToAsciiAsynchronous.cpp
index e0dfd0c4d..cef728f2a 100644
--- a/src/libCom/misc/ipAddrToAsciiAsynchronous.cpp
+++ b/src/libCom/misc/ipAddrToAsciiAsynchronous.cpp
@@ -81,7 +81,6 @@ template class tsFreeList
extern "C" {
static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * );
-static void ipAddrToAsciiEngineShutdownRequest ( void * );
}
// - this class executes the synchronous DNS query
@@ -107,10 +106,7 @@ private:
unsigned cancelPendingCount;
bool exitFlag;
bool callbackInProgress;
- static epicsMutex * pGlobalMutex;
static ipAddrToAsciiEnginePrivate * pEngine;
- static unsigned numberOfReferences;
- static bool shutdownRequest;
ipAddrToAsciiTransaction & createTransaction ();
void release ();
void run ();
@@ -118,15 +114,11 @@ private:
ipAddrToAsciiEnginePrivate & operator = ( const ipAddrToAsciiEngine & );
friend class ipAddrToAsciiEngine;
friend class ipAddrToAsciiTransactionPrivate;
- friend void ipAddrToAsciiEngineShutdownRequest ( void * );
friend void ipAddrToAsciiEngineGlobalMutexConstruct ( void * );
};
-epicsMutex * ipAddrToAsciiEnginePrivate :: pGlobalMutex = 0;
ipAddrToAsciiEnginePrivate * ipAddrToAsciiEnginePrivate :: pEngine = 0;
-unsigned ipAddrToAsciiEnginePrivate :: numberOfReferences = 0u;
-bool ipAddrToAsciiEnginePrivate :: shutdownRequest = false;
-static epicsThreadOnceId ipAddrToAsciiEngineGlobalMutexOnceFlag = 0;
+static epicsThreadOnceId ipAddrToAsciiEngineGlobalMutexOnceFlag = EPICS_THREAD_ONCE_INIT;
// the users are not required to supply a show routine
// for there transaction callback class
@@ -137,26 +129,13 @@ ipAddrToAsciiCallBack::~ipAddrToAsciiCallBack () {}
ipAddrToAsciiTransaction::~ipAddrToAsciiTransaction () {}
ipAddrToAsciiEngine::~ipAddrToAsciiEngine () {}
-static void ipAddrToAsciiEngineShutdownRequest ( void * )
-{
- bool deleteGlobalMutexCondDetected = false;
- {
- epicsGuard < epicsMutex >
- guard ( * ipAddrToAsciiEnginePrivate :: pGlobalMutex );
- ipAddrToAsciiEnginePrivate :: shutdownRequest = true;
- deleteGlobalMutexCondDetected =
- ( ipAddrToAsciiEnginePrivate :: numberOfReferences == 0 );
- }
- if ( deleteGlobalMutexCondDetected ) {
- delete ipAddrToAsciiEnginePrivate :: pGlobalMutex;
- ipAddrToAsciiEnginePrivate :: pGlobalMutex = 0;
- }
-}
-
static void ipAddrToAsciiEngineGlobalMutexConstruct ( void * )
{
- ipAddrToAsciiEnginePrivate :: pGlobalMutex = newEpicsMutex;
- epicsAtExit ( ipAddrToAsciiEngineShutdownRequest, 0 );
+ try {
+ ipAddrToAsciiEnginePrivate::pEngine = new ipAddrToAsciiEnginePrivate ();
+ } catch (std::exception& e) {
+ errlogPrintf("ipAddrToAsciiEnginePrivate ctor fails with: %s\n", e.what());
+ }
}
// for now its probably sufficent to allocate one
@@ -168,21 +147,8 @@ ipAddrToAsciiEngine & ipAddrToAsciiEngine::allocate ()
epicsThreadOnce (
& ipAddrToAsciiEngineGlobalMutexOnceFlag,
ipAddrToAsciiEngineGlobalMutexConstruct, 0 );
- // since we must not own lock when checking this flag
- // this diagnostic has imperfect detection, but never
- // incorrect detection
- if ( ipAddrToAsciiEnginePrivate :: shutdownRequest ) {
- throw std :: runtime_error (
- "ipAddrToAsciiEngine::allocate (): "
- "attempts to create an "
- "ipAddrToAsciiEngine while the exit "
- "handlers are running are rejected");
- }
- epicsGuard < epicsMutex > guard ( * ipAddrToAsciiEnginePrivate::pGlobalMutex );
- if ( ! ipAddrToAsciiEnginePrivate::pEngine ) {
- ipAddrToAsciiEnginePrivate::pEngine = new ipAddrToAsciiEnginePrivate ();
- }
- ipAddrToAsciiEnginePrivate::numberOfReferences++;
+ if(!ipAddrToAsciiEnginePrivate::pEngine)
+ throw std::runtime_error("ipAddrToAsciiEngine::allocate fails");
return * ipAddrToAsciiEnginePrivate::pEngine;
}
@@ -206,32 +172,8 @@ ipAddrToAsciiEnginePrivate::~ipAddrToAsciiEnginePrivate ()
this->thread.exitWait ();
}
-// for now its probably sufficient to allocate one
-// DNS transaction thread for all codes sharing
-// the same process that need DNS services but we
-// leave our options open for the future
void ipAddrToAsciiEnginePrivate::release ()
{
- bool deleteGlobalMutexCondDetected = false;
- epicsThreadOnce (
- & ipAddrToAsciiEngineGlobalMutexOnceFlag,
- ipAddrToAsciiEngineGlobalMutexConstruct, 0 );
- {
- epicsGuard < epicsMutex >
- guard ( * ipAddrToAsciiEnginePrivate::pGlobalMutex );
- assert ( ipAddrToAsciiEnginePrivate::numberOfReferences > 0u );
- ipAddrToAsciiEnginePrivate::numberOfReferences--;
- if ( ipAddrToAsciiEnginePrivate::numberOfReferences == 0u ) {
- deleteGlobalMutexCondDetected =
- ipAddrToAsciiEnginePrivate :: shutdownRequest;
- delete ipAddrToAsciiEnginePrivate :: pEngine;
- ipAddrToAsciiEnginePrivate :: pEngine = 0;
- }
- }
- if ( deleteGlobalMutexCondDetected ) {
- delete ipAddrToAsciiEnginePrivate :: pGlobalMutex;
- ipAddrToAsciiEnginePrivate :: pGlobalMutex = 0;
- }
}
void ipAddrToAsciiEnginePrivate::show ( unsigned level ) const
@@ -365,6 +307,8 @@ ipAddrToAsciiTransactionPrivate::~ipAddrToAsciiTransactionPrivate ()
if ( this->engine.pCurrent == this &&
this->engine.callbackInProgress &&
! this->engine.thread.isCurrentThread() ) {
+ // cancel from another thread while callback in progress
+ // waits for callback to complete
assert ( this->engine.cancelPendingCount < UINT_MAX );
this->engine.cancelPendingCount++;
{
@@ -382,9 +326,11 @@ ipAddrToAsciiTransactionPrivate::~ipAddrToAsciiTransactionPrivate ()
}
else {
if ( this->engine.pCurrent == this ) {
+ // cancel from callback, or while lookup in progress
this->engine.pCurrent = 0;
}
else {
+ // cancel before lookup starts
this->engine.labor.remove ( *this );
}
this->pending = false;
diff --git a/src/libCom/osi/os/WIN32/osdTime.cpp b/src/libCom/osi/os/WIN32/osdTime.cpp
index 67b127200..99f75c195 100644
--- a/src/libCom/osi/os/WIN32/osdTime.cpp
+++ b/src/libCom/osi/os/WIN32/osdTime.cpp
@@ -124,148 +124,31 @@ static int osdTimeGetCurrent ( epicsTimeStamp *pDest )
return epicsTimeOK;
}
-inline void UnixTimeToFileTime ( const time_t * pAnsiTime, LPFILETIME pft )
-{
- LONGLONG ll = Int32x32To64 ( *pAnsiTime, 10000000 ) + 116444736000000000LL;
- pft->dwLowDateTime = static_cast < DWORD > ( ll );
- pft->dwHighDateTime = static_cast < DWORD > ( ll >>32 );
-}
-
-static int daysInMonth[] = { 31, 28, 31, 30, 31, 30, 31,
- 31, 30, 31, 30, 31 };
-
-static bool isLeapYear ( DWORD year )
-{
- if ( (year % 4) == 0 ) {
- return ( ( year % 100 ) != 0 || ( year % 400 ) == 0 );
- } else {
- return false;
- }
-}
-
-static int dayOfYear ( DWORD day, DWORD month, DWORD year )
-{
- DWORD nDays = 0;
- for ( unsigned m = 1; m < month; m++ ) {
- nDays += daysInMonth[m-1];
- if ( m == 2 && isLeapYear(year) ) {
- nDays++;
- }
- }
- return nDays + day;
-}
-
// synthesize a reentrant gmtime on WIN32
int epicsShareAPI epicsTime_gmtime ( const time_t *pAnsiTime, struct tm *pTM )
{
- FILETIME ft;
- UnixTimeToFileTime ( pAnsiTime, &ft );
-
- SYSTEMTIME st;
- BOOL status = FileTimeToSystemTime ( &ft, &st );
- if ( ! status ) {
- return epicsTimeERROR;
+ struct tm * pRet = gmtime ( pAnsiTime );
+ if ( pRet ) {
+ *pTM = *pRet;
+ return epicsTimeOK;
+ }
+ else {
+ return errno;
}
-
- pTM->tm_sec = st.wSecond; // seconds after the minute - [0,59]
- pTM->tm_min = st.wMinute; // minutes after the hour - [0,59]
- pTM->tm_hour = st.wHour; // hours since midnight - [0,23]
- assert ( st.wDay >= 1 && st.wDay <= 31 );
- pTM->tm_mday = st.wDay; // day of the month - [1,31]
- assert ( st.wMonth >= 1 && st.wMonth <= 12 );
- pTM->tm_mon = st.wMonth - 1; // months since January - [0,11]
- assert ( st.wYear >= 1900 );
- pTM->tm_year = st.wYear - 1900; // years since 1900
- pTM->tm_wday = st.wDayOfWeek; // days since Sunday - [0,6]
- pTM->tm_yday = dayOfYear ( st.wDay, st.wMonth, st.wYear ) - 1;
- pTM->tm_isdst = 0;
-
- return epicsTimeOK;
}
// synthesize a reentrant localtime on WIN32
int epicsShareAPI epicsTime_localtime (
const time_t * pAnsiTime, struct tm * pTM )
{
- FILETIME ft;
- UnixTimeToFileTime ( pAnsiTime, & ft );
-
- TIME_ZONE_INFORMATION tzInfo;
- DWORD tzStatus = GetTimeZoneInformation ( & tzInfo );
- if ( tzStatus == TIME_ZONE_ID_INVALID ) {
- return epicsTimeERROR;
+ struct tm * pRet = localtime ( pAnsiTime );
+ if ( pRet ) {
+ *pTM = *pRet;
+ return epicsTimeOK;
}
-
- //
- // There are remarkable weaknesses in the FileTimeToLocalFileTime
- // interface so we don't use it here. Unfortunately, there is no
- // corresponding function that works on file time.
- //
- SYSTEMTIME st;
- BOOL success = FileTimeToSystemTime ( & ft, & st );
- if ( ! success ) {
- return epicsTimeERROR;
+ else {
+ return errno;
}
- SYSTEMTIME lst;
- success = SystemTimeToTzSpecificLocalTime (
- & tzInfo, & st, & lst );
- if ( ! success ) {
- return epicsTimeERROR;
- }
-
- //
- // We must convert back to file time so that we can determine if DST
- // is active...
- //
- FILETIME lft;
- success = SystemTimeToFileTime ( & lst, & lft );
- if ( ! success ) {
- return epicsTimeERROR;
- }
-
- int is_dst = -1; // unknown state of dst
- if ( tzStatus != TIME_ZONE_ID_UNKNOWN &&
- tzInfo.StandardDate.wMonth != 0 &&
- tzInfo.DaylightDate.wMonth != 0) {
- // determine if the specified date is
- // in daylight savings time
- tzInfo.StandardDate.wYear = st.wYear;
- FILETIME StandardDateFT;
- success = SystemTimeToFileTime (
- & tzInfo.StandardDate, & StandardDateFT );
- if ( ! success ) {
- return epicsTimeERROR;
- }
- tzInfo.DaylightDate.wYear = st.wYear;
- FILETIME DaylightDateFT;
- success = SystemTimeToFileTime (
- & tzInfo.DaylightDate, & DaylightDateFT );
- if ( ! success ) {
- return epicsTimeERROR;
- }
- if ( CompareFileTime ( & lft, & DaylightDateFT ) >= 0
- && CompareFileTime ( & lft, & StandardDateFT ) < 0 ) {
- is_dst = 1;
- }
- else {
- is_dst = 0;
- }
- }
-
- pTM->tm_sec = lst.wSecond; // seconds after the minute - [0,59]
- pTM->tm_min = lst.wMinute; // minutes after the hour - [0,59]
- pTM->tm_hour = lst.wHour; // hours since midnight - [0,23]
- assert ( lst.wDay >= 1 && lst.wDay <= 31 );
- pTM->tm_mday = lst.wDay; // day of the month - [1,31]
- assert ( lst.wMonth >= 1 && lst.wMonth <= 12 );
- pTM->tm_mon = lst.wMonth - 1; // months since January - [0,11]
- assert ( lst.wYear >= 1900 );
- pTM->tm_year = lst.wYear - 1900; // years since 1900
- pTM->tm_wday = lst.wDayOfWeek; // days since Sunday - [0,6]
- pTM->tm_yday = dayOfYear ( lst.wDay, lst.wMonth, lst.wYear ) - 1;
- pTM->tm_isdst = is_dst;
-
- return epicsTimeOK;
}
currentTime::currentTime () :
diff --git a/src/libCom/test/Makefile b/src/libCom/test/Makefile
index e0c1b064c..f0359420d 100755
--- a/src/libCom/test/Makefile
+++ b/src/libCom/test/Makefile
@@ -96,6 +96,10 @@ epicsTimeTest_SRCS += epicsTimeTest.cpp
testHarness_SRCS += epicsTimeTest.cpp
TESTS += epicsTimeTest
+TESTPROD_HOST += epicsTimeZoneTest
+epicsTimeZoneTest_SRCS += epicsTimeZoneTest.c
+TESTS += epicsTimeZoneTest
+
TESTPROD_HOST += epicsThreadTest
epicsThreadTest_SRCS += epicsThreadTest.cpp
testHarness_SRCS += epicsThreadTest.cpp
diff --git a/src/libCom/test/epicsCalcTest.cpp b/src/libCom/test/epicsCalcTest.cpp
index 92ec61a37..24e30d417 100644
--- a/src/libCom/test/epicsCalcTest.cpp
+++ b/src/libCom/test/epicsCalcTest.cpp
@@ -8,6 +8,7 @@
// Author: Andrew Johnson
#include "epicsUnitTest.h"
+#include "epicsTypes.h"
#include "epicsMath.h"
#include "epicsAlgorithm.h"
#include "postfix.h"
@@ -38,32 +39,59 @@ void testCalc(const char *expr, double expected) {
/* Evaluate expression, test against expected result */
bool pass = false;
double args[CALCPERFORM_NARGS] = {
- 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0
+ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0
};
char rpn[MAX_POSTFIX_SIZE];
short err;
double result = 0.0;
result /= result; /* Start as NaN */
-
+
if (postfix(expr, rpn, &err)) {
- testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr);
+ testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr);
} else
- if (calcPerform(args, &result, rpn) && finite(result)) {
- testDiag("calcPerform: error evaluating '%s'", expr);
- }
-
+ if (calcPerform(args, &result, rpn) && finite(result)) {
+ testDiag("calcPerform: error evaluating '%s'", expr);
+ }
+
if (finite(expected) && finite(result)) {
- pass = fabs(expected - result) < 1e-8;
+ pass = fabs(expected - result) < 1e-8;
} else if (isnan(expected)) {
- pass = (bool) isnan(result);
+ pass = (bool) isnan(result);
} else {
- pass = (result == expected);
+ pass = (result == expected);
}
if (!testOk(pass, "%s", expr)) {
- testDiag("Expected result is %g, actually got %g", expected, result);
- calcExprDump(rpn);
+ testDiag("Expected result is %g, actually got %g", expected, result);
+ calcExprDump(rpn);
+ }
+}
+
+void testUInt32Calc(const char *expr, epicsUInt32 expected) {
+ /* Evaluate expression, test against expected result */
+ bool pass = false;
+ double args[CALCPERFORM_NARGS] = {
+ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0
+ };
+ char rpn[MAX_POSTFIX_SIZE];
+ short err;
+ epicsUInt32 uresult;
+ double result = 0.0;
+ result /= result; /* Start as NaN */
+
+ if (postfix(expr, rpn, &err)) {
+ testDiag("postfix: %s in expression '%s'", calcErrorStr(err), expr);
+ } else
+ if (calcPerform(args, &result, rpn) && finite(result)) {
+ testDiag("calcPerform: error evaluating '%s'", expr);
+ }
+
+ uresult = (epicsUInt32) result;
+ pass = (uresult == expected);
+ if (!testOk(pass, "%s", expr)) {
+ testDiag("Expected result is 0x%x (%u), actually got 0x%x (%u)",
+ expected, expected, uresult, uresult);
+ calcExprDump(rpn);
}
- return;
}
void testArgs(const char *expr, unsigned long einp, unsigned long eout) {
@@ -238,8 +266,8 @@ MAIN(epicsCalcTest)
const double a=1.0, b=2.0, c=3.0, d=4.0, e=5.0, f=6.0,
g=7.0, h=8.0, i=9.0, j=10.0, k=11.0, l=12.0;
- testPlan(577);
-
+ testPlan(613);
+
/* LITERAL_OPERAND elements */
testExpr(0);
testExpr(1);
@@ -883,7 +911,51 @@ MAIN(epicsCalcTest)
testBadExpr("1?", CALC_ERR_CONDITIONAL);
testBadExpr("1?1", CALC_ERR_CONDITIONAL);
testBadExpr(":1", CALC_ERR_SYNTAX);
-
+
+ // Bit manipulations wrt bit 31 (bug lp:1514520)
+ // using integer literals
+ testUInt32Calc("0xaaaaaaaa AND 0xffff0000", 0xaaaa0000u);
+ testUInt32Calc("0xaaaaaaaa OR 0xffff0000", 0xffffaaaau);
+ testUInt32Calc("0xaaaaaaaa XOR 0xffff0000", 0x5555aaaau);
+ testUInt32Calc("~0xaaaaaaaa", 0x55555555u);
+ testUInt32Calc("~~0xaaaaaaaa", 0xaaaaaaaau);
+ testUInt32Calc("0xaaaaaaaa >> 8", 0xffaaaaaau);
+ testUInt32Calc("0xaaaaaaaa << 8", 0xaaaaaa00u);
+ // using integer literals assigned to variables
+ testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a AND b", 0xaaaa0000u);
+ testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a OR b", 0xffffaaaau);
+ testUInt32Calc("a:=0xaaaaaaaa; b:=0xffff0000; a XOR b", 0x5555aaaau);
+ testUInt32Calc("a:=0xaaaaaaaa; ~a", 0x55555555u);
+ testUInt32Calc("a:=0xaaaaaaaa; ~~a", 0xaaaaaaaau);
+ testUInt32Calc("a:=0xaaaaaaaa; a >> 8", 0xffaaaaaau);
+ testUInt32Calc("a:=0xaaaaaaaa; a << 8", 0xaaaaaa00u);
+
+ // Test proper conversion of double values (+ 0.1 enforces double literal)
+ // when used as inputs to the bitwise operations.
+ // 0xaaaaaaaa = -1431655766 or 2863311530u
+ testUInt32Calc("-1431655766.1 OR 0", 0xaaaaaaaau);
+ testUInt32Calc("2863311530.1 OR 0", 0xaaaaaaaau);
+ testUInt32Calc("0 OR -1431655766.1", 0xaaaaaaaau);
+ testUInt32Calc("0 OR 2863311530.1", 0xaaaaaaaau);
+ testUInt32Calc("-1431655766.1 XOR 0", 0xaaaaaaaau);
+ testUInt32Calc("2863311530.1 XOR 0", 0xaaaaaaaau);
+ testUInt32Calc("0 XOR -1431655766.1", 0xaaaaaaaau);
+ testUInt32Calc("0 XOR 2863311530.1", 0xaaaaaaaau);
+ testUInt32Calc("-1431655766.1 AND 0xffffffff", 0xaaaaaaaau);
+ testUInt32Calc("2863311530.1 AND 0xffffffff", 0xaaaaaaaau);
+ testUInt32Calc("0xffffffff AND -1431655766.1", 0xaaaaaaaau);
+ testUInt32Calc("0xffffffff AND 2863311530.1", 0xaaaaaaaau);
+ testUInt32Calc("~ -1431655766.1", 0x55555555u);
+ testUInt32Calc("~ 2863311530.1", 0x55555555u);
+ testUInt32Calc("-1431655766.1 >> 0", 0xaaaaaaaau);
+ testUInt32Calc("2863311530.1 >> 0", 0xaaaaaaaau);
+ testUInt32Calc("-1431655766.1 >> 0.1", 0xaaaaaaaau);
+ testUInt32Calc("2863311530.1 >> 0.1", 0xaaaaaaaau);
+ testUInt32Calc("-1431655766.1 << 0", 0xaaaaaaaau);
+ testUInt32Calc("2863311530.1 << 0", 0xaaaaaaaau);
+ testUInt32Calc("-1431655766.1 << 0.1", 0xaaaaaaaau);
+ testUInt32Calc("2863311530.1 << 0.1", 0xaaaaaaaau);
+
return testDone();
}
diff --git a/src/libCom/test/epicsTimeZoneTest.c b/src/libCom/test/epicsTimeZoneTest.c
new file mode 100644
index 000000000..dd12b1acf
--- /dev/null
+++ b/src/libCom/test/epicsTimeZoneTest.c
@@ -0,0 +1,117 @@
+/*************************************************************************\
+* Copyright (c) 2015 Michael Davidsaver
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#include