propagate PUTF through DB_LINK and use to RPRO async

For async records to be usable in user triggered
(eg. via RSRV) scan chains, queuing must be handled
properly in the event that a second dbPutField()
is made before the scan chain has completed
(eg. a double click on an OPI).

We change the meaning of PUTF so that it is
propagated through DB links to indicate the
pass through the scan chain directly triggered
by a dbPutField().

propagation is broken if a busy async record
is found, and that record is instead scheduled
to re-process on completion.
This commit is contained in:
Michael Davidsaver
2018-01-22 21:14:31 -08:00
parent 506be838af
commit a4fcd2296a
3 changed files with 101 additions and 20 deletions

View File

@@ -23,6 +23,8 @@
#include <stdio.h>
#include <string.h>
#define EPICS_PRIVATE_API
#include "alarm.h"
#include "cantProceed.h"
#include "cvtFast.h"
@@ -49,6 +51,7 @@
#include "dbFldTypes.h"
#include "dbFldTypes.h"
#include "dbLink.h"
#include "dbDbLink.h" /* for dbDbLinkPUTF() */
#include "dbLockPvt.h"
#include "dbNotify.h"
#include "dbScan.h"
@@ -449,13 +452,26 @@ int dbGetFieldIndex(const struct dbAddr *paddr)
*/
long dbScanPassive(dbCommon *pfrom, dbCommon *pto)
{
long status;
epicsUInt8 pact_save;
/* if not passive just return success */
if (pto->scan != 0)
return 0;
pact_save = pfrom->pact;
pfrom->pact = 1;
dbDbLinkPUTF(pfrom, pto);
if (pfrom && pfrom->ppn)
dbNotifyAdd(pfrom,pto);
return dbProcess(pto);
status = dbProcess(pto);
pfrom->pact = pact_save;
return status;
}
/*
@@ -523,7 +539,7 @@ long dbProcess(dbCommon *precord)
unsigned short monitor_mask;
if (*ptrace)
printf("%s: Active %s\n", context, precord->name);
printf("%s: Active%s %s\n", context, precord->rpro ? " Q" : "", precord->name);
/* raise scan alarm after MAX_LOCK times */
if ((precord->stat == SCAN_ALARM) ||
@@ -1210,7 +1226,7 @@ long dbPutField(DBADDR *paddr, short dbrType,
dbrType < DBR_PUT_ACKT)) {
if (precord->pact) {
if (precord->tpro)
printf("%s: Active %s\n",
printf("%s: Active Q %s\n",
epicsThreadGetNameSelf(), precord->name);
precord->rpro = TRUE;
} else {

View File

@@ -18,6 +18,8 @@
#include <stdio.h>
#include <string.h>
#define EPICS_PRIVATE_API
#include "alarm.h"
#include "cantProceed.h"
#include "cvtFast.h"
@@ -43,17 +45,81 @@
#include "dbNotify.h"
#include "dbScan.h"
#include "dbStaticLib.h"
#include "dbServer.h"
#include "devSup.h"
#include "link.h"
#include "recGbl.h"
#include "recSup.h"
#include "special.h"
#include "dbDbLink.h"
/***************************** Database Links *****************************/
/* Forward definition */
static lset dbDb_lset;
/* call before each call of dbProcess() (also dbScanPassive())
*
* PUTF - This flag is set in dbPutField() prior to calling dbProcess().
* It is normally cleared at the end of processing in recGblFwdLink().
* It may also be cleared in dbProcess() if DISA==DISV (scan disabled),
* or by this function.
* If PUTF==1 before a call to dbProcess(prec), then afterwards
* PACT | !PUTF must be true.
*
* RPRO - Set by dbPutField() or this function when the record to be processed
* is found to be busy (PACT==1).
* Cleared in recGblFwdLink() when a record is scheduled for re-processing,
* or DISA==DISV.
*/
static
void propPUTF(struct link *plink)
{
struct pv_link *ppv_link = &plink->value.pv_link;
DBADDR *paddr = ppv_link->pvt;
dbCommon *psrc = plink->precord,
*pdst = paddr->precord;
dbDbLinkPUTF(psrc, pdst);
}
void dbDbLinkPUTF(dbCommon *psrc, dbCommon *pdst)
{
char context[40] = "";
int trace = *dbLockSetAddrTrace(psrc);
if (trace && dbServerClient(context, sizeof(context))) {
/* No client, use thread name */
strncpy(context, epicsThreadGetNameSelf(), sizeof(context));
context[sizeof(context) - 1] = 0;
}
if(!pdst->pact) {
/* normal propagation of PUTF from src to target */
if(trace)
printf("%s: %s -> %s prop PUTF=%u\n", context, psrc->name, pdst->name, psrc->putf);
assert(!pdst->putf);
pdst->putf = psrc->putf;
} else if(psrc->putf) {
/* found a busy async record,
* we were originally triggered by a dbPutField()
* so queue for reprocessing on completion,
* but only this one time.
*/
if(trace)
printf("%s: %s -> %s prop RPRO=1\n", context, psrc->name, pdst->name);
pdst->putf = FALSE;
pdst->rpro = TRUE;
} else {
/* busy async record, but not originally triggered
* by dbPutField(). Do nothing.
*/
if(trace)
printf("%s: %s -> %s prop DROP\n", context, psrc->name, pdst->name);
}
}
long dbDbInitLink(struct link *plink, short dbfType)
{
DBADDR dbaddr;
@@ -138,11 +204,7 @@ static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
/* scan passive records if link is process passive */
if (ppv_link->pvlMask & pvlOptPP) {
unsigned char pact = precord->pact;
precord->pact = TRUE;
status = dbScanPassive(precord, paddr->precord);
precord->pact = pact;
if (status)
return status;
}
@@ -312,20 +374,18 @@ static long dbDbPutValue(struct link *plink, short dbrType,
if (paddr->pfield == (void *) &pdest->proc ||
(ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
/* if dbPutField caused asyn record to process */
/* ask for reprocessing*/
if (pdest->putf) {
pdest->rpro = TRUE;
} else { /* process dest record with source's PACT true */
unsigned char pact;
epicsUInt8 pact_save = psrce->pact;
if (psrce && psrce->ppn)
dbNotifyAdd(psrce, pdest);
pact = psrce->pact;
psrce->pact = TRUE;
status = dbProcess(pdest);
psrce->pact = pact;
}
psrce->pact = 1;
if (psrce && psrce->ppn)
dbNotifyAdd(psrce, pdest);
propPUTF(plink);
status = dbProcess(pdest);
psrce->pact = pact_save;
}
return status;
}

View File

@@ -23,11 +23,16 @@ extern "C" {
struct link;
struct dbLocker;
struct dbCommon;
epicsShareFunc long dbDbInitLink(struct link *plink, short dbfType);
epicsShareFunc void dbDbAddLink(struct dbLocker *locker, struct link *plink,
short dbfType, DBADDR *ptarget);
#ifdef EPICS_PRIVATE_API
void dbDbLinkPUTF(dbCommon *psrc, dbCommon *pdst);
#endif
#ifdef __cplusplus
}
#endif