Skip to content

Commit a796d33

Browse files
authored
Merge b230afe into a63d66a
2 parents a63d66a + b230afe commit a796d33

5 files changed

Lines changed: 186 additions & 4 deletions

File tree

asyn/asynDriver/asynDriver.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ typedef enum {
5252
typedef enum {
5353
asynExceptionConnect,asynExceptionEnable,asynExceptionAutoConnect,
5454
asynExceptionTraceMask,asynExceptionTraceIOMask,asynExceptionTraceInfoMask,
55-
asynExceptionTraceFile,asynExceptionTraceIOTruncateSize
55+
asynExceptionTraceFile,asynExceptionTraceIOTruncateSize,
56+
asynExceptionShutdown
5657
} asynException;
5758

5859
#define ASYN_EXCEPTION_STRINGS \
@@ -93,6 +94,7 @@ typedef struct asynInterface{
9394
/*registerPort attributes*/
9495
#define ASYN_MULTIDEVICE 0x0001
9596
#define ASYN_CANBLOCK 0x0002
97+
#define ASYN_DESTRUCTIBLE 0x0004
9698

9799
/*standard values for asynUser.reason*/
98100
#define ASYN_REASON_SIGNAL -1
@@ -156,6 +158,7 @@ typedef struct asynManager {
156158
asynInterface *pasynInterface,
157159
asynInterface **ppPrev);
158160
asynStatus (*enable)(asynUser *pasynUser,int yesNo);
161+
asynStatus (*shutdown)(asynUser *pasynUser);
159162
asynStatus (*autoConnect)(asynUser *pasynUser,int yesNo);
160163
asynStatus (*isConnected)(asynUser *pasynUser,int *yesNo);
161164
asynStatus (*isEnabled)(asynUser *pasynUser,int *yesNo);

asyn/asynDriver/asynManager.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <epicsTimer.h>
3030
#include <cantProceed.h>
3131
#include <epicsAssert.h>
32+
#include <epicsExit.h>
3233

3334
#include <epicsExport.h>
3435
#include "asynDriver.h"
@@ -136,6 +137,7 @@ typedef struct interfaceNode {
136137

137138
typedef struct dpCommon { /*device/port common fields*/
138139
BOOL enabled;
140+
BOOL defunct;
139141
BOOL connected;
140142
BOOL autoConnect;
141143
BOOL autoConnectActive;
@@ -308,6 +310,7 @@ static asynStatus exceptionDisconnect(asynUser *pasynUser);
308310
static asynStatus interposeInterface(const char *portName, int addr,
309311
asynInterface *pasynInterface,asynInterface **ppPrev);
310312
static asynStatus enable(asynUser *pasynUser,int yesNo);
313+
static asynStatus shutdown(asynUser *pasynUser);
311314
static asynStatus autoConnectAsyn(asynUser *pasynUser,int yesNo);
312315
static asynStatus isConnected(asynUser *pasynUser,int *yesNo);
313316
static asynStatus isEnabled(asynUser *pasynUser,int *yesNo);
@@ -365,6 +368,7 @@ static asynManager manager = {
365368
exceptionDisconnect,
366369
interposeInterface,
367370
enable,
371+
shutdown,
368372
autoConnectAsyn,
369373
isConnected,
370374
isEnabled,
@@ -513,6 +517,7 @@ static void dpCommonInit(port *pport,device *pdevice,BOOL autoConnect)
513517
pdpCommon = &pport->dpc;
514518
}
515519
pdpCommon->enabled = TRUE;
520+
pdpCommon->defunct = FALSE;
516521
pdpCommon->connected = FALSE;
517522
pdpCommon->autoConnect = autoConnect;
518523
ellInit(&pdpCommon->interposeInterfaceList);
@@ -1031,6 +1036,10 @@ static void reportPrintPort(printPortArgs *pprintPortArgs)
10311036
for(i=asynQueuePriorityLow; i<=asynQueuePriorityConnect; i++)
10321037
nQueued += ellCount(&pport->queueList[i]);
10331038
pdpc = &pport->dpc;
1039+
if (pdpc->defunct) {
1040+
fprintf(fp,"%s destroyed\n", pport->portName);
1041+
goto done;
1042+
}
10341043
fprintf(fp,"%s multiDevice:%s canBlock:%s autoConnect:%s\n",
10351044
pport->portName,
10361045
((pport->attributes&ASYN_MULTIDEVICE) ? "Yes" : "No"),
@@ -1112,6 +1121,8 @@ static void reportPrintPort(printPortArgs *pprintPortArgs)
11121121
if(pasynCommon) {
11131122
pasynCommon->report(drvPvt,fp,details);
11141123
}
1124+
1125+
done:
11151126
#ifdef CYGWIN32
11161127
/* This is a (hopefully) temporary fix for a problem with POSIX threads on Cygwin.
11171128
* If a thread is very short-lived, which this report thread will be if the amount of
@@ -1965,6 +1976,20 @@ static asynStatus getPortName(asynUser *pasynUser,const char **pportName)
19651976
return asynSuccess;
19661977
}
19671978

1979+
static void destroyPortDriver(void *portName) {
1980+
asynStatus status;
1981+
asynUser *pasynUser = pasynManager->createAsynUser(0, 0);
1982+
status = pasynManager->connectDevice(pasynUser, portName, 0);
1983+
if (status != asynSuccess) {
1984+
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
1985+
"asynManager:destroyPortDriver cannot connect to port");
1986+
return;
1987+
}
1988+
pasynManager->shutdown(pasynUser);
1989+
pasynManager->disconnect(pasynUser);
1990+
pasynManager->freeAsynUser(pasynUser);
1991+
}
1992+
19681993
static asynStatus registerPort(const char *portName,
19691994
int attributes,int autoConnect,
19701995
unsigned int priority,unsigned int stackSize)
@@ -2016,6 +2041,9 @@ static asynStatus registerPort(const char *portName,
20162041
epicsMutexMustLock(pasynBase->lock);
20172042
ellAdd(&pasynBase->asynPortList,&pport->node);
20182043
epicsMutexUnlock(pasynBase->lock);
2044+
if (attributes & ASYN_DESTRUCTIBLE) {
2045+
epicsAtExit(destroyPortDriver, (void *)portName);
2046+
}
20192047
return asynSuccess;
20202048
}
20212049

@@ -2150,11 +2178,67 @@ static asynStatus enable(asynUser *pasynUser,int yesNo)
21502178
"asynManager:enable not connected");
21512179
return asynError;
21522180
}
2181+
2182+
if (pdpCommon->defunct) {
2183+
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
2184+
"asynManager:enable: port has been shut down");
2185+
return asynDisabled;
2186+
}
2187+
21532188
pdpCommon->enabled = (yesNo ? 1 : 0);
21542189
exceptionOccurred(pasynUser,asynExceptionEnable);
21552190
return asynSuccess;
21562191
}
21572192

2193+
static asynStatus shutdown(asynUser *pasynUser)
2194+
{
2195+
userPvt *puserPvt = asynUserToUserPvt(pasynUser);
2196+
port *pport = puserPvt->pport;
2197+
dpCommon *pdpCommon = findDpCommon(puserPvt);
2198+
interfaceNode *pinterfaceNode;
2199+
2200+
if (!pport || !pdpCommon) {
2201+
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
2202+
"asynManager:shutdown: not connected");
2203+
return asynError;
2204+
}
2205+
2206+
if (pdpCommon->defunct) {
2207+
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
2208+
"asynManager:shutdown: port is already shut down");
2209+
return asynDisabled;
2210+
}
2211+
2212+
if (!(pport->attributes & ASYN_DESTRUCTIBLE)) {
2213+
epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize,
2214+
"asynManager:shutdown: port does not support shutting down");
2215+
return asynError;
2216+
}
2217+
2218+
// Disabling the port short-circuits queueRequest(), preventing usage of
2219+
// external asynUser instances that will shortly become dangling references.
2220+
// It is marked defunct so it cannot be re-enabled.
2221+
pdpCommon->enabled = FALSE;
2222+
pdpCommon->defunct = TRUE;
2223+
2224+
// Nullifying the private pointers to the driver enables trapping on
2225+
// erroneous accesses, making finding bugs easier.
2226+
pport->pasynLockPortNotify = NULL;
2227+
pport->lockPortNotifyPvt = NULL;
2228+
pinterfaceNode = (interfaceNode *)ellFirst(&pport->interfaceList);
2229+
while(pinterfaceNode) {
2230+
asynInterface *pif = pinterfaceNode->pasynInterface;
2231+
pif->drvPvt = NULL;
2232+
pinterfaceNode = (interfaceNode *)ellNext(&pinterfaceNode->node);
2233+
}
2234+
2235+
// Actual destruction of the driver is delegated to the driver itself, which
2236+
// shall implement an exception handler.
2237+
exceptionOccurred(pasynUser, asynExceptionShutdown);
2238+
2239+
return asynSuccess;
2240+
}
2241+
21582242
static asynStatus autoConnectAsyn(asynUser *pasynUser,int yesNo)
21592243
{
21602244
userPvt *puserPvt = asynUserToUserPvt(pasynUser);

asyn/asynPortDriver/asynPortDriver.cpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3754,7 +3754,7 @@ static asynDrvUser ifaceDrvUser = {
37543754
The bit mask values are defined in asynPortDriver.h, e.g. asynInt32Mask.
37553755
* \param[in] interruptMask Bit mask definining the asyn interfaces that can generate interrupts (callbacks).
37563756
The bit mask values are defined in asynPortDriver.h, e.g. asynInt8ArrayMask.
3757-
* \param[in] asynFlags Flags when creating the asyn port driver; includes ASYN_CANBLOCK and ASYN_MULTIDEVICE.
3757+
* \param[in] asynFlags Flags when creating the asyn port driver; includes ASYN_CANBLOCK, ASYN_MULTIDEVICE and ASYN_DESTRUCTIBLE.
37583758
* \param[in] autoConnect The autoConnect flag for the asyn port driver.
37593759
1 if the driver should autoconnect.
37603760
* \param[in] priority The thread priority for the asyn port driver thread if ASYN_CANBLOCK is set in asynFlags.
@@ -3785,6 +3785,25 @@ asynPortDriver::asynPortDriver(const char *portNameIn, int maxAddrIn, int paramT
37853785
autoConnect, priority, stackSize);
37863786
}
37873787

3788+
void asynPortDriver::exceptionHandler(asynUser *pasynUser, asynException exception) {
3789+
asynPortDriver *pPvt = (asynPortDriver *)pasynUser->userPvt;
3790+
if (exception == asynExceptionShutdown) {
3791+
asynPrint(pPvt->pasynUserSelf, ASYN_TRACE_FLOW,
3792+
"%s: port=%s Port is shutting down.\n",
3793+
driverName, pPvt->portName);
3794+
asynStatus status = pPvt->shutdown();
3795+
if (status != asynSuccess) {
3796+
asynPrint(pPvt->pasynUserSelf, ASYN_TRACE_ERROR,
3797+
"%s: port=%s Driver shutdown encountered an error.\n",
3798+
driverName, pPvt->portName);
3799+
}
3800+
asynPrint(pPvt->pasynUserSelf, ASYN_TRACE_FLOW,
3801+
"%s: port=%s Shutdown complete, destroying the driver.\n",
3802+
driverName, pPvt->portName);
3803+
delete pPvt;
3804+
}
3805+
}
3806+
37883807
/** The following function is required to initialize from two constructors. Once
37893808
* we can rely on C++11 this code can be moved back into the primary
37903809
* constructor. The secondary constructor can then be converted into
@@ -3798,6 +3817,8 @@ void asynPortDriver::initialize(const char *portNameIn, int maxAddrIn, int inter
37983817
asynStandardInterfaces *pInterfaces;
37993818
int addr;
38003819

3820+
shutdownNeeded = asynFlags & ASYN_DESTRUCTIBLE;
3821+
38013822
/* Initialize some members to 0 */
38023823
pInterfaces = &this->asynStdInterfaces;
38033824
memset(pInterfaces, 0, sizeof(asynStdInterfaces));
@@ -3843,6 +3864,7 @@ void asynPortDriver::initialize(const char *portNameIn, int maxAddrIn, int inter
38433864

38443865
/* Create asynUser for debugging and for standardInterfacesBase */
38453866
this->pasynUserSelf = pasynManager->createAsynUser(0, 0);
3867+
this->pasynUserSelf->userPvt = this;
38463868

38473869
/* The following asynPrint will be governed by the global trace mask since asynUser is not yet connected to port */
38483870
asynPrint(this->pasynUserSelf, ASYN_TRACE_FLOW,
@@ -3904,6 +3926,10 @@ void asynPortDriver::initialize(const char *portNameIn, int maxAddrIn, int inter
39043926
throw std::runtime_error(msg);
39053927
}
39063928

3929+
if (shutdownNeeded) {
3930+
pasynManager->exceptionCallbackAdd(pasynUserSelf, asynPortDriver::exceptionHandler);
3931+
}
3932+
39073933
/* Create a thread that waits for interruptAccept and then does all the callbacks once. */
39083934
cbThread = new callbackThread(this);
39093935
}
@@ -3921,10 +3947,30 @@ asynStatus asynPortDriver::createParams()
39213947
return asynSuccess;
39223948
}
39233949

3950+
asynStatus asynPortDriver::shutdown() {
3951+
shutdownNeeded = false;
3952+
delete cbThread;
3953+
cbThread = NULL;
3954+
return asynSuccess;
3955+
}
3956+
39243957
/** Destructor for asynPortDriver class; frees resources allocated when port driver is created. */
39253958
asynPortDriver::~asynPortDriver()
39263959
{
3927-
delete cbThread;
3960+
if (shutdownNeeded) {
3961+
// If shutdownNeeded is still true, it means that the base
3962+
// implementation of asynPortDriver::shutdown() was not called. Which
3963+
// means that one of the derived classes does not call the base
3964+
// function, and needs fixing.
3965+
asynPrint(this->pasynUserSelf, ASYN_TRACE_ERROR,
3966+
"%s: port=%s Driver error: port supports shutdown, but it was "
3967+
"incomplete. A derived class of asynPortDriver has a bug.\n",
3968+
driverName, portName);
3969+
}
3970+
3971+
if (cbThread)
3972+
delete cbThread;
3973+
39283974
epicsMutexDestroy(this->mutexId);
39293975

39303976
for (int addr=0; addr<this->maxAddr; addr++) {

asyn/asynPortDriver/asynPortDriver.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,13 +204,17 @@ class ASYN_API asynPortDriver {
204204
void callbackTask();
205205

206206
protected:
207-
asynParamSet* paramSet;
207+
virtual asynStatus shutdown();
208208
void initialize(const char *portNameIn, int maxAddrIn, int interfaceMask, int interruptMask, int asynFlags,
209209
int autoConnect, int priority, int stackSize);
210+
211+
asynParamSet* paramSet;
210212
asynUser *pasynUserSelf; /**< asynUser connected to ourselves for asynTrace */
211213
asynStandardInterfaces asynStdInterfaces; /**< The asyn interfaces this driver implements */
212214

213215
private:
216+
static void exceptionHandler(asynUser *pasynUser, asynException exception);
217+
214218
std::vector<paramList*> params;
215219
paramList *getParamList(int list);
216220
epicsMutexId mutexId;
@@ -219,6 +223,7 @@ class ASYN_API asynPortDriver {
219223
char *outputEosOctet;
220224
int outputEosLenOctet;
221225
callbackThread *cbThread;
226+
bool shutdownNeeded;
222227
template <typename epicsType, typename interruptType>
223228
asynStatus doCallbacksArray(epicsType *value, size_t nElements,
224229
int reason, int address, void *interruptPvt);

asyn/miscellaneous/asynShellCommands.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1315,6 +1315,49 @@ static void asynSetQueueLockPortTimeoutCall(const iocshArgBuf * args) {
13151315
asynSetQueueLockPortTimeout(portName,timeout);
13161316
}
13171317

1318+
static const iocshArg asynShutdownPortArg0 = {"portName", iocshArgString};
1319+
static const iocshArg *const asynShutdownPortArgs[] = {&asynShutdownPortArg0};
1320+
static const iocshFuncDef asynShutdownPortDef =
1321+
{"asynShutdownPort", 1, asynShutdownPortArgs
1322+
#ifdef IOCSHFUNCDEF_HAS_USAGE
1323+
,
1324+
#else
1325+
};
1326+
static const char asynShutdownPortUsage[] =
1327+
#endif
1328+
"Permanently disables the port and destroys the port driver,\n"
1329+
"releasing its resources.\n"
1330+
#ifdef IOCSHFUNCDEF_HAS_USAGE
1331+
};
1332+
#define asynShutdownPortUsage asynShutdownPortDef.usage
1333+
#else
1334+
;
1335+
#endif
1336+
ASYN_API int
1337+
asynShutdownPort(const char *portName)
1338+
{
1339+
asynUser *pasynUser;
1340+
asynStatus status;
1341+
1342+
pasynUser = pasynManager->createAsynUser(0, 0);
1343+
status = pasynManager->connectDevice(pasynUser, portName, 0);
1344+
if(status != asynSuccess) {
1345+
printf("%s\n", pasynUser->errorMessage);
1346+
pasynManager->freeAsynUser(pasynUser);
1347+
return -1;
1348+
}
1349+
status = pasynManager->shutdown(pasynUser);
1350+
if(status != asynSuccess) {
1351+
printf("%s\n", pasynUser->errorMessage);
1352+
}
1353+
pasynManager->freeAsynUser(pasynUser);
1354+
return 0;
1355+
}
1356+
static void asynShutdownPortCall(const iocshArgBuf * args) {
1357+
const char *portName = args[0].sval;
1358+
asynShutdownPort(portName);
1359+
}
1360+
13181361
static void asynRegister(void)
13191362
{
13201363
static int firstTime = 1;
@@ -1346,5 +1389,6 @@ static void asynRegister(void)
13461389
iocshRegister(&asynRegisterTimeStampSourceDef, asynRegisterTimeStampSourceCall);
13471390
iocshRegister(&asynUnregisterTimeStampSourceDef, asynUnregisterTimeStampSourceCall);
13481391
iocshRegister(&asynSetMinTimerPeriodDef, asynSetMinTimerPeriodCall);
1392+
iocshRegister(&asynShutdownPortDef, asynShutdownPortCall);
13491393
}
13501394
epicsExportRegistrar(asynRegister);

0 commit comments

Comments
 (0)