|
29 | 29 | #include <epicsTimer.h> |
30 | 30 | #include <cantProceed.h> |
31 | 31 | #include <epicsAssert.h> |
| 32 | +#include <epicsExit.h> |
32 | 33 |
|
33 | 34 | #include <epicsExport.h> |
34 | 35 | #include "asynDriver.h" |
@@ -136,6 +137,7 @@ typedef struct interfaceNode { |
136 | 137 |
|
137 | 138 | typedef struct dpCommon { /*device/port common fields*/ |
138 | 139 | BOOL enabled; |
| 140 | + BOOL defunct; |
139 | 141 | BOOL connected; |
140 | 142 | BOOL autoConnect; |
141 | 143 | BOOL autoConnectActive; |
@@ -308,6 +310,7 @@ static asynStatus exceptionDisconnect(asynUser *pasynUser); |
308 | 310 | static asynStatus interposeInterface(const char *portName, int addr, |
309 | 311 | asynInterface *pasynInterface,asynInterface **ppPrev); |
310 | 312 | static asynStatus enable(asynUser *pasynUser,int yesNo); |
| 313 | +static asynStatus shutdown(asynUser *pasynUser); |
311 | 314 | static asynStatus autoConnectAsyn(asynUser *pasynUser,int yesNo); |
312 | 315 | static asynStatus isConnected(asynUser *pasynUser,int *yesNo); |
313 | 316 | static asynStatus isEnabled(asynUser *pasynUser,int *yesNo); |
@@ -365,6 +368,7 @@ static asynManager manager = { |
365 | 368 | exceptionDisconnect, |
366 | 369 | interposeInterface, |
367 | 370 | enable, |
| 371 | + shutdown, |
368 | 372 | autoConnectAsyn, |
369 | 373 | isConnected, |
370 | 374 | isEnabled, |
@@ -513,6 +517,7 @@ static void dpCommonInit(port *pport,device *pdevice,BOOL autoConnect) |
513 | 517 | pdpCommon = &pport->dpc; |
514 | 518 | } |
515 | 519 | pdpCommon->enabled = TRUE; |
| 520 | + pdpCommon->defunct = FALSE; |
516 | 521 | pdpCommon->connected = FALSE; |
517 | 522 | pdpCommon->autoConnect = autoConnect; |
518 | 523 | ellInit(&pdpCommon->interposeInterfaceList); |
@@ -1031,6 +1036,10 @@ static void reportPrintPort(printPortArgs *pprintPortArgs) |
1031 | 1036 | for(i=asynQueuePriorityLow; i<=asynQueuePriorityConnect; i++) |
1032 | 1037 | nQueued += ellCount(&pport->queueList[i]); |
1033 | 1038 | pdpc = &pport->dpc; |
| 1039 | + if (pdpc->defunct) { |
| 1040 | + fprintf(fp,"%s destroyed\n", pport->portName); |
| 1041 | + goto done; |
| 1042 | + } |
1034 | 1043 | fprintf(fp,"%s multiDevice:%s canBlock:%s autoConnect:%s\n", |
1035 | 1044 | pport->portName, |
1036 | 1045 | ((pport->attributes&ASYN_MULTIDEVICE) ? "Yes" : "No"), |
@@ -1112,6 +1121,8 @@ static void reportPrintPort(printPortArgs *pprintPortArgs) |
1112 | 1121 | if(pasynCommon) { |
1113 | 1122 | pasynCommon->report(drvPvt,fp,details); |
1114 | 1123 | } |
| 1124 | + |
| 1125 | +done: |
1115 | 1126 | #ifdef CYGWIN32 |
1116 | 1127 | /* This is a (hopefully) temporary fix for a problem with POSIX threads on Cygwin. |
1117 | 1128 | * 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) |
1965 | 1976 | return asynSuccess; |
1966 | 1977 | } |
1967 | 1978 |
|
| 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 | + |
1968 | 1993 | static asynStatus registerPort(const char *portName, |
1969 | 1994 | int attributes,int autoConnect, |
1970 | 1995 | unsigned int priority,unsigned int stackSize) |
@@ -2016,6 +2041,9 @@ static asynStatus registerPort(const char *portName, |
2016 | 2041 | epicsMutexMustLock(pasynBase->lock); |
2017 | 2042 | ellAdd(&pasynBase->asynPortList,&pport->node); |
2018 | 2043 | epicsMutexUnlock(pasynBase->lock); |
| 2044 | + if (attributes & ASYN_DESTRUCTIBLE) { |
| 2045 | + epicsAtExit(destroyPortDriver, (void *)portName); |
| 2046 | + } |
2019 | 2047 | return asynSuccess; |
2020 | 2048 | } |
2021 | 2049 |
|
@@ -2150,11 +2178,67 @@ static asynStatus enable(asynUser *pasynUser,int yesNo) |
2150 | 2178 | "asynManager:enable not connected"); |
2151 | 2179 | return asynError; |
2152 | 2180 | } |
| 2181 | + |
| 2182 | + if (pdpCommon->defunct) { |
| 2183 | + epicsSnprintf(pasynUser->errorMessage,pasynUser->errorMessageSize, |
| 2184 | + "asynManager:enable: port has been shut down"); |
| 2185 | + return asynDisabled; |
| 2186 | + } |
| 2187 | + |
2153 | 2188 | pdpCommon->enabled = (yesNo ? 1 : 0); |
2154 | 2189 | exceptionOccurred(pasynUser,asynExceptionEnable); |
2155 | 2190 | return asynSuccess; |
2156 | 2191 | } |
2157 | 2192 |
|
| 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 | + |
2158 | 2242 | static asynStatus autoConnectAsyn(asynUser *pasynUser,int yesNo) |
2159 | 2243 | { |
2160 | 2244 | userPvt *puserPvt = asynUserToUserPvt(pasynUser); |
|
0 commit comments