diff --git a/configure/RULES.Db b/configure/RULES.Db index 406c1eb16..c166e0d31 100644 --- a/configure/RULES.Db +++ b/configure/RULES.Db @@ -455,7 +455,7 @@ $(COMMON_DIR)/%.html: ../%.pl $(PODTOHTML) -s -o $(notdir $@) $< @$(MV) $(notdir $@) $@ -.PRECIOUS: $(COMMON_DIR)/%.html %.html +.PRECIOUS: $(COMMON_DIR)/%.html #--------------------------------------------------------------- # DB files diff --git a/configure/RULES_BUILD b/configure/RULES_BUILD index d750548e9..56187155a 100644 --- a/configure/RULES_BUILD +++ b/configure/RULES_BUILD @@ -563,6 +563,10 @@ $(INSTALL_DOC)/%: ../% $(ECHO) "Installing doc $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(INSTALL_DOC) +$(INSTALL_HTML)/$(HTMLS_DIR)/%: $(COMMON_DIR)/% + $(ECHO) "Installing generated html $@" + @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) + $(INSTALL_HTML)/$(HTMLS_DIR)/%: % $(ECHO) "Installing html $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) @@ -571,10 +575,6 @@ $(INSTALL_HTML)/$(HTMLS_DIR)/%: ../% $(ECHO) "Installing html $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) -$(INSTALL_HTML)/$(HTMLS_DIR)/%: $(COMMON_DIR)/% - $(ECHO) "Installing generated html $@" - @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) - $(INSTALL_TEMPLATES_SUBDIR)/%: ../% $(ECHO) "Installing $@" @$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D) diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index df60dff63..10511f50d 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -1927,6 +1927,13 @@ header and removed the need for dbScan.c to reach into the internals of its ## Changes from the 3.15 branch since 3.15.9 +### Fix timers on MS Windows for non-EPICS threads + +The waitable timer changes in 3.15.9 broke calls to `epicsThreadSleep()` and +similar routines that used timers (including `ca_pend_event()`) when made from +threads that were not started using the epicsThread APIs. +[This problem](https://github.com/epics-base/epics-base/pull/200) +[has now been fixed](https://github.com/epics-base/epics-base/pull/201). ## Changes made between 3.15.8 and 3.15.9 diff --git a/modules/database/src/std/rec/dfanoutRecord.dbd.pod b/modules/database/src/std/rec/dfanoutRecord.dbd.pod index ac748ed2c..6b5040091 100644 --- a/modules/database/src/std/rec/dfanoutRecord.dbd.pod +++ b/modules/database/src/std/rec/dfanoutRecord.dbd.pod @@ -71,22 +71,23 @@ SELM is a menu, with three choices: =menu dfanoutSELM -If SELM=="All", then all output links are used, and the values of +If SELM is C, then all output links are used, and the values of SELL and SELN are ignored. -If SELM=="Specified", then the value of SELN is used to specify a single +If SELM is C, then the value of SELN is used to specify a single link which will be used. If SELN==0, then no link will be used; if SELN==1, then OUTA will be used, and so on. -SELN can either have its value set directly, or have its values retrieved -from another EPICS PV. If SELL is a valid PV link, then SELN will be set to -the values of the linked PV. +SELN can either have its value set directly, or have it retrieved from +another EPICS PV. If SELL is a valid PV link, then SELN will be read from +the linked PV. -If SELM=="Mask", then SELN will be treated as a bit mask. If bit one of -SELN is set, then OUTA will be used, if bit two is set, OUTB will be used. -Thus if SELN==5, OUTC and OUTA will be used. +If SELM is C, then SELN will be treated as a bit mask. If bit zero +(the LSB) of SELN is set, then OUTA will be written to; if bit one is set, +OUTB will be written to, and so on. Thus when SELN==5, both OUTC and OUTA +will be written to. -=fields OUTA, OUTB, OUTC, OUTD, OUTE, OUTF, OUTG, OUTH +=fields SELL, SELM, SELN, OUTA, OUTB, OUTC, OUTD, OUTE, OUTF, OUTG, OUTH =head3 Operator Display Parameters diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c index b001775d9..b6c53ce80 100644 --- a/modules/libcom/src/osi/os/WIN32/osdThread.c +++ b/modules/libcom/src/osi/os/WIN32/osdThread.c @@ -674,21 +674,34 @@ void epicsThreadMustJoin(epicsThreadId id) } /* - * epicsThreadSuspendSelf () + * getMyWin32ThreadParam () */ -LIBCOM_API void epicsStdCall epicsThreadSuspendSelf () +static void* getMyWin32ThreadParam ( win32ThreadGlobal * pGbl ) { - win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); win32ThreadParam * pParm; - DWORD stat; - assert ( pGbl ); + if ( ! pGbl ) { + pGbl = fetchWin32ThreadGlobal (); + assert ( pGbl ); + } pParm = ( win32ThreadParam * ) TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); if ( ! pParm ) { pParm = epicsThreadImplicitCreate (); } + return pParm; +} + +/* + * epicsThreadSuspendSelf () + */ +LIBCOM_API void epicsStdCall epicsThreadSuspendSelf () +{ + DWORD stat; + win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); + win32ThreadParam * pParm = getMyWin32ThreadParam ( pGbl ); + if ( pParm ) { EnterCriticalSection ( & pGbl->mutex ); pParm->isSuspended = 1; @@ -732,17 +745,9 @@ LIBCOM_API unsigned epicsStdCall epicsThreadGetPriority (epicsThreadId id) * epicsThreadGetPrioritySelf () */ LIBCOM_API unsigned epicsStdCall epicsThreadGetPrioritySelf () -{ - win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); - win32ThreadParam * pParm; +{ + win32ThreadParam * pParm = getMyWin32ThreadParam ( NULL ); - assert ( pGbl ); - - pParm = ( win32ThreadParam * ) - TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); - if ( ! pParm ) { - pParm = epicsThreadImplicitCreate (); - } if ( pParm ) { return pParm->epicsPriority; } @@ -803,15 +808,12 @@ LIBCOM_API int epicsStdCall epicsThreadIsSuspended ( epicsThreadId id ) */ HANDLE osdThreadGetTimer() { - win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); - win32ThreadParam * pParm; - - assert ( pGbl ); - - pParm = ( win32ThreadParam * ) - TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); - - return pParm->timer; + win32ThreadParam * pParm = getMyWin32ThreadParam ( NULL ); + if (pParm) { + return pParm->timer; + } else { + return NULL; + } } /* @@ -892,17 +894,8 @@ double epicsStdCall epicsThreadSleepQuantum () */ LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetIdSelf (void) { - win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); - win32ThreadParam * pParm; - - assert ( pGbl ); - - pParm = ( win32ThreadParam * ) TlsGetValue ( - pGbl->tlsIndexThreadLibraryEPICS ); - if ( ! pParm ) { - pParm = epicsThreadImplicitCreate (); - assert ( pParm ); /* very dangerous to allow non-unique thread id into use */ - } + win32ThreadParam * pParm = getMyWin32ThreadParam ( NULL ); + assert ( pParm ); /* Don't return a NULL thread id */ return ( epicsThreadId ) pParm; } @@ -939,18 +932,7 @@ LIBCOM_API epicsThreadId epicsStdCall epicsThreadGetId ( const char * pName ) */ LIBCOM_API const char * epicsStdCall epicsThreadGetNameSelf (void) { - win32ThreadGlobal * pGbl = fetchWin32ThreadGlobal (); - win32ThreadParam * pParm; - - if ( ! pGbl ) { - return "thread library not initialized"; - } - - pParm = ( win32ThreadParam * ) - TlsGetValue ( pGbl->tlsIndexThreadLibraryEPICS ); - if ( ! pParm ) { - pParm = epicsThreadImplicitCreate (); - } + win32ThreadParam * pParm = getMyWin32ThreadParam ( NULL ); if ( pParm ) { if ( pParm->pName ) { diff --git a/modules/libcom/test/epicsTimerTest.cpp b/modules/libcom/test/epicsTimerTest.cpp index d4640b84f..74e1919b0 100644 --- a/modules/libcom/test/epicsTimerTest.cpp +++ b/modules/libcom/test/epicsTimerTest.cpp @@ -109,17 +109,14 @@ inline double delayVerify::delay () const double delayVerify::checkError () const { - const double messageThresh = 5.0; // percent - double actualDelay = this->expireStamp - this->beginStamp; - double measuredError = actualDelay - this->expectedDelay; - double percentError = 100.0 * fabs ( measuredError ) / this->expectedDelay; - if(testImpreciseTiming()) - testTodoBegin("imprecise"); - testOk ( percentError < messageThresh, "%f < %f, delay = %f s, error = %f s (%.1f %%)", - percentError, messageThresh, - this->expectedDelay, measuredError, percentError ); - if(testImpreciseTiming()) - testTodoEnd(); + const double minError = testImpreciseTiming() ? 0.25 : 0.05; + double measuredDelay = this->expireStamp - this->beginStamp; + double measuredError = measuredDelay - this->expectedDelay; + double absoluteError = fabs(measuredError); + double percentError = 100.0 * measuredError / this->expectedDelay; + testOk(absoluteError < minError, + "Delay %.3f s, error = %+.6f ms (%+.3f %%)", + this->expectedDelay, measuredError * 1000, percentError); return measuredError; }