diff --git a/configure/CONFIG_BASE_VERSION b/configure/CONFIG_BASE_VERSION index fbe1545a2..463882f4d 100644 --- a/configure/CONFIG_BASE_VERSION +++ b/configure/CONFIG_BASE_VERSION @@ -60,12 +60,8 @@ EPICS_PATCH_LEVEL = 2 EPICS_DEV_SNAPSHOT=-DEV #EPICS_DEV_SNAPSHOT=-pre1 #EPICS_DEV_SNAPSHOT=-pre1-DEV -#EPICS_DEV_SNAPSHOT=-pre2 -#EPICS_DEV_SNAPSHOT=-pre2-DEV #EPICS_DEV_SNAPSHOT=-rc1 #EPICS_DEV_SNAPSHOT=-rc1-DEV -#EPICS_DEV_SNAPSHOT=-rc2 -#EPICS_DEV_SNAPSHOT=-rc2-DEV # No changes should be needed below here diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md index 6765607e3..8d568b9d8 100644 --- a/documentation/RELEASE_NOTES.md +++ b/documentation/RELEASE_NOTES.md @@ -1290,7 +1290,32 @@ header and removed the need for dbScan.c to reach into the internals of its `CALLBACK` objects. -## Changes from the 3.15 branch since 3.15.7 +# Changes incorporated from the 3.15 branch + + +## Changes made between 3.15.7 and 3.15.8 + +### Bug fixes + +The following launchpad bugs have fixes included in this release: + +- [lp: 1812084](https://bugs.launchpad.net/epics-base/+bug/1812084), Build + failure on RTEMS 4.10.2 +- [lp: 1829770](https://bugs.launchpad.net/epics-base/+bug/1829770), event + record device support broken with constant INP +- [lp: 1829919](https://bugs.launchpad.net/epics-base/+bug/1829919), IOC + segfaults when calling dbLoadRecords after iocInit +- [lp: 1838792](https://bugs.launchpad.net/epics-base/+bug/1838792), epicsCalc + bit-wise operators on aarch64 +- [lp: 1841608](https://bugs.launchpad.net/epics-base/+bug/1841608), logClient + falsely sends error logs on all connections +- [lp: 1853168](https://bugs.launchpad.net/epics-base/+bug/1853168), undefined + reference to `clock_gettime()` +- [lp: 1862328](https://bugs.launchpad.net/epics-base/+bug/1862328), Race + condition on IOC start leaves rsrv unresponsive +- [lp: 1868486](https://bugs.launchpad.net/epics-base/+bug/1868486), + epicsMessageQueue lost messages + ### Improvements to the self-test build targets @@ -1314,6 +1339,25 @@ jobs that should be allowed so `make -j4 tapfiles` for a system with 4 CPUs say. Running many more jobs than you have CPUs is likely to be slower and is not recommended. +### Calc Engine Fixes and Enhancements + +The code that implements bit operations for Calc expressions has been reworked +to better handle some CPU architectures and compilers. As part of this work a +new operator has been added: `>>>` performs a logical right-shift, inserting +zero bits into the most significant bits (the operator `>>` is an arithmetic +right-shift which copies the sign bit as it shifts the value rightwards). + +### IOC logClient Changes + +The IOC's error logging system has been updated significantly to fix a number +of issues including: + + - Only send errlog messages to iocLogClient listeners + - Try to minimize lost messages while the log server is down: + + Detect disconnects sooner + + Don't discard the buffer on disconnect + + Flush the buffer immediately after a server reconnects + ### epicsThread: Main thread defaults to allow blocking I/O VxWorks IOCs (and potentially RTEMS IOCs running GeSys) have had problems with @@ -1336,7 +1380,7 @@ This has now been fixed. The remaining record types have had their reference pages moved from the Wiki, and some new reference pages have been written to cover the analog array and -long string input and output record types plus the printf recor type, none of +long string input and output record types plus the printf record type, none of which were previously documented. The wiki reference pages covering the fields common to all, input, and output record types have also been added, thanks to Rolf Keitel. The POD conversion scripts have also been improved and they now diff --git a/documentation/RecordReference.md b/documentation/RecordReference.md index 8d78f1a82..a999d10f7 100644 --- a/documentation/RecordReference.md +++ b/documentation/RecordReference.md @@ -5,8 +5,8 @@ converted from the old EPICS Wiki pages and updated. This list only includes the record types supplied with Base. * [Fields Common to All Record Types](dbCommonRecord.html) -* [Fields Common to Input Record Types](dbCommonInputs.html) -* [Fields Common to Output Record Types](dbCommonOutputs.html) +* [Fields Common to Input Record Types](dbCommonInput.html) +* [Fields Common to Output Record Types](dbCommonOutput.html) ## Record Types diff --git a/modules/libcom/src/osi/os/RTEMS/osdThread.c b/modules/libcom/src/osi/os/RTEMS/osdThread.c index 265fbb765..eb3d73848 100644 --- a/modules/libcom/src/osi/os/RTEMS/osdThread.c +++ b/modules/libcom/src/osi/os/RTEMS/osdThread.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -736,6 +737,23 @@ showInternalTaskInfo (rtems_id tid) } thread = *the_thread; _Thread_Enable_dispatch(); + + /* This looks a bit weird, but it has to support RTEMS versions both before + * and after 4.10.2 when threads changed how their priorities are stored. + */ + int policy; + struct sched_param sp; + rtems_task_priority real_priority, current_priority; + rtems_status_code sc = pthread_getschedparam(tid, &policy, &sp); + if (sc == RTEMS_SUCCESSFUL) { + real_priority = sp.sched_priority; + sc = rtems_task_set_priority(tid, RTEMS_CURRENT_PRIORITY, ¤t_priority); + } + if (sc != RTEMS_SUCCESSFUL) { + fprintf(epicsGetStdout(),"%-30s", " *** RTEMS task gone! ***"); + return; + } + /* * Show both real and current priorities if they differ. * Note that the epicsThreadGetOsiPriorityValue routine is not used here. @@ -743,17 +761,17 @@ showInternalTaskInfo (rtems_id tid) * that priority should be displayed, not the value truncated to * the EPICS range. */ - epicsPri = 199-thread.real_priority; + epicsPri = 199-real_priority; if (epicsPri < 0) fprintf(epicsGetStdout()," <0"); else if (epicsPri > 99) fprintf(epicsGetStdout()," >99"); else fprintf(epicsGetStdout()," %4d", epicsPri); - if (thread.current_priority == thread.real_priority) - fprintf(epicsGetStdout(),"%4d ", (int)thread.current_priority); + if (current_priority == real_priority) + fprintf(epicsGetStdout(),"%4d ", (int)current_priority); else - fprintf(epicsGetStdout(),"%4d/%-3d", (int)thread.real_priority, (int)thread.current_priority); + fprintf(epicsGetStdout(),"%4d/%-3d", (int)real_priority, (int)current_priority); showBitmap (bitbuf, thread.current_state, taskState); fprintf(epicsGetStdout(),"%8.8s", bitbuf); if (thread.current_state & (STATES_WAITING_FOR_SEMAPHORE | diff --git a/modules/libcom/src/osi/os/default/osdMessageQueue.cpp b/modules/libcom/src/osi/os/default/osdMessageQueue.cpp index 94491fa8b..1f42776e0 100644 --- a/modules/libcom/src/osi/os/default/osdMessageQueue.cpp +++ b/modules/libcom/src/osi/os/default/osdMessageQueue.cpp @@ -15,7 +15,6 @@ #include #include - #include #include @@ -108,7 +107,7 @@ LIBCOM_API epicsMessageQueueId epicsStdCall epicsMessageQueueCreate( } static void -freeEventNode(struct eventNode *enode) +destroyEventNode(struct eventNode *enode) { epicsEventDestroy(enode->event); free(enode); @@ -121,7 +120,7 @@ epicsMessageQueueDestroy(epicsMessageQueueId pmsg) while ((evp = reinterpret_cast < struct eventNode * > ( ellGet(&pmsg->eventFreeList) ) ) != NULL) { - freeEventNode(evp); + destroyEventNode(evp); } epicsMutexDestroy(pmsg->mutex); free(pmsg->buf); @@ -147,6 +146,16 @@ getEventNode(epicsMessageQueueId pmsg) return evp; } +static void +freeEventNode(epicsMessageQueueId pmsg, eventNode *evp, epicsEventStatus status) +{ + if (status == epicsEventWaitTimeout) { + epicsEventSignal(evp->event); + epicsEventWait(evp->event); + } + ellAdd(&pmsg->eventFreeList, &evp->link); +} + static int mySend(epicsMessageQueueId pmsg, void *message, unsigned int size, double timeout) @@ -165,7 +174,7 @@ mySend(epicsMessageQueueId pmsg, void *message, unsigned int size, if ((pmsg->numberOfSendersWaiting > 0) || (pmsg->full && (ellFirst(&pmsg->receiveQueue) == NULL))) { /* - * Return if not allowed to wait + * Return if not allowed to wait. NB -1 means wait forever. */ if (timeout == 0) { epicsMutexUnlock(pmsg->mutex); @@ -173,7 +182,7 @@ mySend(epicsMessageQueueId pmsg, void *message, unsigned int size, } /* - * Wait + * Indicate that we're waiting */ struct threadNode threadNode; threadNode.evp = getEventNode(pmsg); @@ -188,22 +197,25 @@ mySend(epicsMessageQueueId pmsg, void *message, unsigned int size, epicsMutexUnlock(pmsg->mutex); - epicsEventStatus status; - if (timeout > 0) - status = epicsEventWaitWithTimeout(threadNode.evp->event, timeout); - else - status = epicsEventWait(threadNode.evp->event); + /* + * Wait for receiver to wake us + */ + epicsEventStatus status = timeout < 0 ? + epicsEventWait(threadNode.evp->event) : + epicsEventWaitWithTimeout(threadNode.evp->event, timeout); epicsMutexMustLock(pmsg->mutex); - if(!threadNode.eventSent) + if (!threadNode.eventSent) { + /* Receiver didn't take us off the sendQueue, do it ourselves */ ellDelete(&pmsg->sendQueue, &threadNode.link); - pmsg->numberOfSendersWaiting--; + pmsg->numberOfSendersWaiting--; + } - ellAdd(&pmsg->eventFreeList, &threadNode.evp->link); + freeEventNode(pmsg, threadNode.evp, status); - if ((pmsg->full && (ellFirst(&pmsg->receiveQueue) == NULL)) || - status != epicsEventOK) { + if (pmsg->full && (ellFirst(&pmsg->receiveQueue) == NULL)) { + /* State of the queue didn't change, exit */ epicsMutexUnlock(pmsg->mutex); return -1; } @@ -296,6 +308,7 @@ myReceive(epicsMessageQueueId pmsg, void *message, unsigned int size, */ if ((pthr = reinterpret_cast < struct threadNode * > ( ellGet(&pmsg->sendQueue) ) ) != NULL) { + pmsg->numberOfSendersWaiting--; pthr->eventSent = true; epicsEventSignal(pthr->evp->event); } @@ -304,7 +317,7 @@ myReceive(epicsMessageQueueId pmsg, void *message, unsigned int size, } /* - * Return if not allowed to wait + * Return if not allowed to wait. NB -1 means wait forever. */ if (timeout == 0) { epicsMutexUnlock(pmsg->mutex); @@ -312,16 +325,7 @@ myReceive(epicsMessageQueueId pmsg, void *message, unsigned int size, } /* - * Wake up the oldest task waiting to send - */ - if ((pthr = reinterpret_cast < struct threadNode * > - ( ellGet(&pmsg->sendQueue) ) ) != NULL) { - pthr->eventSent = true; - epicsEventSignal(pthr->evp->event); - } - - /* - * Wait for message to arrive + * Indicate that we're waiting */ struct threadNode threadNode; threadNode.evp = getEventNode(pmsg); @@ -335,18 +339,32 @@ myReceive(epicsMessageQueueId pmsg, void *message, unsigned int size, } ellAdd(&pmsg->receiveQueue, &threadNode.link); + + /* + * Wake up the oldest task waiting to send + */ + if ((pthr = reinterpret_cast < struct threadNode * > + ( ellGet(&pmsg->sendQueue) ) ) != NULL) { + pmsg->numberOfSendersWaiting--; + pthr->eventSent = true; + epicsEventSignal(pthr->evp->event); + } + epicsMutexUnlock(pmsg->mutex); - if (timeout > 0) + /* + * Wait for a message to arrive + */ + epicsEventStatus status = timeout < 0 ? + epicsEventWait(threadNode.evp->event) : epicsEventWaitWithTimeout(threadNode.evp->event, timeout); - else - epicsEventWait(threadNode.evp->event); epicsMutexMustLock(pmsg->mutex); if (!threadNode.eventSent) ellDelete(&pmsg->receiveQueue, &threadNode.link); - ellAdd(&pmsg->eventFreeList, &threadNode.evp->link); + + freeEventNode(pmsg, threadNode.evp, status); epicsMutexUnlock(pmsg->mutex); @@ -398,7 +416,8 @@ epicsMessageQueuePending(epicsMessageQueueId pmsg) LIBCOM_API void epicsStdCall epicsMessageQueueShow(epicsMessageQueueId pmsg, int level) { - printf("Message Queue Used:%d Slots:%lu", epicsMessageQueuePending(pmsg), pmsg->capacity); + printf("Message Queue Used:%d Slots:%lu", + epicsMessageQueuePending(pmsg), pmsg->capacity); if (level >= 1) printf(" Maximum size:%lu", pmsg->maxMessageSize); printf("\n"); diff --git a/modules/libcom/test/epicsMessageQueueTest.cpp b/modules/libcom/test/epicsMessageQueueTest.cpp index 1bee13c36..f23683c84 100644 --- a/modules/libcom/test/epicsMessageQueueTest.cpp +++ b/modules/libcom/test/epicsMessageQueueTest.cpp @@ -27,7 +27,10 @@ static volatile int sendExit = 0; static volatile int recvExit = 0; static epicsEventId finished; static unsigned int mediumStack; -static int numReceived; + +#define SLEEPY_TESTS 500 +static int numSent, numReceived; +static epicsEventId complete; /* * In Numerical Recipes in C: The Art of Scientific Computing (William H. @@ -124,11 +127,96 @@ fastReceiver(void *arg) int len; numReceived = 0; while (!recvExit) { - len = q->receive(cbuf, sizeof cbuf, 0.01); + len = q->receive(cbuf, sizeof cbuf, 0.010); if (len > 0) { numReceived++; } } + recvExit = 0; + epicsEventSignal(complete); +} + +void sleepySender(double delay) +{ + testDiag("sleepySender: sending every %.3f seconds", delay); + epicsMessageQueue q(4, 20); + epicsThreadCreate("Fast Receiver", epicsThreadPriorityMedium, + mediumStack, fastReceiver, &q); + + numSent = 0; + for (int i = 0 ; i < SLEEPY_TESTS ; i++) { + if (q.send((void *)msg1, 4) == 0) { + numSent++; + } + epicsThreadSleep(delay); + } + epicsThreadSleep(1.0); + testOk(numSent == SLEEPY_TESTS, "Sent %d (should be %d)", + numSent, SLEEPY_TESTS); + testOk(numReceived == SLEEPY_TESTS, "Received %d (should be %d)", + numReceived, SLEEPY_TESTS); + + recvExit = 1; + while (q.send((void *)msg1, 4) != 0) + epicsThreadSleep(0.01); + epicsEventMustWait(complete); +} + +extern "C" void +fastSender(void *arg) +{ + epicsMessageQueue *q = (epicsMessageQueue *)arg; + numSent = 0; + + // Send first withough timeout + q->send((void *)msg1, 4); + numSent++; + + // The rest have a timeout + while (!sendExit) { + if (q->send((void *)msg1, 4, 0.010) == 0) { + numSent++; + } + } + sendExit = 0; + epicsEventSignal(complete); +} + +void sleepyReceiver(double delay) +{ + testDiag("sleepyReceiver: acquiring every %.3f seconds", delay); + epicsMessageQueue q(4, 20); + + // Fill the queue + for (int i = q.pending(); i < 4 ;i++) { + q.send((void *)msg1, 4); + } + + epicsThreadCreate("Fast Sender", epicsThreadPriorityMedium, + mediumStack, fastSender, &q); + epicsThreadSleep(0.5); + + char cbuf[80]; + int len; + numReceived = 0; + + for (int i = 0 ; i < SLEEPY_TESTS ; i++) { + len = q.receive(cbuf, sizeof cbuf); + if (len > 0) { + numReceived++; + } + epicsThreadSleep(delay); + } + + testOk(numSent == SLEEPY_TESTS, "Sent %d (should be %d)", + numSent, SLEEPY_TESTS); + testOk(numReceived == SLEEPY_TESTS, "Received %d (should be %d)", + numReceived, SLEEPY_TESTS); + + sendExit = 1; + while (q.receive(cbuf, sizeof cbuf) <= 0) + epicsThreadSleep(0.01); + epicsEventMustWait(complete); } extern "C" void @@ -156,10 +244,8 @@ extern "C" void messageQueueTest(void *parm) int len; int pass; int want; - int numSent = 0; epicsMessageQueue *q1 = new epicsMessageQueue(4, 20); - epicsMessageQueue *q2 = new epicsMessageQueue(4, 20); testDiag("Simple single-thread tests:"); i = 0; @@ -269,34 +355,17 @@ extern "C" void messageQueueTest(void *parm) testOk(q1->send((void *)msg1, 10) == 0, "Send with no receiver"); epicsThreadSleep(2.0); - testDiag("Single receiver with timeout, single sender with sleep tests:"); - testDiag("These tests last 20 seconds ..."); - epicsThreadCreate("Fast Receiver", epicsThreadPriorityMedium, - mediumStack, fastReceiver, q2); - numSent = 0; - numReceived = 0; - for (i = 0 ; i < 1000 ; i++) { - if (q2->send((void *)msg1, 4) == 0) { - numSent++; - } - epicsThreadSleep(0.011); - } - epicsThreadSleep(1.0); - if (!testOk(numSent == 1000 && numReceived == 1000, "sleep=0.011")) { - testDiag("numSent should be 1000, actual=%d, numReceived should be 1000, actual=%d", numSent, numReceived); - } - numSent = 0; - numReceived = 0; - for (i = 0 ; i < 1000 ; i++) { - if (q2->send((void *)msg1, 4) == 0) { - numSent++; - } - epicsThreadSleep(0.010); - } - epicsThreadSleep(1.0); - if (!testOk(numSent == 1000 && numReceived == 1000, "sleep=0.010")) { - testDiag("numSent should be 1000, actual=%d, numReceived should be 1000, actual=%d", numSent, numReceived); - } + testDiag("6 Single receiver single sender 'Sleepy timeout' tests,"); + testDiag(" these should take about %.2f seconds each:", + SLEEPY_TESTS * 0.010); + + complete = epicsEventMustCreate(epicsEventEmpty); + sleepySender(0.009); + sleepySender(0.010); + sleepySender(0.011); + sleepyReceiver(0.009); + sleepyReceiver(0.010); + sleepyReceiver(0.011); testDiag("Single receiver, single sender tests:"); epicsThreadSetPriority(myThreadId, epicsThreadPriorityHigh); @@ -359,7 +428,7 @@ extern "C" void messageQueueTest(void *parm) MAIN(epicsMessageQueueTest) { - testPlan(64); + testPlan(74); finished = epicsEventMustCreate(epicsEventEmpty); mediumStack = epicsThreadGetStackSize(epicsThreadStackMedium);