Merged changes from Base-3.14, to revno 12613
This commit is contained in:
@@ -14,6 +14,29 @@
|
||||
<h2 align="center">Changes between 3.15.3 and 3.15.4</h2>
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h3>CALC engine bitwise operator fixes</h3>
|
||||
|
||||
<p>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
|
||||
<a href="https://code.launchpad.net/bugs/1514520">Launchpad bug
|
||||
#1514520</a>.</p>
|
||||
|
||||
<h3>Fix <tt>ipAddrToAsciiAsync()</tt>: don't try to join the daemon thread</h3>
|
||||
|
||||
<p>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).
|
||||
<a href="https://bugs.launchpad.net/epics-base/+bug/1527636">lp:1527636</a></p>
|
||||
|
||||
<h3>Fix epicsTime_localtime() on Windows</h3>
|
||||
|
||||
<p>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
|
||||
<a href="https://bugs.launchpad.net/bugs/1528284">Launchpad bug 1528284</a>.</p>
|
||||
|
||||
<h3>Moved <tt>mlockall()</tt> into its own epicsThread routine</h3>
|
||||
|
||||
<p>Since EPICS Base 3.15.0.2 on Posix OSs the initialization of the epicsThread
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 () :
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
117
src/libCom/test/epicsTimeZoneTest.c
Normal file
117
src/libCom/test/epicsTimeZoneTest.c
Normal file
@@ -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 <stdio.h>
|
||||
|
||||
#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();
|
||||
}
|
||||
@@ -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)" > $@
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user