diff --git a/configure/os/CONFIG.win32-x86.win32-x86 b/configure/os/CONFIG.win32-x86.win32-x86 index a8115a714..767f4c7fa 100644 --- a/configure/os/CONFIG.win32-x86.win32-x86 +++ b/configure/os/CONFIG.win32-x86.win32-x86 @@ -49,9 +49,9 @@ OPT_CFLAGS_YES_NO = -Ox -Oy- OPT_CFLAGS_YES = $(OPT_CFLAGS_YES_$(OPT_WHOLE_PROGRAM)) # -# -Zi generate program database for debugging information +# -Z7 generate C7 compatible debugging information (inside .obj) # -RTCsu enable run-time error checks -OPT_CFLAGS_NO = -Zi -RTCsu +OPT_CFLAGS_NO = -Z7 -RTCsu # specify object file name and location OBJ_CFLAG = -Fo @@ -119,9 +119,9 @@ OPT_CXXFLAGS_YES_NO = -Ox -Oy- OPT_CXXFLAGS_YES = $(OPT_CXXFLAGS_YES_$(OPT_WHOLE_PROGRAM)) # -# -Zi generate program database for debugging information +# -Z7 generate C7 compatible debugging information (inside .obj) # -RTCsu enable run-time error checks -OPT_CXXFLAGS_NO = -RTCsu -Zi +OPT_CXXFLAGS_NO = -RTCsu -Z7 # specify object file name and location OBJ_CXXFLAG = -Fo @@ -143,20 +143,6 @@ STATIC_LDLIBS_NO= STATIC_LDFLAGS= RANLIB= -# -# option needed for parallel builds with Visual Studio 2013 onward -# VS2012 and above have VisualStudioVersion, so just need to exclude 2012 (11.0) -# -FS Force Synchronous PDB Writes -# -ifneq ($(VisualStudioVersion),) -ifneq ($(VisualStudioVersion),11.0) - OPT_CXXFLAGS_NO += -FS - OPT_CFLAGS_NO += -FS -endif -endif - - -# # add -profile here to run the ms profiler # -LTCG whole program optimization # -incremental:no full linking diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index ad3995dea..6c6699e7a 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -1805,6 +1805,15 @@ header and removed the need for dbScan.c to reach into the internals of its ## Changes made on the 3.15 branch since 3.15.8 +### Use waitable timers on Microsoft Windows + +The `epicsEventWaitWithTimeout` and `epicsThreadSleep` functions have +been changed to use waitable timers. On Windows 10 version 1803 or higher +they will use high resolution timers for more consistent timing. + +See https://groups.google.com/a/chromium.org/g/scheduler-dev/c/0GlSPYreJeY +for a comparison of the performance of different timers. + ### Change to the `junitfiles` self-test build target The names of the generated junit xml test output files have been changed diff --git a/modules/database/src/ioc/db/dbAccess.c b/modules/database/src/ioc/db/dbAccess.c index 91093b546..8aac52f00 100644 --- a/modules/database/src/ioc/db/dbAccess.c +++ b/modules/database/src/ioc/db/dbAccess.c @@ -1376,7 +1376,8 @@ long dbPut(DBADDR *paddr, short dbrType, /* Always do special processing if needed */ if (special) { long status2 = dbPutSpecial(paddr, 1); - if (status2) goto done; + if (status2) + status = status2; } if (status) goto done; diff --git a/modules/libcom/src/osi/os/WIN32/osdEvent.c b/modules/libcom/src/osi/os/WIN32/osdEvent.c index 996dbd3be..5fbf50da6 100644 --- a/modules/libcom/src/osi/os/WIN32/osdEvent.c +++ b/modules/libcom/src/osi/os/WIN32/osdEvent.c @@ -26,6 +26,8 @@ #include "libComAPI.h" #include "epicsEvent.h" +#include "osdThreadPvt.h" + typedef struct epicsEventOSD { HANDLE handle; } epicsEventOSD; @@ -90,27 +92,37 @@ LIBCOM_API epicsEventStatus epicsEventWait ( epicsEventId pSem ) LIBCOM_API epicsEventStatus epicsEventWaitWithTimeout ( epicsEventId pSem, double timeOut ) { - static const unsigned mSecPerSec = 1000; + static const unsigned nSec100PerSec = 10000000u; + HANDLE handles[2]; DWORD status; - DWORD tmo; + LARGE_INTEGER tmo; + HANDLE timer; if ( timeOut <= 0.0 ) { - tmo = 0u; - } - else if ( timeOut >= INFINITE / mSecPerSec ) { - tmo = INFINITE - 1; + tmo.QuadPart = 0u; } else { - tmo = ( DWORD ) ( ( timeOut * mSecPerSec ) + 0.5 ); - if ( tmo == 0 ) { - tmo = 1; + tmo.QuadPart = -((LONGLONG)(timeOut * nSec100PerSec + 0.5)); + } + + if (tmo.QuadPart < 0) { + timer = osdThreadGetTimer(); + if (!SetWaitableTimer(timer, &tmo, 0, NULL, NULL, 0)) { + return epicsEventError; } + handles[0] = pSem->handle; + handles[1] = timer; + status = WaitForMultipleObjects (2, handles, FALSE, INFINITE); + } + else { + status = WaitForSingleObject(pSem->handle, 0); } - status = WaitForSingleObject ( pSem->handle, tmo ); if ( status == WAIT_OBJECT_0 ) { return epicsEventOK; } - else if ( status == WAIT_TIMEOUT ) { + else if ( status == WAIT_OBJECT_0 + 1 || status == WAIT_TIMEOUT ) { + /* WaitForMultipleObjects will trigger WAIT_OBJECT_0 + 1, + WaitForSingleObject will trigger WAIT_TIMEOUT */ return epicsEventWaitTimeout; } else { diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c index 6c00be4ca..462c03f8f 100644 --- a/modules/libcom/src/osi/os/WIN32/osdThread.c +++ b/modules/libcom/src/osi/os/WIN32/osdThread.c @@ -19,9 +19,6 @@ #define VC_EXTRALEAN #define STRICT -#ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x400 /* No support for W95 */ -#endif #include #include /* for _endthread() etc */ @@ -33,6 +30,7 @@ #include "ellLib.h" #include "epicsExit.h" #include "epicsAtomic.h" +#include "osdThreadPvt.h" LIBCOM_API void osdThreadHooksRun(epicsThreadId id); @@ -55,6 +53,7 @@ typedef struct epicsThreadOSD { unsigned epicsPriority; char isSuspended; int joinable; + HANDLE timer; /* waitable timer */ } win32ThreadParam; typedef struct epicsThreadPrivateOSD { @@ -221,6 +220,19 @@ static win32ThreadGlobal * fetchWin32ThreadGlobal ( void ) return pWin32ThreadGlobal; } +static void epicsParmCleanupDataWIN32 ( win32ThreadParam * pParm ) +{ + if ( pParm ) { + if ( pParm->handle ) { + CloseHandle ( pParm->handle ); + } + if ( pParm->timer ) { + CloseHandle ( pParm->timer ); + } + free ( pParm ); + } +} + static void epicsParmCleanupWIN32 ( win32ThreadParam * pParm ) { win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); @@ -239,8 +251,7 @@ static void epicsParmCleanupWIN32 ( win32ThreadParam * pParm ) ellDelete ( & pGbl->threadList, & pParm->node ); LeaveCriticalSection ( & pGbl->mutex ); - CloseHandle ( pParm->handle ); - free ( pParm ); + epicsParmCleanupDataWIN32 ( pParm ); } } @@ -470,10 +481,10 @@ static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter ) } epicsExitCallAtThreadExits (); + /* * CAUTION: !!!! the thread id might continue to be used after this thread exits !!!! */ - if ( pGbl ) { TlsSetValue ( pGbl->tlsIndexThreadLibraryEPICS, (void*)0xdeadbeef ); } @@ -493,6 +504,16 @@ static win32ThreadParam * epicsThreadParmCreate ( const char *pName ) strcpy ( pParmWIN32->pName, pName ); pParmWIN32->isSuspended = 0; epicsAtomicIncrIntT(&pParmWIN32->refcnt); +#ifdef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION + pParmWIN32->timer = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); +#endif + if (pParmWIN32->timer == NULL) { + pParmWIN32->timer = CreateWaitableTimer(NULL, 0, NULL); + } + if (pParmWIN32->timer == NULL) { + free(pParmWIN32); + return NULL; + } } return pParmWIN32; } @@ -590,7 +611,7 @@ epicsThreadId epicsThreadCreateOpt ( CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, & threadId ); if ( pParmWIN32->handle == 0 ) { - free ( pParmWIN32 ); + epicsParmCleanupDataWIN32 ( pParmWIN32 ); return NULL; } /* weird win32 interface threadId parameter inconsistency */ @@ -600,8 +621,7 @@ epicsThreadId epicsThreadCreateOpt ( osdPriority = epicsThreadGetOsdPriorityValue (opts->priority); bstat = SetThreadPriority ( pParmWIN32->handle, osdPriority ); if (!bstat) { - CloseHandle ( pParmWIN32->handle ); - free ( pParmWIN32 ); + epicsParmCleanupDataWIN32 ( pParmWIN32 ); return NULL; } @@ -611,11 +631,10 @@ epicsThreadId epicsThreadCreateOpt ( wstat = ResumeThread ( pParmWIN32->handle ); if (wstat==0xFFFFFFFF) { - EnterCriticalSection ( & pGbl->mutex ); - ellDelete ( & pGbl->threadList, & pParmWIN32->node ); - LeaveCriticalSection ( & pGbl->mutex ); - CloseHandle ( pParmWIN32->handle ); - free ( pParmWIN32 ); + EnterCriticalSection ( & pGbl->mutex ); + ellDelete ( & pGbl->threadList, & pParmWIN32->node ); + LeaveCriticalSection ( & pGbl->mutex ); + epicsParmCleanupDataWIN32 ( pParmWIN32 ); return NULL; } @@ -777,24 +796,52 @@ LIBCOM_API int epicsStdCall epicsThreadIsSuspended ( epicsThreadId id ) } } +/** + * osdThreadGetTimer () + * return stored waitable timer object for thread + */ +HANDLE osdThreadGetTimer() +{ + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm; + + assert ( pGbl ); + + pParm = ( win32ThreadParam * ) + TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); + + return pParm->timer; +} + /* * epicsThreadSleep () */ LIBCOM_API void epicsStdCall epicsThreadSleep ( double seconds ) { - static const unsigned mSecPerSec = 1000; - DWORD milliSecDelay; + static const unsigned nSec100PerSec = 10000000u; + LARGE_INTEGER tmo; + HANDLE timer; - if ( seconds > 0.0 ) { - seconds *= mSecPerSec; - seconds += 0.99999999; /* 8 9s here is optimal */ - milliSecDelay = ( seconds >= INFINITE ) ? - INFINITE - 1 : ( DWORD ) seconds; + if ( seconds <= 0.0 ) { + tmo.QuadPart = 0u; } - else { /* seconds <= 0 or NAN */ - milliSecDelay = 0u; + else { + tmo.QuadPart = -((LONGLONG)(seconds * nSec100PerSec + 0.5)); + } + + if (tmo.QuadPart == 0) { + Sleep ( 0 ); + } + else { + timer = osdThreadGetTimer(); + if (!SetWaitableTimer(timer, &tmo, 0, NULL, NULL, 0)) { + fprintf ( stderr, "epicsThreadSleep: SetWaitableTimer failed %lu\n", GetLastError() ); + return; + } + if (WaitForSingleObject(timer, INFINITE) != WAIT_OBJECT_0) { + fprintf ( stderr, "epicsThreadSleep: WaitForSingleObject failed %lu\n", GetLastError() ); + } } - Sleep ( milliSecDelay ); } /* diff --git a/modules/libcom/src/osi/os/WIN32/osdThreadPvt.h b/modules/libcom/src/osi/os/WIN32/osdThreadPvt.h new file mode 100644 index 000000000..f069841ef --- /dev/null +++ b/modules/libcom/src/osi/os/WIN32/osdThreadPvt.h @@ -0,0 +1,20 @@ +/*************************************************************************\ +* SPDX-License-Identifier: EPICS +* EPICS BASE is distributed subject to a Software License Agreement found +* in file LICENSE that is included with this distribution. +\*************************************************************************/ + +#ifndef osdThreadPvth +#define osdThreadPvth + +#ifdef __cplusplus +extern "C" { +#endif + +extern HANDLE osdThreadGetTimer(void); + +#ifdef __cplusplus +} +#endif + +#endif /* osdThreadPvth */