From 4bd4b936499cf4f69796dfab10ae41fa5dbcc1a8 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Fri, 13 Mar 2015 15:15:03 -0400 Subject: [PATCH] dbCa: update comments on locking --- src/ioc/db/dbCa.c | 58 +++++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/src/ioc/db/dbCa.c b/src/ioc/db/dbCa.c index 644ae12b9..984b59f13 100644 --- a/src/ioc/db/dbCa.c +++ b/src/ioc/db/dbCa.c @@ -79,49 +79,37 @@ static int dbca_chan_count; /* caLink locking * - * workListLock - * This is only used to put request into and take them out of workList. - * While this is locked no other locks are taken + * Lock ordering: + * dbScanLock -> caLink.lock -> workListLock * - * dbScanLock - * dbCaAddLink and dbCaRemoveLink are only called by dbAccess or iocInit - * They are only called by dbAccess when it has a global lock on lock set. - * It is assumed that ALL other dbCaxxx calls are made only if dbScanLock - * is already active. These routines are intended for use by record/device - * support. + * workListLock: + * Guards access to workList. * - * caLink.lock - * Any code that use a caLink takes this lock and releases it when done + * dbScanLock: + * All dbCa* functions operating on a single link may only be called when + * the record containing the DBLINK is locked. Including: + * dbCaGet*() + * dbCaIsLinkConnected() + * dbCaPutLink() + * dbCaScanFwdLink() + * dbCaAddLinkCallback() + * dbCaRemoveLink() * - * dbCaTask and the channel access callbacks NEVER access anything in the - * records except after locking caLink.lock and checking that caLink.plink - * is not null. They NEVER call dbScanLock. + * Guard the pointer plink.value.pv_link.pvt, but not the struct caLink + * which is pointed to. * - * The above is necessary to prevent deadlocks and attempts to use a caLink - * that has been deleted. + * caLink.lock: + * Guards the caLink structure (but not the struct DBLINK) * - * Just a few words about handling dbCaRemoveLink because this is when - * it is essential that nothing trys to use a caLink that has been freed. + * The dbCaTask only locks caLink, and must not lock the record (a violation of lock order). * - * dbCaRemoveLink is called when links are being modified. This is only - * done with the dbScan mechanism guranteeing that nothing from - * database access trys to access the record containing the caLink. + * During link modification or IOC shutdown the pca->plink pointer (guarded by caLink.lock) + * is used as a flag to indicate that a link is no longer active. * - * Thus the problem is to make sure that nothing from channel access - * accesses a caLink that is deleted. This is done as follows. + * References to the struct caLink are owned by the dbCaTask, and any scanOnceCallback() + * which is in progress. * - * dbCaRemoveLink does the following: - * epicsMutexMustLock(pca->lock); - * pca->plink = 0; - * plink->value.pv_link.pvt = 0; - * epicsMutexUnlock(pca->lock); - * addAction(pca,CA_CLEAR_CHANNEL); - * - * dbCaTask issues a ca_clear_channel and then frees the caLink. - * - * If any channel access callback gets called before the ca_clear_channel - * it finds pca->plink=0 and does nothing. Once ca_clear_channel - * is called no other callback for this caLink will be called. + * The libca and scanOnceCallback callbacks take no action if pca->plink==NULL. * * dbCaPutLinkCallback causes an additional complication because * when dbCaRemoveLink is called the callback may not have occured.