diff --git a/ADApp/ADSrc/NDArray.cpp b/ADApp/ADSrc/NDArray.cpp index 257d5de7e..232f1cec1 100644 --- a/ADApp/ADSrc/NDArray.cpp +++ b/ADApp/ADSrc/NDArray.cpp @@ -235,6 +235,11 @@ int NDArray::release() printf("%s: WARNING, no owner\n", functionName); return(ND_ERROR); } + /* No-op if pool was already destroyed (e.g. IOC exit: PVA tears down after driver). */ + if (NDArrayPool::isPoolDestroyed(pNDArrayPool)) { + pNDArrayPool = NULL; + return(ND_ERROR); + } return(pNDArrayPool->release(this)); } diff --git a/ADApp/ADSrc/NDArray.h b/ADApp/ADSrc/NDArray.h index 43868b740..3e5aa88f4 100644 --- a/ADApp/ADSrc/NDArray.h +++ b/ADApp/ADSrc/NDArray.h @@ -187,6 +187,13 @@ class ADCORE_API NDArrayPool { size_t getMemorySize(); int getNumFree(); void emptyFreeList(); + + /** Register a pool as being destroyed so late NDArray::release() calls no-op (avoids SIGSEGV + * when pvAccess tears down MonitorElements after the driver and pool are deleted). */ + static void registerDestroyingPool(NDArrayPool *p); + /** Check if the given pool address was registered as destroyed (pointer not dereferenced). */ + static bool isPoolDestroyed(NDArrayPool *p); + static void setDefaultFrameMemoryFunctions(MallocFunc_t newMalloc, FreeFunc_t newFree); virtual void* frameMalloc(size_t size); diff --git a/ADApp/ADSrc/NDArrayPool.cpp b/ADApp/ADSrc/NDArrayPool.cpp index bea77dad8..0e3f00300 100644 --- a/ADApp/ADSrc/NDArrayPool.cpp +++ b/ADApp/ADSrc/NDArrayPool.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -46,6 +47,33 @@ FreeFunc_t defaultFrameFree = free; volatile int eraseNDAttributes=0; extern "C" {epicsExportAddress(int, eraseNDAttributes);} +/* Registry of pool addresses being destroyed so NDArray::release() can no-op (avoids SIGSEGV + * when pvAccess tears down after the driver). Only the address is used; the pointer is never + * dereferenced in isPoolDestroyed(). */ +static std::set *destroyedPools = NULL; +static epicsMutexId destroyedPoolsMutex = NULL; + +void NDArrayPool::registerDestroyingPool(NDArrayPool *p) +{ + if (!p) return; + if (!destroyedPoolsMutex) + destroyedPoolsMutex = epicsMutexCreate(); + if (!destroyedPools) + destroyedPools = new std::set(); + epicsMutexLock(destroyedPoolsMutex); + destroyedPools->insert(p); + epicsMutexUnlock(destroyedPoolsMutex); +} + +bool NDArrayPool::isPoolDestroyed(NDArrayPool *p) +{ + if (!p || !destroyedPoolsMutex || !destroyedPools) return false; + epicsMutexLock(destroyedPoolsMutex); + bool found = (destroyedPools->find(p) != destroyedPools->end()); + epicsMutexUnlock(destroyedPoolsMutex); + return found; +} + /** NDArrayPool constructor * \param[in] pDriver Pointer to the asynNDArrayDriver that created this object. * \param[in] maxMemory Maxiumum number of bytes of memory the the pool is allowed to use, summed over diff --git a/ADApp/ADSrc/asynNDArrayDriver.cpp b/ADApp/ADSrc/asynNDArrayDriver.cpp index ce1fdb8c4..f4e9d459c 100644 --- a/ADApp/ADSrc/asynNDArrayDriver.cpp +++ b/ADApp/ADSrc/asynNDArrayDriver.cpp @@ -866,8 +866,8 @@ asynNDArrayDriver::asynNDArrayDriver(const char *portName, int maxAddr, int maxB interfaceMask | asynInt32Mask | asynFloat64Mask | asynOctetMask | asynInt32ArrayMask | asynGenericPointerMask | asynDrvUserMask, interruptMask | asynInt32Mask | asynFloat64Mask | asynOctetMask | asynInt32ArrayMask | asynGenericPointerMask, asynFlags, autoConnect, priority, stackSize), - pNDArrayPool(NULL), queuedArrayCountMutex_(NULL), queuedArrayCount_(0), - queuedArrayUpdateRun_(true) + pNDArrayPool(NULL), maxAddr_(maxAddr), queuedArrayCountMutex_(NULL), + queuedArrayCount_(0), queuedArrayUpdateRun_(true) { char versionString[20]; static const char *functionName = "asynNDArrayDriver"; @@ -1029,6 +1029,17 @@ asynNDArrayDriver::~asynNDArrayDriver() epicsEventSignal(queuedArrayEvent_); epicsEventWait(queuedArrayUpdateDone_); + /* Register pool as destroying so any late NDArray::release() (e.g. from pvAccess + * MonitorElement teardown) no-ops instead of touching the pool we are about to delete. */ + NDArrayPool::registerDestroyingPool(this->pNDArrayPoolPvt_); + + /* Null pNDArrayPool on driver-owned arrays as well (belt and braces). */ + if (this->pArrays) { + for (int i = 0; i < this->maxAddr_; i++) { + if (this->pArrays[i]) + this->pArrays[i]->pNDArrayPool = NULL; + } + } delete this->pNDArrayPoolPvt_; free(this->pArrays); delete this->pAttributeList; diff --git a/ADApp/ADSrc/asynNDArrayDriver.h b/ADApp/ADSrc/asynNDArrayDriver.h index 5d12ad971..2e88ec601 100644 --- a/ADApp/ADSrc/asynNDArrayDriver.h +++ b/ADApp/ADSrc/asynNDArrayDriver.h @@ -241,6 +241,7 @@ class ADCORE_API asynNDArrayDriver : public asynPortDriver { private: asynStatus preAllocateBuffers(); + int maxAddr_; NDArrayPool *pNDArrayPoolPvt_; epicsMutex *queuedArrayCountMutex_; epicsEventId queuedArrayEvent_;