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 @@

Changes between 3.15.3 and 3.15.4

+

CALC engine bitwise operator fixes

+ +

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.

+ +

Fix ipAddrToAsciiAsync(): don't try to join the daemon thread

+ +

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

+ +

Fix epicsTime_localtime() on Windows

+ +

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.

+

Moved mlockall() into its own epicsThread routine

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 + +#include "envDefs.h" +#include "epicsTime.h" +#include "epicsUnitTest.h" + +#include "testMain.h" + +#if defined(_WIN32) +# define tzset _tzset +#endif + +static +void setTZ(const char *base, const char *dst, int offset) +{ + char tz[20]; + if(offset!=0 || dst) + sprintf(tz, "%s%d%s", base, offset/3600, dst); + else + sprintf(tz, "%s", base); + testDiag("TZ=\"%s\"", tz); + + epicsEnvSet("TZ", tz); + tzset(); +} + +static +void test_localtime(time_t T, int sec, int min, int hour, + int mday, int mon, int year, + int wday, int yday, int isdst) +{ + struct tm B; + testDiag("test_localtime(%ld, ...)", (long)T); + if(epicsTime_localtime(&T, &B)!=epicsTimeOK) { + testFail("epicsTime_localtime() error"); + testSkip(9, "epicsTime_localtime() failed"); + } else { + B.tm_year += 1900; /* for readability */ + testPass("epicsTime_localtime() success"); +#define TEST(FLD) testOk(B.tm_##FLD==FLD, "%s %d==%d", #FLD, B.tm_##FLD, FLD) + TEST(sec); + TEST(min); + TEST(hour); + TEST(mday); + TEST(mon); + TEST(year); + TEST(wday); + TEST(yday); + TEST(isdst); +#undef TEST + } +} + +static +void test_gmtime(time_t T, int sec, int min, int hour, + int mday, int mon, int year, + int wday, int yday, int isdst) +{ + struct tm B; + testDiag("test_gmtime(%ld, ...)", (long)T); + if(epicsTime_gmtime(&T, &B)!=epicsTimeOK) { + testFail("epicsTime_localtime() error"); + testSkip(9, "epicsTime_localtime() failed"); + } else { + B.tm_year += 1900; /* for readability */ + testPass("epicsTime_localtime() success"); +#define TEST(FLD) testOk(B.tm_##FLD==FLD, "%s %d==%d", #FLD, B.tm_##FLD, FLD) + TEST(sec); + TEST(min); + TEST(hour); + TEST(mday); + TEST(mon); + TEST(year); + TEST(wday); + TEST(yday); + TEST(isdst); +#undef TEST + } +} + +MAIN(epicsTimeZoneTest) +{ + testPlan(80); + /* 1445259616 + * Mon Oct 19 09:00:16 2015 EDT + * Mon Oct 19 08:00:16 2015 CDT + * Mon Oct 19 13:00:16 2015 UTC + */ + testDiag("POSIX 1445259616"); + setTZ("EST", "EDT", 5*3600); + test_localtime(1445259616ul, 16, 0, 9, 19, 9, 2015, 1, 291, 1); + setTZ("CST", "CDT", 6*3600); + test_localtime(1445259616ul, 16, 0, 8, 19, 9, 2015, 1, 291, 1); + setTZ("UTC", NULL, 0); + test_localtime(1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); + test_gmtime(1445259616ul, 16, 0, 13, 19, 9, 2015, 1, 291, 0); + /* 1421244931 + * Wed Jan 14 09:15:31 2015 EST + * Wed Jan 14 08:15:31 2015 CST + * Wed Jan 14 14:15:31 2015 UTC + */ + testDiag("POSIX 1421244931"); + setTZ("EST", "EDT", 5*3600); + test_localtime(1421244931ul, 31, 15, 9, 14, 0, 2015, 3, 13, 0); + setTZ("CST", "CDT", 6*3600); + test_localtime(1421244931ul, 31, 15, 8, 14, 0, 2015, 3, 13, 0); + setTZ("UTC", NULL, 0); + test_localtime(1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); + test_gmtime(1421244931ul, 31, 15, 14, 14, 0, 2015, 3, 13, 0); + return testDone(); +} diff --git a/src/std/softIoc/RULES b/src/std/softIoc/RULES index 72492016e..cb540ad3c 100644 --- a/src/std/softIoc/RULES +++ b/src/std/softIoc/RULES @@ -17,5 +17,5 @@ softMain$(DEP): epicsInstallDir.h epicsInstallDir.h: $(ECHO) "FINAL_LOCATION=$(FINAL_LOCATION)" - $(PERL) $(STDDIR)/softIoc/makeInstallDir.pl '$(FINAL_LOCATION)' > $@ + $(PERL) $(STDDIR)/softIoc/makeInstallDir.pl "$(FINAL_LOCATION)" > $@ diff --git a/src/std/softIoc/makeInstallDir.pl b/src/std/softIoc/makeInstallDir.pl index 96c305711..61f271f15 100644 --- a/src/std/softIoc/makeInstallDir.pl +++ b/src/std/softIoc/makeInstallDir.pl @@ -9,7 +9,8 @@ eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*- use strict; -die "Path to INSTALL_LOCATION missing\n" unless @ARGV == 1; +die "$0: Argument missing, INSTALL_LOCATION\n" if @ARGV == 0; +die "$0: Too many arguments, expecting one\n" unless @ARGV == 1; my $path = shift; diff --git a/src/template/ext/top/configure/CONFIG b/src/template/ext/top/configure/CONFIG index ab213ce52..321f6cea2 100644 --- a/src/template/ext/top/configure/CONFIG +++ b/src/template/ext/top/configure/CONFIG @@ -1,48 +1,43 @@ -# $Revision-Id$ +# CONFIG - Load build configuration data +# +# Do not make changes in this file, any site-specific +# overrides should be given in a CONFIG_SITE file. -# You might want to change this to some shared set of rules, e.g. -# RULES=/path/to/epics/support/modules/rules/x-y -RULES=$(EPICS_BASE) +# Where the build rules come from +RULES = $(EPICS_BASE) INSTALL_IDLFILE = $(INSTALL) include $(TOP)/configure/RELEASE -include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH) -include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).Common + ifdef T_A --include $(TOP)/configure/RELEASE.Common.$(T_A) --include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) + -include $(TOP)/configure/RELEASE.Common.$(T_A) + -include $(TOP)/configure/RELEASE.$(EPICS_HOST_ARCH).$(T_A) endif -CONFIG=$(RULES)/configure +CONFIG = $(RULES)/configure include $(CONFIG)/CONFIG -# Override for definition in base +# Override some Base definitions INSTALL_LOCATION = $(TOP) + +# CONFIG_SITE files contain build configuration overrides include $(TOP)/configure/CONFIG_SITE -ifdef INSTALL_LOCATION_EXTENSIONS -INSTALL_LOCATION = $(INSTALL_LOCATION_EXTENSIONS) -endif - -# Site specific host architecture definitions +# Host-arch specific settings -include $(TOP)/configure/os/CONFIG_SITE.$(EPICS_HOST_ARCH).Common +ifdef INSTALL_LOCATION_EXTENSIONS + INSTALL_LOCATION = $(INSTALL_LOCATION_EXTENSIONS) +endif + ifdef T_A + # Target-arch specific settings + -include $(TOP)/configure/os/CONFIG_SITE.Common.$(T_A) -# Site specific target architecture definitions --include $(TOP)/configure/os/CONFIG_SITE.Common.$(T_A) - -# Cross compile specific definitions -ifneq ($(EPICS_HOST_ARCH),$(T_A)) --include $(TOP)/configure/CONFIG.CrossCommon -endif - -# Site specific host-target combination definitions --include $(TOP)/configure/os/CONFIG.$(EPICS_HOST_ARCH).$(T_A) --include $(TOP)/configure/os/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) - --include $(TOP)/configure/O.$(T_A)/CONFIG_APP_INCLUDE - + # Host & target specific combination settings + -include $(TOP)/configure/os/CONFIG_SITE.$(EPICS_HOST_ARCH).$(T_A) endif diff --git a/src/template/ext/top/configure/RELEASE b/src/template/ext/top/configure/RELEASE index cb4c9cce2..2a7ad2714 100644 --- a/src/template/ext/top/configure/RELEASE +++ b/src/template/ext/top/configure/RELEASE @@ -1,22 +1,23 @@ -#RELEASE Location of external products +# RELEASE Locations of external modules # # NOTE: The build does not check dependancies on files # external to this application. Thus you should run # "gnumake clean uninstall install" in the top directory -# each time EPICS_BASE, SNCSEQ, or any other external -# module defined in a RELEASE* file is rebuilt. +# each time EPICS_BASE or any other external module that +# is defined in a RELEASE* file gets rebuilt. # -# Host/target specific settings can be specified in files named -# RELEASE.$(EPICS_HOST_ARCH).Common -# RELEASE.Common.$(T_A) -# RELEASE.$(EPICS_HOST_ARCH).$(T_A) +# Host/target specific paths can be specified in files named +# RELEASE.$(EPICS_HOST_ARCH).Common +# RELEASE.Common.$(T_A) +# RELEASE.$(EPICS_HOST_ARCH).$(T_A) # Define INSTALL_LOCATION in CONFIG_SITE -# Location of external products -EPICS_BASE=_EPICS_BASE_ EPICS_EXTENSIONS = $(TOP) -# OAG_APPS may be needed by extension SDDS -#OAG_APPS=$(TOP)/../../oag/apps +# Locations of external modules +# OAG_APPS may be needed by the SDDS extension +#OAG_APPS = $(TOP)/../../oag/apps + +EPICS_BASE = _EPICS_BASE_ diff --git a/src/template/ext/top/configure/RULES b/src/template/ext/top/configure/RULES index f92e647af..87f23cc85 100644 --- a/src/template/ext/top/configure/RULES +++ b/src/template/ext/top/configure/RULES @@ -1,8 +1,9 @@ # $Revision-Id$ include $(CONFIG)/RULES -include $(TOP)/configure/RULES_PYTHON -include $(TOP)/configure/RULES_IDL +-include $(TOP)/configure/RULES_PYTHON +-include $(TOP)/configure/RULES_IDL + ifdef BASE_3_15 -include $(TOP)/configure/RULES_JAVA + -include $(TOP)/configure/RULES_JAVA endif diff --git a/src/template/ext/top/configure/RULES_PYTHON b/src/template/ext/top/configure/RULES_PYTHON index c8522e01e..41bdc9e65 100644 --- a/src/template/ext/top/configure/RULES_PYTHON +++ b/src/template/ext/top/configure/RULES_PYTHON @@ -43,7 +43,11 @@ $(PYTHON_PACKAGE_PTH): %_wrap.c: ../%.i $(SWIG) -python -o $@ $< +ifdef BASE_3_15 +clean: +else clean:: +endif @$(RM) *.py *.so *.pth endif