diff --git a/modules/database/src/ioc/db/dbCommonPvt.h b/modules/database/src/ioc/db/dbCommonPvt.h index e91de6f65..eda8b18a7 100644 --- a/modules/database/src/ioc/db/dbCommonPvt.h +++ b/modules/database/src/ioc/db/dbCommonPvt.h @@ -5,11 +5,16 @@ #include #include "dbCommon.h" +struct epicsThreadOSD; + /** Base internal additional information for every record */ typedef struct dbCommonPvt { struct dbRecordNode *recnode; + /* Thread which is currently processing this record */ + struct epicsThreadOSD* procThread; + struct dbCommon common; } dbCommonPvt; diff --git a/modules/database/src/ioc/db/dbDbLink.c b/modules/database/src/ioc/db/dbDbLink.c index c4e2b1987..d53e1568c 100644 --- a/modules/database/src/ioc/db/dbDbLink.c +++ b/modules/database/src/ioc/db/dbDbLink.c @@ -56,7 +56,7 @@ #include "dbAddr.h" #include "dbBase.h" #include "dbBkpt.h" -#include "dbCommon.h" +#include "dbCommonPvt.h" #include "dbConvertFast.h" #include "dbConvert.h" #include "db_field_log.h" @@ -386,8 +386,11 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst) { char context[40] = ""; int trace = dbAccessDebugPUTF && *dbLockSetAddrTrace(psrc); + int srcset = dbRec2Pvt(psrc)->procThread==NULL; + int dstset = dbRec2Pvt(pdst)->procThread==NULL; long status; epicsUInt8 pact = psrc->pact; + epicsThreadId self = epicsThreadGetIdSelf(); psrc->pact = TRUE; @@ -408,8 +411,9 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst) pdst->putf = psrc->putf; } - else if (psrc->putf) { - /* The dst record is busy (awaiting async reprocessing) and + else if (psrc->putf && dbRec2Pvt(pdst)->procThread!=self) { + /* The dst record is busy (awaiting async reprocessing), + * not being processed recursively by us, and * we were originally triggered by a call to dbPutField(), * so we mark the dst record for reprocessing once the async * completion is over. @@ -422,17 +426,37 @@ static long processTarget(dbCommon *psrc, dbCommon *pdst) pdst->rpro = TRUE; } else { - /* The dst record is busy, but we weren't triggered by a call - * to dbPutField(). Do nothing. + /* The dst record is busy, but either is being processed recursively, + * or wasn't triggered by a call to dbPutField(). Do nothing. */ if (trace) printf("%s: '%s' -> Active '%s', done\n", context, psrc->name, pdst->name); } + if(srcset) { + dbRec2Pvt(psrc)->procThread = self; + } else { + assert(dbRec2Pvt(psrc)->procThread==self); + } + if(dstset) { + dbRec2Pvt(pdst)->procThread = self; + } else { + assert(dbRec2Pvt(psrc)->procThread==self); + } + status = dbProcess(pdst); psrc->pact = pact; + assert(dbRec2Pvt(psrc)->procThread==self); + assert(dbRec2Pvt(pdst)->procThread==self); + if(srcset) { + dbRec2Pvt(psrc)->procThread = NULL; + } + if(dstset) { + dbRec2Pvt(pdst)->procThread = NULL; + } + return status; } diff --git a/modules/database/test/std/rec/asyncproctest.c b/modules/database/test/std/rec/asyncproctest.c index ac788c755..b10bcac68 100644 --- a/modules/database/test/std/rec/asyncproctest.c +++ b/modules/database/test/std/rec/asyncproctest.c @@ -120,11 +120,9 @@ MAIN(asyncproctest) if (epicsEventWaitWithTimeout(done, 10.0) != epicsEventOK) testAbort("Processing timed out"); - testTodoBegin("Bug"); testdbGetFieldEqual("chain4_pos", DBF_SHORT, 1); testdbGetFieldEqual("chain4_rel", DBF_SHORT, 1); testdbGetFieldEqual("chain4_lim", DBF_SHORT, 1); - testTodoEnd(); } testIocShutdownOk();