From 9f660f2238a6efe85af8a49510edeaee69b6641a Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Mon, 4 Sep 2023 09:50:08 +0200
Subject: [PATCH 01/66] add initHookAtPrepare
---
modules/database/src/ioc/db/dbUnitTest.c | 2 ++
modules/libcom/src/iocsh/initHooks.c | 3 +++
modules/libcom/src/iocsh/initHooks.h | 12 ++++++++++++
3 files changed, 17 insertions(+)
diff --git a/modules/database/src/ioc/db/dbUnitTest.c b/modules/database/src/ioc/db/dbUnitTest.c
index 2c7efd8a9..bf4ec205b 100644
--- a/modules/database/src/ioc/db/dbUnitTest.c
+++ b/modules/database/src/ioc/db/dbUnitTest.c
@@ -50,6 +50,7 @@ void testdbPrepare(void)
{
if(!testEvtLock)
testEvtLock = epicsMutexMustCreate();
+ initHookAnnounce(initHookAfterPrepareDatabase);
}
void testdbReadDatabase(const char* file,
@@ -94,6 +95,7 @@ void testIocShutdownOk(void)
void testdbCleanup(void)
{
+ initHookAnnounce(initHookBeforeCleanupDatabase);
dbFreeBase(pdbbase);
db_cleanup_events();
initHookFree();
diff --git a/modules/libcom/src/iocsh/initHooks.c b/modules/libcom/src/iocsh/initHooks.c
index 750a76d42..90b1de2c5 100644
--- a/modules/libcom/src/iocsh/initHooks.c
+++ b/modules/libcom/src/iocsh/initHooks.c
@@ -139,6 +139,9 @@ const char *initHookName(int state)
"initHookBeforeFree",
"initHookAfterShutdown",
+ "initHookAfterPrepareDatabase",
+ "initHookBeforeCleanupDatabase",
+
"initHookAfterInterruptAccept",
"initHookAtEnd"
};
diff --git a/modules/libcom/src/iocsh/initHooks.h b/modules/libcom/src/iocsh/initHooks.h
index de4a72a32..a5ad93d3f 100644
--- a/modules/libcom/src/iocsh/initHooks.h
+++ b/modules/libcom/src/iocsh/initHooks.h
@@ -71,6 +71,7 @@ extern "C" {
* if the IOC is later paused and restarted.
*/
typedef enum {
+
// iocInit() begins
initHookAtIocBuild = 0, /**< Start of iocBuild() / iocInit() */
initHookAtBeginning, /**< Database sanity checks passed */
@@ -133,6 +134,17 @@ typedef enum {
initHookAfterShutdown,
// iocShutdown() ends
+ /** \brief Called during testdbPrepare()
+ * Use this hook to repeat actions each time an empty test database is initialized.
+ * \since UNRELEASED Added, triggered only by unittest code.
+ */
+ initHookAfterPrepareDatabase,
+ /** \brief Called during testdbCleanup()
+ * Use this hook to perform cleanup each time before a test database is free()'d.
+ * \since UNRELEASED Added, triggered only by unittest code.
+ */
+ initHookBeforeCleanupDatabase,
+
/* Deprecated states: */
/** Only announced once. Deprecated in favor of initHookAfterDatabaseRunning */
initHookAfterInterruptAccept,
From 3b22e5f7109dac8a9e9d20bc9715d54bcf543464 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Tue, 12 Sep 2023 11:45:33 +0200
Subject: [PATCH 02/66] doc dbLock.h
---
modules/database/src/ioc/db/dbLock.h | 48 +++++++++++++++++++++++++++-
1 file changed, 47 insertions(+), 1 deletion(-)
diff --git a/modules/database/src/ioc/db/dbLock.h b/modules/database/src/ioc/db/dbLock.h
index 3d617f995..8fe068ce9 100644
--- a/modules/database/src/ioc/db/dbLock.h
+++ b/modules/database/src/ioc/db/dbLock.h
@@ -24,18 +24,64 @@ extern "C" {
struct dbCommon;
struct dbBase;
+/** @brief Lock multiple records.
+ *
+ * A dbLocker allows a caller to simultaneously lock multiple records.
+ * The list of records is provided to dbLockerAlloc().
+ * And the resulting dbLocker can be locked/unlocked repeatedly.
+ *
+ * Each thread can only lock one dbLocker at a time.
+ * While locked, dbScanLock() may be called only on those records
+ * included in the dbLocker.
+ *
+ * @since 3.16.0.1
+ */
+struct dbLocker;
typedef struct dbLocker dbLocker;
+/** @brief Lock a record for modification.
+ *
+ * While locked, caller may access record using eg. dbGet() or dbPut(),
+ * but not dbGetField() or dbPutField().
+ * The caller must later call dbScanUnlock().
+ * dbScanLock() may be called again as the record lock behaves as a recursive mutex.
+ */
DBCORE_API void dbScanLock(struct dbCommon *precord);
+/** @brief Unlock a record.
+ *
+ * Reverse the action of dbScanLock()
+ */
DBCORE_API void dbScanUnlock(struct dbCommon *precord);
+/** @brief Prepare to lock a set of records.
+ * @param precs Array of nrecs dbCommon pointers.
+ * @param nrecs Length of precs array
+ * @param flags Set to 0
+ * @return NULL on error
+ * @since 3.16.0.1
+ */
DBCORE_API dbLocker *dbLockerAlloc(struct dbCommon * const *precs,
size_t nrecs,
unsigned int flags);
-DBCORE_API void dbLockerFree(dbLocker *);
+/** @brief Free dbLocker allocated by dbLockerAlloc()
+ * @param plocker Must not be NULL
+ * @since 3.16.0.1
+ */
+DBCORE_API void dbLockerFree(dbLocker *plocker);
+/** @brief Lock all records of dbLocker
+ *
+ * While locked, caller may access any associated record passed to dbLockerAlloc() .
+ * dbScanLockMany() may not be called again (multi-lock is not recursive).
+ * dbScanLock()/dbScanUnlock() may be called on individual record.
+ * The caller must later call dbScanUnlockMany().
+ * @since 3.16.0.1
+ */
DBCORE_API void dbScanLockMany(dbLocker*);
+/** @brief Unlock all records of dbLocker
+ * @since 3.16.0.1
+ */
DBCORE_API void dbScanUnlockMany(dbLocker*);
DBCORE_API unsigned long dbLockGetLockId(
From 39b5c01c5df06fb7cc0b241fe8ee7140c454a5c7 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 13 Sep 2023 14:00:53 +0200
Subject: [PATCH 03/66] minor
---
modules/database/src/ioc/db/dbLockPvt.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/modules/database/src/ioc/db/dbLockPvt.h b/modules/database/src/ioc/db/dbLockPvt.h
index 520241c3e..99ab590df 100644
--- a/modules/database/src/ioc/db/dbLockPvt.h
+++ b/modules/database/src/ioc/db/dbLockPvt.h
@@ -10,6 +10,7 @@
#define DBLOCKPVT_H
#include "dbLock.h"
+#include "epicsMutex.h"
#include "epicsSpin.h"
/* Define to enable additional error checking */
From 5aca4c684cc87158ded4c4d4c3303b4c018e43fa Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Wed, 13 Sep 2023 17:03:17 +0200
Subject: [PATCH 04/66] dbEvent minor
---
modules/database/src/ioc/db/dbEvent.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index d5c930dfc..653ea411c 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -1017,6 +1017,7 @@ static int event_read ( struct event_que *ev_que )
notifiedRemaining = eventsRemaining;
}
LOCKEVQUE (ev_que);
+ /* concurrent db_cancel_event() may have free()'d pevent */
/*
* check to see if this event has been canceled each
@@ -1029,13 +1030,10 @@ static int event_read ( struct event_que *ev_que )
ev_que->evUser->pSuicideEvent = NULL;
}
else {
+ pevent->callBackInProgress = FALSE;
if ( pevent->user_sub==NULL && pevent->npend==0u ) {
- pevent->callBackInProgress = FALSE;
epicsEventSignal ( ev_que->evUser->pflush_sem );
}
- else {
- pevent->callBackInProgress = FALSE;
- }
}
}
db_delete_field_log(pfl);
From 3d25756065fb0bb311321fcf226a1fad0a3e537c Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 14 Sep 2023 09:20:43 +0200
Subject: [PATCH 05/66] privatize evSubscrip
---
modules/database/src/ioc/db/dbChannel.h | 10 ++++++++--
modules/database/src/ioc/db/dbEvent.c | 1 +
modules/database/test/ioc/db/dbPutGetTest.c | 2 ++
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
index 23ffd7d09..70e422405 100644
--- a/modules/database/src/ioc/db/dbChannel.h
+++ b/modules/database/src/ioc/db/dbChannel.h
@@ -41,7 +41,12 @@ extern "C" {
/**
* event subscription
*/
-typedef struct evSubscrip {
+struct evSubscrip;
+
+typedef struct evSubscrip evSubscrip;
+
+#ifdef EPICS_PRIVATE_API
+struct evSubscrip {
ELLNODE node;
struct dbChannel * chan;
EVENTFUNC * user_sub;
@@ -54,7 +59,8 @@ typedef struct evSubscrip {
char useValque;
char callBackInProgress;
char enabled;
-} evSubscrip;
+};
+#endif
typedef struct chFilter chFilter;
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index 653ea411c..3016630fb 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -18,6 +18,7 @@
* Ralph Lange
*/
+#define EPICS_PRIVATE_API
#include
#include
#include
diff --git a/modules/database/test/ioc/db/dbPutGetTest.c b/modules/database/test/ioc/db/dbPutGetTest.c
index 8f539407d..3a5155661 100644
--- a/modules/database/test/ioc/db/dbPutGetTest.c
+++ b/modules/database/test/ioc/db/dbPutGetTest.c
@@ -4,6 +4,8 @@
* in file LICENSE that is included with this distribution.
\*************************************************************************/
+#define EPICS_PRIVATE_API
+
#include
#include
From fab8fd7102143a661a81cf7163dd889601eb015c Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 14 Sep 2023 10:11:25 +0200
Subject: [PATCH 06/66] dbEvent: handle multiple db_event_cancel()
Allow for multiple db_event_cancel() (concurrent or
self-cancel) prior to event_task wakeup.
In db_event_cancel(), immediate free() only if idle
(not queued or in progress). Otherwise, defer free()
to event task. Avoids need to immediately expunge
canceled event from queue. Allow event task to
process canceled events as normal (except no user_sub)
until npend==0.
---
modules/database/src/ioc/db/dbChannel.h | 13 +-
modules/database/src/ioc/db/dbEvent.c | 179 ++++++++----------------
2 files changed, 73 insertions(+), 119 deletions(-)
diff --git a/modules/database/src/ioc/db/dbChannel.h b/modules/database/src/ioc/db/dbChannel.h
index 70e422405..d4cb9e9bc 100644
--- a/modules/database/src/ioc/db/dbChannel.h
+++ b/modules/database/src/ioc/db/dbChannel.h
@@ -49,15 +49,24 @@ typedef struct evSubscrip evSubscrip;
struct evSubscrip {
ELLNODE node;
struct dbChannel * chan;
+ /* user_sub==NULL used to indicate db_cancel_event() */
EVENTFUNC * user_sub;
void * user_arg;
+ /* associated queue, may be shared with other evSubscrip */
struct event_que * ev_que;
+ /* NULL if !npend. if npend!=0, pointer to last event added to event_que::valque */
db_field_log ** pLastLog;
- unsigned long npend; /**< n times this event is on the queue */
- unsigned long nreplace; /**< n times replacing event on the queue */
+ /* n times this event is on the queue */
+ unsigned long npend;
+ /* n times replacing event on the queue */
+ unsigned long nreplace;
+ /* DBE mask */
unsigned char select;
+ /* if set, subscription will yield dbfl_type_val */
char useValque;
+ /* event_task is handling this subscription */
char callBackInProgress;
+ /* this node added to dbCommon::mlis */
char enabled;
};
#endif
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index 3016630fb..38ec9e701 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -76,7 +76,6 @@ struct event_que {
unsigned short getix;
unsigned short quota; /* the number of assigned entries*/
unsigned short nDuplicates; /* N events duplicated on this q */
- unsigned short nCanceled; /* the number of canceled entries */
unsigned possibleStall;
};
@@ -92,7 +91,7 @@ struct event_user {
void *extralabor_arg;/* parameter to above */
epicsThreadId taskid; /* event handler task id */
- struct evSubscrip *pSuicideEvent; /* event that is deleting itself */
+ epicsUInt32 pflush_seq; /* worker cycle count for synchronization */
unsigned queovr; /* event que overflow count */
unsigned char pendexit; /* exit pend task */
unsigned char extra_labor; /* if set call extra labor func */
@@ -123,10 +122,9 @@ static void *dbevFieldLogFreeList;
static char *EVENT_PEND_NAME = "eventTask";
-static struct evSubscrip canceledEvent;
-
static epicsMutexId stopSync;
+/* unused space in queue (EVENTQUESIZE when empty) */
static unsigned short ringSpace ( const struct event_que *pevq )
{
if ( pevq->evque[pevq->putix] == EVENTQEMPTY ) {
@@ -140,17 +138,11 @@ static unsigned short ringSpace ( const struct event_que *pevq )
return 0;
}
-/*
- * db_event_list ()
- */
int db_event_list ( const char *pname, unsigned level )
{
return dbel ( pname, level );
}
-/*
- * dbel ()
- */
int dbel ( const char *pname, unsigned level )
{
DBADDR addr;
@@ -218,7 +210,6 @@ int dbel ( const char *pname, unsigned level )
if ( level > 2 ) {
unsigned nDuplicates;
- unsigned nCanceled;
if ( pevent->nreplace ) {
printf (", discarded by replacement=%ld", pevent->nreplace);
}
@@ -227,14 +218,10 @@ int dbel ( const char *pname, unsigned level )
}
LOCKEVQUE(pevent->ev_que);
nDuplicates = pevent->ev_que->nDuplicates;
- nCanceled = pevent->ev_que->nCanceled;
UNLOCKEVQUE(pevent->ev_que);
if ( nDuplicates ) {
printf (", duplicate count =%u\n", nDuplicates );
}
- if ( nCanceled ) {
- printf (", canceled count =%u\n", nCanceled );
- }
}
if ( level > 3 ) {
@@ -331,7 +318,6 @@ dbEventCtx db_init_events (void)
evUser->flowCtrlMode = FALSE;
evUser->extraLaborBusy = FALSE;
- evUser->pSuicideEvent = NULL;
return (dbEventCtx) evUser;
fail:
if(evUser->lock)
@@ -462,8 +448,7 @@ dbEventSubscription db_add_event (
while ( TRUE ) {
int success = 0;
LOCKEVQUE ( ev_que );
- success = ( ev_que->quota + ev_que->nCanceled <
- EVENTQUESIZE - EVENTENTRIES );
+ success = ( ev_que->quota < EVENTQUESIZE - EVENTENTRIES );
if ( success ) {
ev_que->quota += EVENTENTRIES;
}
@@ -580,62 +565,52 @@ static void event_remove ( struct event_que *ev_que,
void db_cancel_event (dbEventSubscription event)
{
struct evSubscrip * const pevent = (struct evSubscrip *) event;
- unsigned short getix;
+ struct event_que *que = pevent->ev_que;
+ char sync = 0;
db_event_disable ( event );
- /*
- * flag the event as canceled by NULLing out the callback handler
- *
- * make certain that the event isn't being accessed while
- * its call back changes
- */
- LOCKEVQUE (pevent->ev_que);
+ LOCKEVQUE (que);
- pevent->user_sub = NULL;
+ pevent->user_sub = NULL; /* callback pointer doubles as canceled flag */
- /*
- * purge this event from the queue
- *
- * Its better to take this approach rather than waiting
- * for the event thread to finish removing this event
- * from the queue because the event thread will not
- * process if we are in flow control mode. Since blocking
- * here will block CA's TCP input queue then a dead lock
- * would be possible.
- */
- for ( getix = pevent->ev_que->getix;
- pevent->ev_que->evque[getix] != EVENTQEMPTY; ) {
- if ( pevent->ev_que->evque[getix] == pevent ) {
- assert ( pevent->ev_que->nCanceled < USHRT_MAX );
- pevent->ev_que->nCanceled++;
- event_remove ( pevent->ev_que, getix, &canceledEvent );
- }
- getix = RNGINC ( getix );
- if ( getix == pevent->ev_que->getix ) {
- break;
- }
- }
- assert ( pevent->npend == 0u );
+ if(pevent->callBackInProgress) {
+ /* this event callback is pending or in-progress in event_task. */
+ if(pevent->ev_que->evUser->taskid != epicsThreadGetIdSelf())
+ sync = 1; /* concurrent to event_task, so wait */
- if ( pevent->ev_que->evUser->taskid == epicsThreadGetIdSelf() ) {
- pevent->ev_que->evUser->pSuicideEvent = pevent;
- }
- else {
- while ( pevent->callBackInProgress ) {
- UNLOCKEVQUE (pevent->ev_que);
- epicsEventMustWait ( pevent->ev_que->evUser->pflush_sem );
- LOCKEVQUE (pevent->ev_que);
- }
+ } else if(pevent->npend) {
+ /* some (now defunct) events in the queue, defer free() to event_task */
+
+ } else {
+ /* no other references, cleanup now */
+
+ pevent->ev_que->quota -= EVENTENTRIES;
+ freeListFree ( dbevEventSubscriptionFreeList, pevent );
}
- pevent->ev_que->quota -= EVENTENTRIES;
+ UNLOCKEVQUE (que);
- UNLOCKEVQUE (pevent->ev_que);
-
- freeListFree ( dbevEventSubscriptionFreeList, pevent );
-
- return;
+ if(sync) {
+ /* wait for worker to cycle */
+ struct event_user *evUser = que->evUser;
+ epicsUInt32 curSeq;
+ epicsMutexMustLock ( evUser->lock );
+ /* grab current cycle counter, then wait for it to change */
+ curSeq = evUser->pflush_seq;
+ do {
+ epicsMutexUnlock( evUser->lock );
+ epicsEventMustWait(evUser->pflush_sem);
+ /* The complexity needed to track the # of waiters does not seem
+ * worth it for the relatively rare situation of concurrent cancel.
+ * So uncondtionally re-trigger. This will result in one spurious
+ * wakeup for each cancellation.
+ */
+ epicsEventTrigger(evUser->pflush_sem);
+ epicsMutexMustLock ( evUser->lock );
+ } while(curSeq == evUser->pflush_seq);
+ epicsMutexUnlock( evUser->lock );
+ }
}
/*
@@ -935,10 +910,7 @@ void db_post_single_event (dbEventSubscription event)
*/
static int event_read ( struct event_que *ev_que )
{
- db_field_log *pfl;
int notifiedRemaining = 0;
- void ( *user_sub ) ( void *user_arg, struct dbChannel *chan,
- int eventsRemaining, db_field_log *pfl );
/*
* evUser ring buffer must be locked for the multiple
@@ -959,19 +931,7 @@ static int event_read ( struct event_que *ev_que )
while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) {
struct evSubscrip *pevent = ev_que->evque[ev_que->getix];
int eventsRemaining;
-
- pfl = ev_que->valque[ev_que->getix];
- if ( pevent == &canceledEvent ) {
- ev_que->evque[ev_que->getix] = EVENTQEMPTY;
- if (ev_que->valque[ev_que->getix]) {
- db_delete_field_log(ev_que->valque[ev_que->getix]);
- ev_que->valque[ev_que->getix] = NULL;
- }
- ev_que->getix = RNGINC ( ev_que->getix );
- assert ( ev_que->nCanceled > 0 );
- ev_que->nCanceled--;
- continue;
- }
+ db_field_log *pfl = ev_que->valque[ev_que->getix];
/*
* Simple type values queued up for reliable interprocess
@@ -981,13 +941,7 @@ static int event_read ( struct event_que *ev_que )
event_remove ( ev_que, ev_que->getix, EVENTQEMPTY );
ev_que->getix = RNGINC ( ev_que->getix );
- eventsRemaining = ev_que->evque[ev_que->getix] != EVENTQEMPTY && !ev_que->nCanceled;
-
- /*
- * create a local copy of the call back parameters while
- * we still have the lock
- */
- user_sub = pevent->user_sub;
+ eventsRemaining = ev_que->evque[ev_que->getix] != EVENTQEMPTY;
/*
* Next event pointer can be used by event tasks to determine
@@ -999,14 +953,12 @@ static int event_read ( struct event_que *ev_que )
* record lock, and it is calling db_post_events() waiting
* for the event queue lock (which this thread now has).
*/
- if ( user_sub ) {
- /*
- * This provides a way to test to see if an event is in use
- * despite the fact that the event queue does not point to
- * it.
- */
+ if ( pevent->user_sub ) {
+ EVENTFUNC* user_sub = pevent->user_sub;
pevent->callBackInProgress = TRUE;
+
UNLOCKEVQUE (ev_que);
+
/* Run post-event-queue filter chain */
if (ellCount(&pevent->chan->post_chain)) {
pfl = dbChannelRunPostChain(pevent->chan, pfl);
@@ -1017,25 +969,15 @@ static int event_read ( struct event_que *ev_que )
eventsRemaining, pfl );
notifiedRemaining = eventsRemaining;
}
- LOCKEVQUE (ev_que);
- /* concurrent db_cancel_event() may have free()'d pevent */
- /*
- * check to see if this event has been canceled each
- * time that the callBackInProgress flag is set to false
- * while we have the event queue lock, and post the flush
- * complete sem if there are no longer any events on the
- * queue
- */
- if ( ev_que->evUser->pSuicideEvent == pevent ) {
- ev_que->evUser->pSuicideEvent = NULL;
- }
- else {
- pevent->callBackInProgress = FALSE;
- if ( pevent->user_sub==NULL && pevent->npend==0u ) {
- epicsEventSignal ( ev_que->evUser->pflush_sem );
- }
- }
+ LOCKEVQUE (ev_que);
+
+ pevent->callBackInProgress = FALSE;
+ }
+ /* callback may have called db_cancel_event(), so must check user_sub again */
+ if(!pevent->user_sub && !pevent->npend) {
+ pevent->ev_que->quota -= EVENTENTRIES;
+ freeListFree ( dbevEventSubscriptionFreeList, pevent );
}
db_delete_field_log(pfl);
}
@@ -1050,9 +992,6 @@ static int event_read ( struct event_que *ev_que )
return DB_EVENT_OK;
}
-/*
- * EVENT_TASK()
- */
static void event_task (void *pParm)
{
struct event_user * const evUser = (struct event_user *) pParm;
@@ -1069,6 +1008,7 @@ static void event_task (void *pParm)
do {
void (*pExtraLaborSub) (void *);
void *pExtraLaborArg;
+ char wake;
epicsEventMustWait(evUser->ppendsem);
/*
@@ -1093,15 +1033,20 @@ static void event_task (void *pParm)
}
evUser->extraLaborBusy = FALSE;
- for ( ev_que = &evUser->firstque; ev_que;
- ev_que = ev_que->nextque ) {
+ for ( ev_que = &evUser->firstque; ev_que; ev_que = ev_que->nextque ) {
+ /* unlock during iteration is safe as event_que will not be free'd */
epicsMutexUnlock ( evUser->lock );
event_read (ev_que);
epicsMutexMustLock ( evUser->lock );
}
pendexit = evUser->pendexit;
+
+ evUser->pflush_seq++;
+
epicsMutexUnlock ( evUser->lock );
+ epicsEventSignal(evUser->pflush_sem);
+
} while( ! pendexit );
epicsMutexDestroy(evUser->firstque.writelock);
From 7c4a21eab44183a84f25ea234ce2fde8ad08c4ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=89rico=20Nogueira?=
Date: Mon, 31 Jul 2023 11:36:11 -0300
Subject: [PATCH 07/66] libCom: detect support for backtrace() with
__has_include.
This is necessary in order to build epics-base with musl libc, for
example, and any other C libraries which don't include this
functionality. In order to not regress builds with older compilers, we
still support the uclibc check. Furthermore, it has been checked that
uclibc-ng (the maintained version of uclibc) doesn't install the
header when the functionality is disabled [1] [2].
To avoid repetition, we don't define HAS_EXECINFO to 0 when it is not
available.
[1] https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/Makefile.in?id=cdb07d2cd52af39feb425e6d36c02b30916b9f0a#n224
[2] https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/Makefile.in?id=cdb07d2cd52af39feb425e6d36c02b30916b9f0a#n277
---
modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp b/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp
index 8d50e6a8e..9be48755a 100644
--- a/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp
+++ b/modules/libcom/src/osi/os/posix/osdExecinfoBackTrace.cpp
@@ -12,10 +12,13 @@
#include
// execinfo.h may not be present if uclibc is configured to omit backtrace()
-#if !defined(__UCLIBC_MAJOR__) || defined(__UCLIBC_HAS_EXECINFO__)
+// some C libraries, such as musl, don't have execinfo.h at all
+#if defined(__has_include)
+# if __has_include()
+# define HAS_EXECINFO 1
+# endif
+#elif !defined(__UCLIBC_MAJOR__) || defined(__UCLIBC_HAS_EXECINFO__)
# define HAS_EXECINFO 1
-#else
-# define HAS_EXECINFO 0
#endif
#if HAS_EXECINFO
From 45b3bce51534178a0baaf3912a17827caa1b000d Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Tue, 23 May 2023 10:30:31 -0700
Subject: [PATCH 08/66] epicsThreadShow() zombies
Flag when the thread has returned, but the tracking
struct is still around. eg. in need of joining.
---
modules/libcom/src/osi/os/Linux/osdThread.h | 1 +
modules/libcom/src/osi/os/Linux/osdThreadExtra.c | 6 ++++--
modules/libcom/src/osi/os/RTEMS-score/osdThread.c | 9 ++++++++-
modules/libcom/src/osi/os/WIN32/osdThread.c | 6 ++++++
modules/libcom/src/osi/os/posix/osdThread.c | 3 +++
modules/libcom/src/osi/os/posix/osdThread.h | 1 +
modules/libcom/src/osi/os/posix/osdThreadExtra.c | 6 ++++--
7 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/modules/libcom/src/osi/os/Linux/osdThread.h b/modules/libcom/src/osi/os/Linux/osdThread.h
index 7f0a8bbc9..4bf7f29a6 100644
--- a/modules/libcom/src/osi/os/Linux/osdThread.h
+++ b/modules/libcom/src/osi/os/Linux/osdThread.h
@@ -36,6 +36,7 @@ typedef struct epicsThreadOSD {
int isEpicsThread;
int isRealTimeScheduled;
int isOnThreadList;
+ int isRunning;
unsigned int osiPriority;
int joinable;
char name[1]; /* actually larger */
diff --git a/modules/libcom/src/osi/os/Linux/osdThreadExtra.c b/modules/libcom/src/osi/os/Linux/osdThreadExtra.c
index 813846bc2..e10c6fcc5 100644
--- a/modules/libcom/src/osi/os/Linux/osdThreadExtra.c
+++ b/modules/libcom/src/osi/os/Linux/osdThreadExtra.c
@@ -23,6 +23,7 @@
#include
#include
+#include "epicsAtomic.h"
#include "epicsStdio.h"
#include "ellLib.h"
#include "epicsEvent.h"
@@ -45,11 +46,12 @@ void epicsThreadShowInfo(epicsThreadId pthreadInfo, unsigned int level)
if (!status)
priority = param.sched_priority;
}
- fprintf(epicsGetStdout(),"%16.16s %14p %8lu %3d%8d %8.8s\n",
+ fprintf(epicsGetStdout(),"%16.16s %14p %8lu %3d%8d %8.8s%s\n",
pthreadInfo->name,(void *)
pthreadInfo,(unsigned long)pthreadInfo->lwpId,
pthreadInfo->osiPriority,priority,
- pthreadInfo->isSuspended ? "SUSPEND" : "OK");
+ pthreadInfo->isSuspended ? "SUSPEND" : "OK",
+ epicsAtomicGetIntT(&pthreadInfo->isRunning) ? "" : " ZOMBIE");
}
}
diff --git a/modules/libcom/src/osi/os/RTEMS-score/osdThread.c b/modules/libcom/src/osi/os/RTEMS-score/osdThread.c
index 110951445..7d37bbe48 100644
--- a/modules/libcom/src/osi/os/RTEMS-score/osdThread.c
+++ b/modules/libcom/src/osi/os/RTEMS-score/osdThread.c
@@ -53,6 +53,7 @@ struct taskVar {
rtems_id join_barrier; /* only valid if joinable */
int refcnt;
int joinable;
+ int isRunning;
EPICSTHREADFUNC funptr;
void *parm;
unsigned int threadVariableCapacity;
@@ -197,6 +198,7 @@ threadWrapper (rtems_task_argument arg)
osdThreadHooksRun((epicsThreadId)v->id);
(*v->funptr)(v->parm);
epicsExitCallAtThreadExits ();
+ epicsAtomicSetIntT(&v->isRunning, 0);
taskVarLock ();
if (v->back)
v->back->forw = v->forw;
@@ -239,6 +241,7 @@ setThreadInfo(rtems_id tid, const char *name, EPICSTHREADFUNC funptr,
v->refcnt = joinable ? 2 : 1;
v->threadVariableCapacity = 0;
v->threadVariables = NULL;
+ v->isRunning = 1;
if (joinable) {
char c[3];
strncpy(c, v->name, 3);
@@ -797,7 +800,11 @@ epicsThreadShowInfo (struct taskVar *v, unsigned int level)
fprintf(epicsGetStdout(),"+--------+-----------+--------+--------+---------------------+\n");
} else {
fprintf(epicsGetStdout(),"%9.8x", (int)v->id);
- showInternalTaskInfo (v->id);
+ if(epicsAtomicGetIntT(&v->isRunning)) {
+ showInternalTaskInfo (v->id);
+ } else {
+ fprintf(epicsGetStdout(),"%-30s", " *** ZOMBIE task! ***");
+ }
fprintf(epicsGetStdout()," %s\n", v->name);
}
}
diff --git a/modules/libcom/src/osi/os/WIN32/osdThread.c b/modules/libcom/src/osi/os/WIN32/osdThread.c
index 1cfeb3767..5e9eeb435 100644
--- a/modules/libcom/src/osi/os/WIN32/osdThread.c
+++ b/modules/libcom/src/osi/os/WIN32/osdThread.c
@@ -102,6 +102,7 @@ typedef struct epicsThreadOSD {
unsigned epicsPriority;
char isSuspended;
int joinable;
+ int isRunning;
HANDLE timer; /* waitable timer */
} win32ThreadParam;
@@ -519,6 +520,8 @@ static unsigned WINAPI epicsWin32ThreadEntry ( LPVOID lpParameter )
epicsExitCallAtThreadExits ();
+ epicsAtomicSetIntT(&pParm->isRunning, 0);
+
/* On Windows we could omit this and rely on the callback given to FlsAlloc() to free.
* However < vista doesn't implement FLS at all, and WINE (circa 5.0.3) doesn't
* implement fully (dtor never runs). So for EPICS threads, we explicitly
@@ -540,6 +543,7 @@ static win32ThreadParam * epicsThreadParmCreate ( const char *pName )
pParmWIN32->pName = (char *) ( pParmWIN32 + 1 );
strcpy ( pParmWIN32->pName, pName );
pParmWIN32->isSuspended = 0;
+ pParmWIN32->isRunning = 1;
epicsAtomicIncrIntT(&pParmWIN32->refcnt);
#ifdef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
pParmWIN32->timer = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS);
@@ -1044,6 +1048,8 @@ static void epicsThreadShowInfo ( epicsThreadId id, unsigned level )
fprintf (epicsGetStdout(), " %-8p %-8p ",
(void *) pParm->handle, (void *) pParm->parm );
}
+ if(!epicsAtomicGetIntT(&pParm->isRunning))
+ fprintf (epicsGetStdout(), " ZOMBIE");
}
else {
fprintf (epicsGetStdout(),
diff --git a/modules/libcom/src/osi/os/posix/osdThread.c b/modules/libcom/src/osi/os/posix/osdThread.c
index 5fa85d4a5..6142e73c5 100644
--- a/modules/libcom/src/osi/os/posix/osdThread.c
+++ b/modules/libcom/src/osi/os/posix/osdThread.c
@@ -173,6 +173,7 @@ static epicsThreadOSD * create_threadInfo(const char *name)
pthreadInfo = calloc(1,sizeof(*pthreadInfo) + strlen(name));
if(!pthreadInfo)
return NULL;
+ pthreadInfo->isRunning = 1;
pthreadInfo->suspendEvent = epicsEventCreate(epicsEventEmpty);
if(!pthreadInfo->suspendEvent){
free(pthreadInfo);
@@ -441,6 +442,8 @@ static void * start_routine(void *arg)
(*pthreadInfo->createFunc)(pthreadInfo->createArg);
epicsExitCallAtThreadExits ();
+
+ epicsAtomicSetIntT(&pthreadInfo->isRunning, 0);
return(0);
}
diff --git a/modules/libcom/src/osi/os/posix/osdThread.h b/modules/libcom/src/osi/os/posix/osdThread.h
index 58065a497..ea49030a3 100644
--- a/modules/libcom/src/osi/os/posix/osdThread.h
+++ b/modules/libcom/src/osi/os/posix/osdThread.h
@@ -34,6 +34,7 @@ typedef struct epicsThreadOSD {
int isEpicsThread;
int isRealTimeScheduled;
int isOnThreadList;
+ int isRunning;
unsigned int osiPriority;
int joinable;
char name[1]; /* actually larger */
diff --git a/modules/libcom/src/osi/os/posix/osdThreadExtra.c b/modules/libcom/src/osi/os/posix/osdThreadExtra.c
index 0fb3023f2..4344110f0 100644
--- a/modules/libcom/src/osi/os/posix/osdThreadExtra.c
+++ b/modules/libcom/src/osi/os/posix/osdThreadExtra.c
@@ -12,6 +12,7 @@
/* This is part of the posix implementation of epicsThread */
+#include "epicsAtomic.h"
#include "epicsStdio.h"
#include "ellLib.h"
#include "epicsEvent.h"
@@ -35,11 +36,12 @@ void epicsThreadShowInfo(epicsThreadOSD *pthreadInfo, unsigned int level)
status = pthread_getschedparam(pthreadInfo->tid,&policy,¶m);
if(!status) priority = param.sched_priority;
}
- fprintf(epicsGetStdout(),"%16.16s %14p %12lu %3d%8d %8.8s\n",
+ fprintf(epicsGetStdout(),"%16.16s %14p %12lu %3d%8d %8.8s%s\n",
pthreadInfo->name,(void *)
pthreadInfo,(unsigned long)pthreadInfo->tid,
pthreadInfo->osiPriority,priority,
- pthreadInfo->isSuspended?"SUSPEND":"OK");
+ pthreadInfo->isSuspended?"SUSPEND":"OK",
+ epicsAtomicGetIntT(&pthreadInfo->isRunning) ? "" : " ZOMBIE");
}
}
From 8c08c5724725ca8012093672fa6fa16984c05875 Mon Sep 17 00:00:00 2001
From: Emilio Perez
Date: Wed, 8 Mar 2023 14:48:02 +0000
Subject: [PATCH 09/66] Allow adding error symbols after early initialization
This was acomplished by making errSymbolAdd add the error symbol directly
into the global hash table and removing errnumlist which is not needed
anymore.
Unit tests were added for checking the following cases:
- Adding a valid symbol and checking that it exists (fixed by this change)
- Getting an existing error symbol
- Getting a non existing error symbol
- Adding an invalid error symbol (fixed by this change)
- Adding an error symbol with a code that already
exists (fixed by this change)
Therefore, issue #268 was fixed
error: statically allocate error symbol hash table
This will allow calling errSymbolAdd before errSymBld, therefore, a
function adding error symbols can now be run before iocInit or errlogInit
error: add a constant for the minimum module number
Make adding an identical error symbol not fail
A test case was also added which test that adding an error symbol
with same error code and message as one added before will not fail
Add locking to error symbol table
This protects the cases of:
- simultaneously adding and requesting of an error symbol
- simultaneously adding many error symbols
Update release notes regarding adding error symbols
---
documentation/RELEASE_NOTES.md | 4 +
modules/libcom/src/error/Makefile | 1 +
modules/libcom/src/error/errMdef.h | 2 +
modules/libcom/src/error/errSymLib.c | 141 ++++++++++++++------------
modules/libcom/src/error/errSymTbl.h | 3 +
modules/libcom/test/epicsErrlogTest.c | 58 ++++++++++-
6 files changed, 143 insertions(+), 66 deletions(-)
diff --git a/documentation/RELEASE_NOTES.md b/documentation/RELEASE_NOTES.md
index e0ec809b8..75922f874 100644
--- a/documentation/RELEASE_NOTES.md
+++ b/documentation/RELEASE_NOTES.md
@@ -102,6 +102,10 @@ and string formats, some of which support full nanosecond precision.
More information is included in the filters documentation, which can be found in
the `html/filters.html` document that is generated during the build
+### Allow adding new error symbols at any time
+
+`errSymbolAdd` can now be called after early initialization.
+
### Add conditional output (OOPT) to the longout record
The longout record can now be configured using its new OOPT and OOCH fields
diff --git a/modules/libcom/src/error/Makefile b/modules/libcom/src/error/Makefile
index 570974b95..f98ed4b24 100644
--- a/modules/libcom/src/error/Makefile
+++ b/modules/libcom/src/error/Makefile
@@ -26,6 +26,7 @@ ERR_S_FILES += $(LIBCOM)/as/asLib.h
ERR_S_FILES += $(LIBCOM)/misc/epicsStdlib.h
ERR_S_FILES += $(LIBCOM)/pool/epicsThreadPool.h
ERR_S_FILES += $(LIBCOM)/error/errMdef.h
+ERR_S_FILES += $(LIBCOM)/error/errSymTbl.h
ERR_S_FILES += $(TOP)/modules/database/src/ioc/db/dbAccessDefs.h
ERR_S_FILES += $(TOP)/modules/database/src/ioc/dbStatic/dbStaticLib.h
diff --git a/modules/libcom/src/error/errMdef.h b/modules/libcom/src/error/errMdef.h
index 1a9c47e33..9c07353c4 100644
--- a/modules/libcom/src/error/errMdef.h
+++ b/modules/libcom/src/error/errMdef.h
@@ -19,6 +19,7 @@
#define RTN_SUCCESS(STATUS) ((STATUS)==0)
/* Module numbers start above 500 for compatibility with vxWorks errnoLib */
+#define MIN_MODULE_NUM 501
/* FIXME: M_xxx values could be declared as integer variables and set
* at runtime from registration routines; the S_xxx definitions would
@@ -32,6 +33,7 @@
#define M_stdlib (504 << 16) /* EPICS Standard library */
#define M_pool (505 << 16) /* Thread pool */
#define M_time (506 << 16) /* epicsTime */
+#define M_err (507 << 16) /* Error */
/* ioc */
#define M_dbAccess (511 << 16) /* Database Access Routines */
diff --git a/modules/libcom/src/error/errSymLib.c b/modules/libcom/src/error/errSymLib.c
index f3162cf66..a47208062 100644
--- a/modules/libcom/src/error/errSymLib.c
+++ b/modules/libcom/src/error/errSymLib.c
@@ -23,6 +23,8 @@
#include "cantProceed.h"
#include "epicsAssert.h"
#include "epicsStdio.h"
+#include "epicsMutex.h"
+#include "epicsThread.h"
#include "errMdef.h"
#include "errSymTbl.h"
#include "ellLib.h"
@@ -33,69 +35,49 @@
static epicsUInt16 errhash(long errNum);
typedef struct errnumnode {
- ELLNODE node;
long errNum;
struct errnumnode *hashnode;
const char *message;
long pad;
} ERRNUMNODE;
-static ELLLIST errnumlist = ELLLIST_INIT;
-static ERRNUMNODE **hashtable;
-static int initialized = 0;
+typedef struct {
+ ERRNUMNODE *table[NHASH * sizeof(ERRNUMNODE *)];
+ epicsMutexId tableMutexId;
+} errHashTable_t;
+
+static errHashTable_t errHashTable;
+
extern ERRSYMTAB_ID errSymTbl;
/****************************************************************
* ERRSYMBLD
*
- * Create the normal ell LIST of sorted error messages nodes
- * Followed by linked hash lists - that link together those
- * ell nodes that have a common hash number.
+ * Populate EPICS error symbols
*
***************************************************************/
int errSymBld(void)
{
- ERRSYMBOL *errArray = errSymTbl->symbols;
- ERRNUMNODE *perrNumNode = NULL;
- ERRNUMNODE *pNextNode = NULL;
- ERRNUMNODE **phashnode = NULL;
- int i;
- int modnum;
-
- if (initialized)
- return(0);
-
- hashtable = (ERRNUMNODE**)callocMustSucceed
- (NHASH, sizeof(ERRNUMNODE*),"errSymBld");
+ ERRSYMBOL *errArray = errSymTbl->symbols;
+ int i;
for (i = 0; i < errSymTbl->nsymbols; i++, errArray++) {
- modnum = errArray->errNum >> 16;
- if (modnum < 501) {
- fprintf(stderr, "errSymBld: ERROR - Module number in errSymTbl < 501 was Module=%lx Name=%s\n",
- errArray->errNum, errArray->name);
- continue;
- }
- if ((errSymbolAdd(errArray->errNum, errArray->name)) < 0) {
+ if (errSymbolAdd(errArray->errNum, errArray->name)) {
fprintf(stderr, "errSymBld: ERROR - errSymbolAdd() failed \n");
- continue;
}
}
- perrNumNode = (ERRNUMNODE *) ellFirst(&errnumlist);
- while (perrNumNode) {
- /* hash each perrNumNode->errNum */
- epicsUInt16 hashInd = errhash(perrNumNode->errNum);
- phashnode = (ERRNUMNODE**)&hashtable[hashInd];
- pNextNode = (ERRNUMNODE*) *phashnode;
- /* search for last node (NULL) of hashnode linked list */
- while (pNextNode) {
- phashnode = &pNextNode->hashnode;
- pNextNode = *phashnode;
- }
- *phashnode = perrNumNode;
- perrNumNode = (ERRNUMNODE *) ellNext((ELLNODE *) perrNumNode);
- }
- initialized = 1;
- return(0);
+ return 0;
+}
+
+static void _initErrorHashTable(void *_unused)
+{
+ errHashTable.tableMutexId = epicsMutexMustCreate();
+}
+
+static void initErrorHashTable()
+{
+ static epicsThreadOnceId initErrSymOnceFlag = EPICS_THREAD_ONCE_INIT;
+ epicsThreadOnce(&initErrSymOnceFlag, _initErrorHashTable, NULL);
}
/****************************************************************
@@ -109,21 +91,48 @@ static epicsUInt16 errhash(long errNum)
modnum = (unsigned short) (errNum >> 16);
errnum = (unsigned short) (errNum & 0xffff);
- return (((modnum - 500) * 20) + errnum) % NHASH;
+ return ((modnum - (MIN_MODULE_NUM - 1)) * 20 + errnum) % NHASH;
}
/****************************************************************
* ERRSYMBOLADD
- * adds symbols to the master errnumlist as compiled from errSymTbl.c
+ * adds symbols to the global error symbol hash table
***************************************************************/
int errSymbolAdd(long errNum, const char *name)
{
- ERRNUMNODE *pNew = (ERRNUMNODE*) callocMustSucceed(1,
- sizeof(ERRNUMNODE), "errSymbolAdd");
+ ERRNUMNODE *pNextNode = NULL;
+ ERRNUMNODE **phashnode = NULL;
+ ERRNUMNODE *pNew = NULL;
+ int modnum = (epicsUInt16) (errNum >> 16);
+ if (modnum < MIN_MODULE_NUM)
+ return S_err_invCode;
+
+ initErrorHashTable();
+
+ epicsUInt16 hashInd = errhash(errNum);
+
+ epicsMutexLock(errHashTable.tableMutexId);
+ phashnode = (ERRNUMNODE**)&errHashTable.table[hashInd];
+ pNextNode = (ERRNUMNODE*) *phashnode;
+
+ /* search for last node (NULL) of hashnode linked list */
+ while (pNextNode) {
+ if (pNextNode->errNum == errNum) {
+ int notIdentical = strcmp(name, pNextNode->message);
+ epicsMutexUnlock(errHashTable.tableMutexId);
+ return notIdentical ? S_err_codeExists : 0;
+ }
+ phashnode = &pNextNode->hashnode;
+ pNextNode = *phashnode;
+ }
+
+ pNew = (ERRNUMNODE*) callocMustSucceed(
+ 1, sizeof(ERRNUMNODE), "errSymbolAdd");
pNew->errNum = errNum;
pNew->message = name;
- ellAdd(&errnumlist, (ELLNODE*)pNew);
+ *phashnode = pNew;
+ epicsMutexUnlock(errHashTable.tableMutexId);
return 0;
}
@@ -155,31 +164,31 @@ const char* errSymLookupInternal(long status)
if (!status)
return "Ok";
- if (!initialized)
- errSymBld();
+ initErrorHashTable();
modNum = (unsigned) status;
modNum >>= 16;
modNum &= 0xffff;
- if (modNum <= 500) {
- const char * pStr = strerror ((int) status);
- if (pStr) {
- return pStr;
- }
+ if (modNum < MIN_MODULE_NUM) {
+ return strerror ((int) status);
}
else {
unsigned hashInd = errhash(status);
- phashnode = (ERRNUMNODE**)&hashtable[hashInd];
+ const char *result = NULL;
+ epicsMutexLock(errHashTable.tableMutexId);
+ phashnode = (ERRNUMNODE**)&errHashTable.table[hashInd];
pNextNode = *phashnode;
while (pNextNode) {
if (pNextNode->errNum==status){
- return pNextNode->message;
+ result = pNextNode->message;
+ break;
}
phashnode = &pNextNode->hashnode;
pNextNode = *phashnode;
}
+ epicsMutexUnlock(errHashTable.tableMutexId);
+ return result;
}
- return NULL;
}
const char* errSymMsg(long status)
@@ -210,12 +219,12 @@ void errSymDump(void)
int i;
int msgcount = 0;
- if (!initialized) errSymBld();
+ initErrorHashTable();
msgcount = 0;
printf("errSymDump: number of hash slots = %d\n", NHASH);
for (i = 0; i < NHASH; i++) {
- ERRNUMNODE **phashnode = &hashtable[i];
+ ERRNUMNODE **phashnode = &errHashTable.table[i];
ERRNUMNODE *pNextNode = *phashnode;
int count = 0;
@@ -246,14 +255,15 @@ void errSymTestPrint(long errNum)
epicsUInt16 modnum;
epicsUInt16 errnum;
- if (!initialized) errSymBld();
+ initErrorHashTable();
message[0] = '\0';
modnum = (epicsUInt16) (errNum >> 16);
errnum = (epicsUInt16) (errNum & 0xffff);
- if (modnum < 501) {
+ if (modnum < MIN_MODULE_NUM) {
fprintf(stderr, "Usage: errSymTestPrint(long errNum) \n");
- fprintf(stderr, "errSymTestPrint: module number < 501 \n");
+ fprintf(stderr,
+ "errSymTestPrint: module number < %d\n", MIN_MODULE_NUM);
return;
}
errSymLookup(errNum, message, sizeof(message));
@@ -271,10 +281,11 @@ void errSymTest(epicsUInt16 modnum, epicsUInt16 begErrNum,
long errNum;
epicsUInt16 errnum;
- if (!initialized) errSymBld();
- if (modnum < 501)
+ if (modnum < MIN_MODULE_NUM)
return;
+ initErrorHashTable();
+
/* print range of error messages */
for (errnum = begErrNum; errnum <= endErrNum; errnum++) {
errNum = modnum << 16;
diff --git a/modules/libcom/src/error/errSymTbl.h b/modules/libcom/src/error/errSymTbl.h
index 89d74316c..1b39ff6f3 100644
--- a/modules/libcom/src/error/errSymTbl.h
+++ b/modules/libcom/src/error/errSymTbl.h
@@ -16,6 +16,9 @@
#include "libComAPI.h"
#include "epicsTypes.h"
+#define S_err_invCode (M_err | 1) /* Invalid error symbol code */
+#define S_err_codeExists (M_err | 2) /* Error code already exists */
+
/* ERRSYMBOL - entry in symbol table */
typedef struct {
long errNum; /* errMessage symbol number */
diff --git a/modules/libcom/test/epicsErrlogTest.c b/modules/libcom/test/epicsErrlogTest.c
index 16a04a73c..3638da34d 100644
--- a/modules/libcom/test/epicsErrlogTest.c
+++ b/modules/libcom/test/epicsErrlogTest.c
@@ -29,6 +29,7 @@
#include "osiSock.h"
#include "fdmgr.h"
#include "epicsString.h"
+#include "errSymTbl.h"
/* private between errlog.c and this test */
LIBCOM_API
@@ -197,13 +198,61 @@ void testANSIStrip(void)
#undef testEscape
}
+static void testErrorMessageMatches(long status, const char *expected)
+{
+ const char *msg = errSymMsg(status);
+ testOk(strcmp(msg, expected) == 0,
+ "Error code %ld returns \"%s\", expected message \"%s\"", status,
+ msg, expected
+ );
+}
+
+static void testGettingExistingErrorSymbol()
+{
+ testErrorMessageMatches(S_err_invCode, "Invalid error symbol code");
+}
+
+static void testGettingNonExistingErrorSymbol()
+{
+ long invented_code = (0x7999 << 16) | 0x9998;
+ testErrorMessageMatches(invented_code, "");
+}
+
+static void testAddingErrorSymbol()
+{
+ long invented_code = (0x7999 << 16) | 0x9999;
+ errSymbolAdd(invented_code, "Invented Error Message");
+ testErrorMessageMatches(invented_code, "Invented Error Message");
+}
+
+static void testAddingInvalidErrorSymbol()
+{
+ long invented_code = (500 << 16) | 0x1;
+ testOk(errSymbolAdd(invented_code, "No matter"),
+ "Adding error symbol with module code < 501 should fail");
+}
+
+static void testAddingExistingErrorSymbol()
+{
+ testOk(errSymbolAdd(S_err_invCode, "Duplicate"),
+ "Adding an error symbol with an existing error code should fail");
+}
+
+static void testAddingExistingErrorSymbolWithSameMessage()
+{
+ long invented_code = (0x7999 << 16) | 0x9997;
+ errSymbolAdd(invented_code, "Invented Error Message");
+ testOk(!errSymbolAdd(invented_code, "Invented Error Message"),
+ "Adding identical error symbol shouldn't fail");
+}
+
MAIN(epicsErrlogTest)
{
size_t mlen, i, N;
char msg[256];
clientPvt pvt, pvt2;
- testPlan(48);
+ testPlan(54);
testANSIStrip();
@@ -414,6 +463,13 @@ MAIN(epicsErrlogTest)
testLogPrefix();
osiSockRelease();
+ testGettingExistingErrorSymbol();
+ testGettingNonExistingErrorSymbol();
+ testAddingErrorSymbol();
+ testAddingInvalidErrorSymbol();
+ testAddingExistingErrorSymbol();
+ testAddingExistingErrorSymbolWithSameMessage();
+
return testDone();
}
/*
From 88ea1507f46daabb0520d773193b271ac6c9b56d Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Mon, 28 Aug 2023 14:00:27 +0200
Subject: [PATCH 10/66] Fix compile w/ vs2012
---
modules/libcom/src/error/errSymLib.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/modules/libcom/src/error/errSymLib.c b/modules/libcom/src/error/errSymLib.c
index a47208062..37e8102cd 100644
--- a/modules/libcom/src/error/errSymLib.c
+++ b/modules/libcom/src/error/errSymLib.c
@@ -23,6 +23,7 @@
#include "cantProceed.h"
#include "epicsAssert.h"
#include "epicsStdio.h"
+#include "epicsTypes.h"
#include "epicsMutex.h"
#include "epicsThread.h"
#include "errMdef.h"
@@ -104,14 +105,13 @@ int errSymbolAdd(long errNum, const char *name)
ERRNUMNODE **phashnode = NULL;
ERRNUMNODE *pNew = NULL;
int modnum = (epicsUInt16) (errNum >> 16);
+ epicsUInt16 hashInd = errhash(errNum);
if (modnum < MIN_MODULE_NUM)
return S_err_invCode;
initErrorHashTable();
- epicsUInt16 hashInd = errhash(errNum);
-
epicsMutexLock(errHashTable.tableMutexId);
phashnode = (ERRNUMNODE**)&errHashTable.table[hashInd];
pNextNode = (ERRNUMNODE*) *phashnode;
From 60fa2d31da2deaaa89636b4a1f5b852efe0b2699 Mon Sep 17 00:00:00 2001
From: Jeremy Lorelli
Date: Tue, 18 Jul 2023 16:24:37 -0700
Subject: [PATCH 11/66] libCom: Fix buggy pointer dereference in postfix()
---
modules/libcom/src/calc/postfix.c | 8 ++++++++
modules/libcom/test/epicsCalcTest.cpp | 4 +++-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/modules/libcom/src/calc/postfix.c b/modules/libcom/src/calc/postfix.c
index 7457abaab..d9dce515d 100644
--- a/modules/libcom/src/calc/postfix.c
+++ b/modules/libcom/src/calc/postfix.c
@@ -335,6 +335,10 @@ LIBCOM_API long
break;
case SEPERATOR:
+ if (pstacktop == stack) {
+ *perror = CALC_ERR_BAD_SEPERATOR;
+ goto bad;
+ }
/* Move operators to the output until open paren */
while (pstacktop->name[0] != '(') {
if (pstacktop <= stack+1) {
@@ -353,6 +357,10 @@ LIBCOM_API long
break;
case CLOSE_PAREN:
+ if (pstacktop == stack) {
+ *perror = CALC_ERR_PAREN_NOT_OPEN;
+ goto bad;
+ }
/* Move operators to the output until matching paren */
while (pstacktop->name[0] != '(') {
if (pstacktop <= stack+1) {
diff --git a/modules/libcom/test/epicsCalcTest.cpp b/modules/libcom/test/epicsCalcTest.cpp
index 870547e4d..4f0bfbecb 100644
--- a/modules/libcom/test/epicsCalcTest.cpp
+++ b/modules/libcom/test/epicsCalcTest.cpp
@@ -298,7 +298,7 @@ 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(635);
+ testPlan(637);
/* LITERAL_OPERAND elements */
testExpr(0);
@@ -953,6 +953,8 @@ MAIN(epicsCalcTest)
testBadExpr("1?", CALC_ERR_CONDITIONAL);
testBadExpr("1?1", CALC_ERR_CONDITIONAL);
testBadExpr(":1", CALC_ERR_SYNTAX);
+ testBadExpr("0,", CALC_ERR_BAD_SEPERATOR);
+ testBadExpr("0)", CALC_ERR_PAREN_NOT_OPEN);
// Bit manipulations wrt bit 31 (bug lp:1514520)
// using integer literals
From 766c9906b55dec0b723774907ec2f2ffd70a7352 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 22 Oct 2023 13:35:45 -0700
Subject: [PATCH 12/66] update ci-scripts
---
.ci | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.ci b/.ci
index 1e0e326f7..899b18336 160000
--- a/.ci
+++ b/.ci
@@ -1 +1 @@
-Subproject commit 1e0e326f74ffac4154ce80b5d41c410c754cf5d8
+Subproject commit 899b18336b4ce3bd9328fd30c33621224c78a4d7
From badd8f518d3753b70acc6d30a28ad873fd4b0d15 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 22 Oct 2023 17:42:19 -0700
Subject: [PATCH 13/66] update modules/pvData
---
modules/pvData | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/pvData b/modules/pvData
index 2547514ab..13e4e577b 160000
--- a/modules/pvData
+++ b/modules/pvData
@@ -1 +1 @@
-Subproject commit 2547514abb90875bf47dda728ecaa9e30a5ab354
+Subproject commit 13e4e577bbc197ee7fe27e858e6ecd88d5265b45
From 6dec68554c30daab64823ef57e7757002cf4f644 Mon Sep 17 00:00:00 2001
From: AlexWells
Date: Wed, 8 Mar 2023 15:56:01 +0000
Subject: [PATCH 14/66] iocsh: Add underline separator between help outputs
Also tweaks the overall format of the message a bit.
Add tests for new help output format
---
modules/libcom/src/error/errlog.h | 16 +--
modules/libcom/src/iocsh/iocsh.cpp | 15 ++-
modules/libcom/test/Makefile | 1 +
modules/libcom/test/iocshTest.cpp | 109 ++++++++++++++++++++-
modules/libcom/test/iocshTestHelpFunction1 | 4 +
modules/libcom/test/iocshTestHelpFunctions | 14 +++
6 files changed, 147 insertions(+), 12 deletions(-)
create mode 100644 modules/libcom/test/iocshTestHelpFunction1
create mode 100644 modules/libcom/test/iocshTestHelpFunctions
diff --git a/modules/libcom/src/error/errlog.h b/modules/libcom/src/error/errlog.h
index 32e2da7a5..4aaf054ac 100644
--- a/modules/libcom/src/error/errlog.h
+++ b/modules/libcom/src/error/errlog.h
@@ -285,14 +285,16 @@ LIBCOM_API void errSymLookup(long status, char *pBuf, size_t bufLength);
#define ANSI_ESC_MAGENTA "\033[35;1m"
#define ANSI_ESC_CYAN "\033[36;1m"
#define ANSI_ESC_BOLD "\033[1m"
+#define ANSI_ESC_UNDERLINE "\033[4m"
#define ANSI_ESC_RESET "\033[0m"
-#define ANSI_RED(STR) ANSI_ESC_RED STR ANSI_ESC_RESET
-#define ANSI_GREEN(STR) ANSI_ESC_GREEN STR ANSI_ESC_RESET
-#define ANSI_YELLOW(STR) ANSI_ESC_YELLOW STR ANSI_ESC_RESET
-#define ANSI_BLUE(STR) ANSI_ESC_BLUE STR ANSI_ESC_RESET
-#define ANSI_MAGENTA(STR) ANSI_ESC_MAGENTA STR ANSI_ESC_RESET
-#define ANSI_CYAN(STR) ANSI_ESC_CYAN STR ANSI_ESC_RESET
-#define ANSI_BOLD(STR) ANSI_ESC_BOLD STR ANSI_ESC_RESET
+#define ANSI_RED(STR) ANSI_ESC_RED STR ANSI_ESC_RESET
+#define ANSI_GREEN(STR) ANSI_ESC_GREEN STR ANSI_ESC_RESET
+#define ANSI_YELLOW(STR) ANSI_ESC_YELLOW STR ANSI_ESC_RESET
+#define ANSI_BLUE(STR) ANSI_ESC_BLUE STR ANSI_ESC_RESET
+#define ANSI_MAGENTA(STR) ANSI_ESC_MAGENTA STR ANSI_ESC_RESET
+#define ANSI_CYAN(STR) ANSI_ESC_CYAN STR ANSI_ESC_RESET
+#define ANSI_BOLD(STR) ANSI_ESC_BOLD STR ANSI_ESC_RESET
+#define ANSI_UNDERLINE(STR) ANSI_ESC_UNDERLINE STR ANSI_ESC_RESET
#define ERL_ERROR ANSI_RED("ERROR")
#define ERL_WARNING ANSI_MAGENTA("WARNING")
/** @} */
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index 7c9551845..461780513 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -888,15 +888,19 @@ static void helpCallFunc(const iocshArgBuf *args)
"Type 'help ' to see the arguments of . eg. 'help db*'\n");
}
else {
+ bool firstFunction = true;
for (int iarg = 1 ; iarg < argc ; iarg++) {
for (pcmd = iocshCommandHead ; pcmd != NULL ; pcmd = pcmd->next) {
piocshFuncDef = pcmd->def.pFuncDef;
if (epicsStrGlobMatch(piocshFuncDef->name, argv[iarg]) != 0) {
- if(piocshFuncDef->usage) {
- fputs("\nUsage: ", epicsGetStdout());
+
+ if (! firstFunction) {
+ fprintf(epicsGetStdout(),
+ ANSI_UNDERLINE(" \n"));
}
+
fprintf(epicsGetStdout(),
- ANSI_BOLD("%s"),
+ ANSI_BOLD("\n%s"),
piocshFuncDef->name);
for (int a = 0 ; a < piocshFuncDef->nargs ; a++) {
@@ -909,11 +913,14 @@ static void helpCallFunc(const iocshArgBuf *args)
fprintf(epicsGetStdout(), " '%s'", cp);
}
}
- fprintf(epicsGetStdout(),"\n");;
+ fprintf(epicsGetStdout(),"\n");
if(piocshFuncDef->usage) {
fprintf(epicsGetStdout(), "\n%s", piocshFuncDef->usage);
}
+
+ firstFunction = false;
}
+
}
}
}
diff --git a/modules/libcom/test/Makefile b/modules/libcom/test/Makefile
index 809ae7338..a3893661f 100644
--- a/modules/libcom/test/Makefile
+++ b/modules/libcom/test/Makefile
@@ -269,6 +269,7 @@ TESTPROD_HOST += iocshTest
iocshTest_SRCS += iocshTest.cpp
TESTS += iocshTest
TESTFILES += $(wildcard ../iocshTest*.cmd)
+TESTFILES += ../iocshTestHelpFunction1 ../iocshTestHelpFunctions
TESTPROD_HOST += epicsLoadTest
epicsLoadTest_SRCS += epicsLoadTest.cpp
diff --git a/modules/libcom/test/iocshTest.cpp b/modules/libcom/test/iocshTest.cpp
index 7ca5c3867..4b98112da 100644
--- a/modules/libcom/test/iocshTest.cpp
+++ b/modules/libcom/test/iocshTest.cpp
@@ -8,6 +8,8 @@
#include
#include
#include
+#include
+#include
#include
#include
@@ -83,14 +85,117 @@ void assertCallFunc(const iocshArgBuf *args)
iocshSetError(args[0].ival);
}
+const iocshFuncDef testHelpFunction1Def = {"testHelpFunction1",0,0,
+ "Usage message of testHelpFunction1\n"};
+const iocshFuncDef testHelpFunction2Def = {"testHelpFunction2",0,0,
+ "Usage message of testHelpFunction2\n"};
+const iocshFuncDef testHelpFunction3Def = {"testHelpFunction3",0,0,
+ "Usage message of testHelpFunction3\n"};
+void doNothing(const iocshArgBuf *args)
+{
+ return;
+}
+
+std::string readFile(std::string filename)
+{
+ std::ifstream t(filename.c_str());
+ std::stringstream buffer;
+
+ if (!t.is_open()) {
+ throw std::invalid_argument("Could not open filename " + filename);
+ }
+
+ buffer << t.rdbuf();
+ return buffer.str();
+}
+
+bool compareFiles(const std::string& p1, const std::string& p2)
+{
+ std::ifstream f1(p1.c_str(), std::ifstream::binary|std::ifstream::ate);
+ std::ifstream f2(p2.c_str(), std::ifstream::binary|std::ifstream::ate);
+
+ if (f1.fail() || f2.fail()) {
+ testDiag("One or more files failed to open");
+ testDiag("f1.fail(): %d f2.fail(): %d", f1.fail(), f2.fail());
+ return false; // File problem
+ }
+
+ if (f1.tellg() != f2.tellg()) {
+ testDiag("File sizes did not match");
+ return false; // Size mismatch
+ }
+
+ // Seek back to beginning and use std::equal to compare contents
+ f1.seekg(0, std::ifstream::beg);
+ f2.seekg(0, std::ifstream::beg);
+
+ bool are_equal = std::equal(
+ std::istreambuf_iterator(f1.rdbuf()),
+ std::istreambuf_iterator(),
+ std::istreambuf_iterator(f2.rdbuf())
+ );
+
+ if (! are_equal) {
+ testDiag("File contents did not match");
+
+ std::string line;
+ f1.seekg(0, std::ifstream::beg);
+ f2.seekg(0, std::ifstream::beg);
+
+ testDiag("File1 contents: ");
+ while(std::getline(f1, line)) {
+ testDiag("%s", line.c_str());
+ }
+
+ testDiag("File2 contents: ");
+ while(std::getline(f2, line)) {
+ testDiag("%s", line.c_str());
+ }
+ }
+
+ return are_equal;
+}
+
+
+void testHelp(void)
+{
+ testDiag("iocshTest testHelp start");
+
+ // Filename to save help output to
+ const std::string filename = "testHelpOutput";
+
+ // Verify help lists expected commands
+ iocshCmd(("help > " + filename).c_str());
+ std::string contents = readFile(filename);
+ testOk1(contents.find("help") != std::string::npos);
+ testOk1(contents.find("testHelpFunction1") != std::string::npos);
+ testOk1(contents.find("testHelpFunction2") != std::string::npos);
+ testOk1(contents.find("testHelpFunction3") != std::string::npos);
+
+ // Confirm formatting of a single command
+ iocshCmd(("help testHelpFunction1 > " + filename).c_str());
+ testOk1(compareFiles(filename, "iocshTestHelpFunction1") == true);
+
+ // Confirm formatting of multiple commands
+ iocshCmd(("help testHelp* > " + filename).c_str());
+ testOk1(compareFiles(filename, "iocshTestHelpFunctions") == true);
+
+ remove(filename.c_str());
+}
+
} // namespace
+
+
MAIN(iocshTest)
{
- testPlan(19);
+ testPlan(25);
libComRegister();
iocshRegister(&positionFuncDef, &positionCallFunc);
iocshRegister(&assertFuncDef, &assertCallFunc);
+ iocshRegister(&testHelpFunction1Def, &doNothing);
+ iocshRegister(&testHelpFunction2Def, &doNothing);
+ iocshRegister(&testHelpFunction3Def, &doNothing);
findTestData();
testFile("iocshTestSuccess.cmd");
@@ -129,6 +234,8 @@ MAIN(iocshTest)
testPosition("after_error_1", false);
reached.clear();
+ testHelp();
+
// cleanup after macLib to avoid valgrind false positives
dbmfFreeChunks();
return testDone();
diff --git a/modules/libcom/test/iocshTestHelpFunction1 b/modules/libcom/test/iocshTestHelpFunction1
new file mode 100644
index 000000000..515d62f49
--- /dev/null
+++ b/modules/libcom/test/iocshTestHelpFunction1
@@ -0,0 +1,4 @@
+[1m
+testHelpFunction1[0m
+
+Usage message of testHelpFunction1
diff --git a/modules/libcom/test/iocshTestHelpFunctions b/modules/libcom/test/iocshTestHelpFunctions
new file mode 100644
index 000000000..0a6d11159
--- /dev/null
+++ b/modules/libcom/test/iocshTestHelpFunctions
@@ -0,0 +1,14 @@
+[1m
+testHelpFunction1[0m
+
+Usage message of testHelpFunction1
+[4m
+[0m[1m
+testHelpFunction2[0m
+
+Usage message of testHelpFunction2
+[4m
+[0m[1m
+testHelpFunction3[0m
+
+Usage message of testHelpFunction3
From df908f299b67add4339d8f6ffee8adab65b0d099 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sun, 22 Oct 2023 10:48:56 -0700
Subject: [PATCH 15/66] remove unused local
---
modules/database/src/ioc/db/dbEvent.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/modules/database/src/ioc/db/dbEvent.c b/modules/database/src/ioc/db/dbEvent.c
index 38ec9e701..44059dbfa 100644
--- a/modules/database/src/ioc/db/dbEvent.c
+++ b/modules/database/src/ioc/db/dbEvent.c
@@ -1008,7 +1008,6 @@ static void event_task (void *pParm)
do {
void (*pExtraLaborSub) (void *);
void *pExtraLaborArg;
- char wake;
epicsEventMustWait(evUser->ppendsem);
/*
From 49ea46ee5e85a518fa3afd189bee06f43e1a3f24 Mon Sep 17 00:00:00 2001
From: Jack Harper
Date: Tue, 25 Apr 2023 11:15:21 +0100
Subject: [PATCH 16/66] iocsh: add comment to cvtArg explaining default
iocsharg behaviour
---
modules/libcom/src/iocsh/iocsh.cpp | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index 461780513..617c50917 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -755,6 +755,13 @@ void epicsStdCall iocshFree(void)
iocshTableUnlock ();
}
+/*
+ * Parse argument input based on the arg type specified.
+ * It is worth noting that depending on type this argument may
+ * be defaulted if a value is not specified. For example, a
+ * double/int with no value will default to 0 which may allow
+ * you to add optional arguments to the end of your argument list.
+ */
static int
cvtArg (const char *filename, int lineno, char *arg, iocshArgBuf *argBuf,
const iocshArg *piocshArg)
From 92cae86ff2ad7e747967808b65ec4834bf7b7d94 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Sat, 3 Sep 2022 11:36:57 -0700
Subject: [PATCH 17/66] dbRecordsOnceOnly allow append only with "*"
with
> record(ai, "myrec") {}
dbRecordsOnceOnly!=0 currently disallows appending fields with either form:
> record("*", "myrec") {} # error
> record(ai, "myrec") {} # error
Change the meaning such that dbRecordsOnceOnly!=0
allways allows appending when explicitly intended (rtype "*").
> record("*", "myrec") {} # allowed
> record(ai, "myrec") {} # error
Also clearly label this parse error.
---
.../database/src/ioc/dbStatic/dbLexRoutines.c | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/modules/database/src/ioc/dbStatic/dbLexRoutines.c b/modules/database/src/ioc/dbStatic/dbLexRoutines.c
index 85af1f110..4819d4a41 100644
--- a/modules/database/src/ioc/dbStatic/dbLexRoutines.c
+++ b/modules/database/src/ioc/dbStatic/dbLexRoutines.c
@@ -1110,14 +1110,10 @@ static void dbRecordHead(char *recordType, char *name, int visible)
allocTemp(pdbentry);
if (recordType[0] == '*' && recordType[1] == 0) {
- if (dbRecordsOnceOnly)
- epicsPrintf("Record-type \"*\" not valid with dbRecordsOnceOnly\n");
- else {
- status = dbFindRecord(pdbentry, name);
- if (status == 0)
- return; /* done */
- epicsPrintf("Record \"%s\" not found\n", name);
- }
+ status = dbFindRecord(pdbentry, name);
+ if (status == 0)
+ return; /* done */
+ epicsPrintf(ERL_ERROR ": Record \"%s\" not found\n", name);
yyerror(NULL);
duplicate = TRUE;
return;
@@ -1136,15 +1132,16 @@ static void dbRecordHead(char *recordType, char *name, int visible)
status = dbCreateRecord(pdbentry,name);
if (status == S_dbLib_recExists) {
if (strcmp(recordType, dbGetRecordTypeName(pdbentry)) != 0) {
- epicsPrintf("Record \"%s\" of type \"%s\" redefined with new type "
+ epicsPrintf(ERL_ERROR ": Record \"%s\" of type \"%s\" redefined with new type "
"\"%s\"\n", name, dbGetRecordTypeName(pdbentry), recordType);
yyerror(NULL);
duplicate = TRUE;
return;
}
else if (dbRecordsOnceOnly) {
- epicsPrintf("Record \"%s\" already defined (dbRecordsOnceOnly is "
- "set)\n", name);
+ epicsPrintf(ERL_ERROR ": Record \"%s\" already defined and dbRecordsOnceOnly set.\n"
+ "Used record type \"*\" to append.\n",
+ name);
yyerror(NULL);
duplicate = TRUE;
}
From 395015aac4d24fdc81e757b4db7e162a8b52a9fc Mon Sep 17 00:00:00 2001
From: JJL772
Date: Fri, 7 Jul 2023 13:16:21 -0700
Subject: [PATCH 18/66] Com: Make STATIC_ASSERT macro typedefs unique
---
modules/libcom/src/osi/epicsAssert.h | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/modules/libcom/src/osi/epicsAssert.h b/modules/libcom/src/osi/epicsAssert.h
index 7b524f7af..ed00bde1a 100644
--- a/modules/libcom/src/osi/epicsAssert.h
+++ b/modules/libcom/src/osi/epicsAssert.h
@@ -78,14 +78,22 @@ LIBCOM_API void epicsAssert (const char *pFile, const unsigned line,
#if __cplusplus>=201103L
#define STATIC_ASSERT(expr) static_assert(expr, #expr)
#else
-#define STATIC_JOIN(x, y) STATIC_JOIN2(x, y)
-#define STATIC_JOIN2(x, y) x ## y
+
+#define STATIC_JOIN(x, y, z, w) STATIC_JOIN4(x, y, z, w)
+#define STATIC_JOIN4(x, y, z, w) x ## y ## z ## w
+
+/* Compilers that do not support __COUNTER__ (e.g. GCC 4.1) will not be able to use unique static assert typedefs */
+#ifdef __COUNTER__
+# define STATIC_ASSERT_MSG(l) STATIC_JOIN(static_assert_, __COUNTER__, _failed_at_line_, l)
+#else
+# define STATIC_ASSERT_MSG(l) STATIC_JOIN(static_assert_, 0, _failed_at_line_, l)
+#endif
/**\brief Declare a condition that should be true at compile-time.
* \param expr A C/C++ const-expression that should evaluate to True.
*/
#define STATIC_ASSERT(expr) \
- typedef int STATIC_JOIN(static_assert_failed_at_line_, __LINE__) \
+ typedef int STATIC_ASSERT_MSG(__LINE__) \
[ (expr) ? 1 : -1 ] EPICS_UNUSED
#endif
From 2ca70d3aa280610c768ae78668d2f62fc4ef5212 Mon Sep 17 00:00:00 2001
From: Michael Davidsaver
Date: Thu, 5 Oct 2023 14:12:25 -0700
Subject: [PATCH 19/66] iocsh: keep history file
---
.gitignore | 1 +
modules/libcom/src/iocsh/iocsh.cpp | 37 ++++++++++++++++++++++++++++++
src/template/base/top/.gitignore | 3 +++
src/template/ext/top/.gitignore | 3 +++
4 files changed, 44 insertions(+)
diff --git a/.gitignore b/.gitignore
index b7a25673a..46b32dc1a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,4 @@ O.*/
*.log
.*.swp
.DS_Store
+.iocsh_history
diff --git a/modules/libcom/src/iocsh/iocsh.cpp b/modules/libcom/src/iocsh/iocsh.cpp
index 617c50917..b40752a73 100644
--- a/modules/libcom/src/iocsh/iocsh.cpp
+++ b/modules/libcom/src/iocsh/iocsh.cpp
@@ -16,6 +16,7 @@
#include
#include
The following roles are used below:
- - Release Manager ()
+ - Release Manager
- Responsible for managing and tagging the release
- - Platform Developers (informal)
- - Responsible for individual operating system platforms
+ - Core Developers
+ - Responsible for maintaining the EPICS software
- Application Developers
- Responsible for support modules that depend on EPICS Base.
- - APS Website Editor (Andrew Johnson)
- - Responsible for the APS EPICS website
+ - Website Editors
+ - Responsible for the EPICS websites