Merge lp:~epics-core/epics-base/dbscan-update into lp:~epics-core/epics-base/3.16

Proposed by mdavidsaver
Status: Merged
Merged at revision: 12628
Proposed branch: lp:~epics-core/epics-base/dbscan-update
Merge into: lp:~epics-core/epics-base/3.16
Diff against target: 488 lines (+231/-73)
7 files modified
src/ioc/db/dbCa.c (+28/-3)
src/ioc/db/dbCaPvt.h (+51/-50)
src/ioc/db/dbScan.c (+65/-17)
src/ioc/db/dbScan.h (+6/-3)
src/ioc/db/test/Makefile (+6/-0)
src/ioc/db/test/dbScanTest.c (+73/-0)
src/ioc/db/test/epicsRunDbTests.c (+2/-0)
To merge this branch: bzr merge lp:~epics-core/epics-base/dbscan-update
Reviewer Review Type Date Requested Status
mdavidsaver Needs Resubmitting
Andrew Johnson Needs Fixing
Ralph Lange Needs Fixing
Review via email: mp+245680@code.launchpad.net

Description of the change

Some misc. changes to dbScan API looking forward to parallel scanning. Adds two new API functions scanOnce3() and scanIoImmediate().

scanOnce3() allows a once scan w/ completion callback. dbCa is changed to use this, which fixes lp:1362362.

scanIoImmediate() allows external drivers to use the I/O Intr infrastructure while doing scanning on internal threads instead of the shared callback threads.

Also a minor changed to eventNameToHandle to avoid a cantProceed().

To post a comment you must log in.
Revision history for this message
mdavidsaver (mdavidsaver) wrote :

I've included a test case for scanOnce3(). I have one for scanIoImmediate(), but this is dependent on a re-write of scanIoTest.c. I'm unsure about including that in this branch as it is much larger than the other changes.

Revision history for this message
Ralph Lange (ralph-lange) wrote :

I like the changes.

scanIoImmediate() needs to be documented.

So - now we have two options for IO scan processing:
1. Request processing on shared background thread created by iocCore
2. Direct processing on current thread

My original idea of "request progressing on private non-shared thread created by iocCore" is still missing, but I will postpone that (and the complexity that would come with it) until I have a valid use case.

review: Needs Fixing
Revision history for this message
mdavidsaver (mdavidsaver) wrote :
Revision history for this message
Andrew Johnson (anj) wrote :

My only objection to this is the name scanOnce3() which doesn't match the style of the other routine names in the IOC. Something like scanOnceCallback() might be more in keeping, see dbCa.h for several examples using that suffix, but feel free to suggest any alternative you prefer. Just adding a number to indicate extra parameters is not a naming style I particularly like.

review: Needs Fixing
12629. By mdavidsaver

dbScan: rename scanOnce3 to scanOnceCallback

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

Renamed scanOnce3 to scanOnceCallback in code and documentation.

review: Needs Resubmitting

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/ioc/db/dbCa.c'
2--- src/ioc/db/dbCa.c 2014-10-06 23:04:33 +0000
3+++ src/ioc/db/dbCa.c 2015-02-17 16:31:52 +0000
4@@ -629,6 +629,31 @@
5 return gotAttributes ? 0 : -1;
6 }
7
8+static void scanComplete(void *raw, dbCommon *prec)
9+{
10+ caLink *pca = raw;
11+ epicsMutexMustLock(pca->lock);
12+ if(pca->scanningOnce==0)
13+ errlogPrintf("dbCa.c complete callback w/ scanningOnce==0\n");
14+ else if(--pca->scanningOnce){
15+ /* another scan is queued */
16+ if(scanOnceCallback(prec, scanComplete, raw)) {
17+ errlogPrintf("dbCa.c failed to re-queue scanOnce\n");
18+ }
19+ }
20+ epicsMutexUnlock(pca->lock);
21+}
22+
23+/* must be called with pca->lock held */
24+static void scanLinkOnce(dbCommon *prec, caLink *pca) {
25+ if(pca->scanningOnce==0 && scanOnceCallback(prec, scanComplete, pca)) {
26+ errlogPrintf("dbCa.c failed to queue scanOnce\n");
27+ }
28+ if(pca->scanningOnce<5)
29+ pca->scanningOnce++;
30+ /* else too many scans queued */
31+}
32+
33 static void connectionCallback(struct connection_handler_args arg)
34 {
35 caLink *pca;
36@@ -649,7 +674,7 @@
37 if (precord &&
38 ((ppv_link->pvlMask & pvlOptCP) ||
39 ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
40- scanOnce(precord);
41+ scanLinkOnce(precord, pca);
42 goto done;
43 }
44 pca->hasReadAccess = ca_read_access(arg.chid);
45@@ -762,7 +787,7 @@
46
47 if ((ppv_link->pvlMask & pvlOptCP) ||
48 ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0))
49- scanOnce(precord);
50+ scanLinkOnce(precord, pca);
51 }
52 done:
53 epicsMutexUnlock(pca->lock);
54@@ -835,7 +860,7 @@
55 if (precord &&
56 ((ppv_link->pvlMask & pvlOptCP) ||
57 ((ppv_link->pvlMask & pvlOptCPP) && precord->scan == 0)))
58- scanOnce(precord);
59+ scanLinkOnce(precord, pca);
60 done:
61 epicsMutexUnlock(pca->lock);
62 }
63
64=== modified file 'src/ioc/db/dbCaPvt.h'
65--- src/ioc/db/dbCaPvt.h 2014-10-06 05:57:02 +0000
66+++ src/ioc/db/dbCaPvt.h 2015-02-17 16:31:52 +0000
67@@ -38,56 +38,57 @@
68
69
70 typedef struct caLink
71 {
72- ELLNODE node;
73- epicsMutexId lock;
74- struct link *plink;
75- char *pvname;
76- chid chid;
77- short link_action;
78- /* The following have new values after each data event*/
79- epicsEnum16 sevr;
80- epicsEnum16 stat;
81- epicsTimeStamp timeStamp;
82- /* The following have values after connection*/
83- short dbrType;
84- long nelements;
85- char hasReadAccess;
86- char hasWriteAccess;
87- char isConnected;
88- char gotFirstConnection;
89- /* The following are for dbCaAddLinkCallback */
90- dbCaCallback connect;
91- dbCaCallback monitor;
92- void *userPvt;
93- /* The following are for write request */
94- short putType;
95- dbCaCallback putCallback;
96- void *putUserPvt;
97- struct link *plinkPutCallback;
98- /* The following are for access to additional attributes*/
99- char gotAttributes;
100- dbCaCallback getAttributes;
101- void *getAttributesPvt;
102- /* The following have values after getAttribEventCallback*/
103- double controlLimits[2];
104- double displayLimits[2];
105- double alarmLimits[4];
106- short precision;
107- char units[MAX_UNITS_SIZE]; /* units of value */
108- /* The following are for handling data*/
109- void *pgetNative;
110- char *pgetString;
111- void *pputNative;
112- char *pputString;
113- char gotInNative;
114- char gotInString;
115- char gotOutNative;
116- char gotOutString;
117- char newOutNative;
118- char newOutString;
119- /* The following are for dbcar*/
120- unsigned long nDisconnect;
121- unsigned long nNoWrite; /*only modified by dbCaPutLink*/
122+ ELLNODE node;
123+ epicsMutexId lock;
124+ struct link *plink;
125+ char *pvname;
126+ chid chid;
127+ short link_action;
128+ /* The following have new values after each data event*/
129+ epicsEnum16 sevr;
130+ epicsEnum16 stat;
131+ epicsTimeStamp timeStamp;
132+ /* The following have values after connection*/
133+ short dbrType;
134+ long nelements;
135+ char hasReadAccess;
136+ char hasWriteAccess;
137+ char isConnected;
138+ char gotFirstConnection;
139+ /* The following are for dbCaAddLinkCallback */
140+ dbCaCallback connect;
141+ dbCaCallback monitor;
142+ void *userPvt;
143+ /* The following are for write request */
144+ short putType;
145+ dbCaCallback putCallback;
146+ void *putUserPvt;
147+ struct link *plinkPutCallback;
148+ /* The following are for access to additional attributes*/
149+ char gotAttributes;
150+ dbCaCallback getAttributes;
151+ void *getAttributesPvt;
152+ /* The following have values after getAttribEventCallback*/
153+ double controlLimits[2];
154+ double displayLimits[2];
155+ double alarmLimits[4];
156+ short precision;
157+ char units[MAX_UNITS_SIZE]; /* units of value */
158+ /* The following are for handling data*/
159+ void *pgetNative;
160+ char *pgetString;
161+ void *pputNative;
162+ char *pputString;
163+ char gotInNative;
164+ char gotInString;
165+ char gotOutNative;
166+ char gotOutString;
167+ char newOutNative;
168+ char newOutString;
169+ unsigned char scanningOnce;
170+ /* The following are for dbcar*/
171+ unsigned long nDisconnect;
172+ unsigned long nNoWrite; /*only modified by dbCaPutLink*/
173 }caLink;
174
175 #endif /* INC_dbCaPvt_H */
176
177=== modified file 'src/ioc/db/dbScan.c'
178--- src/ioc/db/dbScan.c 2014-12-02 17:30:37 +0000
179+++ src/ioc/db/dbScan.c 2015-02-17 16:31:52 +0000
180@@ -26,10 +26,9 @@
181 #include "ellLib.h"
182 #include "epicsEvent.h"
183 #include "epicsExit.h"
184-#include "epicsInterrupt.h"
185 #include "epicsMutex.h"
186 #include "epicsPrint.h"
187-#include "epicsRingPointer.h"
188+#include "epicsRingBytes.h"
189 #include "epicsStdio.h"
190 #include "epicsStdlib.h"
191 #include "epicsString.h"
192@@ -64,7 +63,7 @@
193
194 static int onceQueueSize = 1000;
195 static epicsEventId onceSem;
196-static epicsRingPointerId onceQ;
197+static epicsRingBytesId onceQ;
198 static epicsThreadId onceTaskId;
199 static void *exitOnce;
200
201@@ -172,7 +171,7 @@
202 deletePeriodic();
203 ioscanDestroy();
204
205- epicsRingPointerDelete(onceQ);
206+ epicsRingBytesDelete(onceQ);
207
208 epicsEventDestroy(startStopEvent);
209 epicsEventDestroy(onceSem);
210@@ -456,7 +455,9 @@
211 if (strcmp(pel->event_name, eventname) == 0) break;
212 }
213 if (pel == NULL) {
214- pel = dbCalloc(1, sizeof(event_list));
215+ pel = calloc(1, sizeof(event_list));
216+ if (!pel)
217+ goto done;
218 strcpy(pel->event_name, eventname);
219 for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) {
220 callbackSetUser(&pel->scan_list[prio], &pel->callback[prio]);
221@@ -474,6 +475,7 @@
222 pevent_list[e] = pel;
223 }
224 }
225+done:
226 epicsMutexUnlock(event_lock);
227 return pel;
228 }
229@@ -580,6 +582,28 @@
230 return queued;
231 }
232
233+unsigned int scanIoImmediate(IOSCANPVT piosh, int prio)
234+{
235+ io_scan_list *piosl;
236+
237+ if (prio<0 || prio>=NUM_CALLBACK_PRIORITIES)
238+ return S_db_errArg;
239+ else if (scanCtl != ctlRun)
240+ return 0;
241+
242+ piosl = &piosh->iosl[prio];
243+
244+ if (ellCount(&piosl->scan_list.list) == 0)
245+ return 0;
246+
247+ scanList(&piosl->scan_list);
248+
249+ if (piosh->cb)
250+ piosh->cb(piosh->arg, piosh, prio);
251+
252+ return 1 << prio;
253+}
254+
255 /* May not be called while a scan request is queued or running */
256 void scanIoSetComplete(IOSCANPVT piosh, io_scan_complete cb, void *arg)
257 {
258@@ -587,15 +611,27 @@
259 piosh->arg = arg;
260 }
261
262-void scanOnce(struct dbCommon *precord)
263+int scanOnce(struct dbCommon *precord) {
264+ return scanOnceCallback(precord, NULL, NULL);
265+}
266+
267+typedef struct {
268+ struct dbCommon *prec;
269+ once_complete cb;
270+ void *usr;
271+} onceEntry;
272+
273+int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr)
274 {
275 static int newOverflow = TRUE;
276- int lockKey;
277+ onceEntry ent;
278 int pushOK;
279
280- lockKey = epicsInterruptLock();
281- pushOK = epicsRingPointerPush(onceQ, precord);
282- epicsInterruptUnlock(lockKey);
283+ ent.prec = precord;
284+ ent.cb = cb;
285+ ent.usr = usr;
286+
287+ pushOK = epicsRingBytesPut(onceQ, (void*)&ent, sizeof(ent));
288
289 if (!pushOK) {
290 if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n");
291@@ -604,6 +640,8 @@
292 newOverflow = TRUE;
293 }
294 epicsEventSignal(onceSem);
295+
296+ return !pushOK;
297 }
298
299 static void onceTask(void *arg)
300@@ -612,14 +650,24 @@
301 epicsEventSignal(startStopEvent);
302
303 while (TRUE) {
304- void *precord;
305
306 epicsEventMustWait(onceSem);
307- while ((precord = epicsRingPointerPop(onceQ))) {
308- if (precord == &exitOnce) goto shutdown;
309- dbScanLock(precord);
310- dbProcess(precord);
311- dbScanUnlock(precord);
312+ while(1) {
313+ onceEntry ent;
314+ int bytes = epicsRingBytesGet(onceQ, (void*)&ent, sizeof(ent));
315+ if(bytes==0)
316+ break;
317+ if(bytes!=sizeof(ent)) {
318+ errlogPrintf("onceTask: received incomplete %d of %u\n",
319+ bytes, (unsigned)sizeof(ent));
320+ continue; /* what to do? */
321+ } else if (ent.prec == (void*)&exitOnce) goto shutdown;
322+
323+ dbScanLock(ent.prec);
324+ dbProcess(ent.prec);
325+ dbScanUnlock(ent.prec);
326+ if(ent.cb)
327+ ent.cb(ent.usr, ent.prec);
328 }
329 }
330
331@@ -636,7 +684,7 @@
332
333 static void initOnce(void)
334 {
335- if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) {
336+ if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
337 cantProceed("initOnce: Ring buffer create failed\n");
338 }
339 onceSem = epicsEventMustCreate(epicsEventEmpty);
340
341=== modified file 'src/ioc/db/dbScan.h'
342--- src/ioc/db/dbScan.h 2014-09-30 21:22:03 +0000
343+++ src/ioc/db/dbScan.h 2015-02-17 16:31:52 +0000
344@@ -39,9 +39,10 @@
345 typedef struct ioscan_head *IOSCANPVT;
346 typedef struct event_list *EVENTPVT;
347
348+struct dbCommon;
349+
350 typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio);
351-
352-struct dbCommon;
353+typedef void (*once_complete)(void *usr, struct dbCommon*);
354
355 epicsShareFunc long scanInit(void);
356 epicsShareFunc void scanRun(void);
357@@ -54,7 +55,8 @@
358 epicsShareFunc void scanAdd(struct dbCommon *);
359 epicsShareFunc void scanDelete(struct dbCommon *);
360 epicsShareFunc double scanPeriod(int scan);
361-epicsShareFunc void scanOnce(struct dbCommon *);
362+epicsShareFunc int scanOnce(struct dbCommon *);
363+epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr);
364 epicsShareFunc int scanOnceSetQueueSize(int size);
365
366 /*print periodic lists*/
367@@ -68,6 +70,7 @@
368
369 epicsShareFunc void scanIoInit(IOSCANPVT *ppios);
370 epicsShareFunc unsigned int scanIoRequest(IOSCANPVT pios);
371+epicsShareFunc unsigned int scanIoImmediate(IOSCANPVT pios, int prio);
372 epicsShareFunc void scanIoSetComplete(IOSCANPVT, io_scan_complete, void *usr);
373
374 #ifdef __cplusplus
375
376=== modified file 'src/ioc/db/test/Makefile'
377--- src/ioc/db/test/Makefile 2014-11-19 14:08:48 +0000
378+++ src/ioc/db/test/Makefile 2015-02-17 16:31:52 +0000
379@@ -28,6 +28,12 @@
380
381 PROD_LIBS = dbTestIoc dbCore ca Com
382
383+TESTPROD_HOST += dbScanTest
384+dbScanTest_SRCS += dbScanTest.c
385+dbScanTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
386+testHarness_SRCS += dbScanTest.c
387+TESTS += dbScanTest
388+
389 TESTPROD_HOST += dbShutdownTest
390 dbShutdownTest_SRCS += dbShutdownTest.c
391 dbShutdownTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
392
393=== added file 'src/ioc/db/test/dbScanTest.c'
394--- src/ioc/db/test/dbScanTest.c 1970-01-01 00:00:00 +0000
395+++ src/ioc/db/test/dbScanTest.c 2015-02-17 16:31:52 +0000
396@@ -0,0 +1,73 @@
397+/*************************************************************************\
398+* Copyright (c) 2015 Brookhaven Science Assoc. as operator of Brookhaven
399+* National Laboratory.
400+* EPICS BASE is distributed subject to a Software License Agreement found
401+* in file LICENSE that is included with this distribution.
402+ \*************************************************************************/
403+
404+/*
405+ * Author: Michael Davidsaver <mdavidsaver@bnl.gov>
406+ */
407+
408+#include <string.h>
409+
410+#include "dbScan.h"
411+#include "epicsEvent.h"
412+
413+#include "dbUnitTest.h"
414+#include "testMain.h"
415+
416+#include "dbAccess.h"
417+#include "errlog.h"
418+
419+void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
420+
421+static epicsEventId waiter;
422+static int called;
423+static dbCommon *prec;
424+
425+static void onceComp(void *junk, dbCommon *prec)
426+{
427+ testOk1(junk==(void*)&waiter);
428+ testOk1(strcmp(prec->name, "reca")==0);
429+ called = 1;
430+ epicsEventMustTrigger(waiter);
431+}
432+
433+static void testOnce(void)
434+{
435+ testDiag("check scanOnceCallback() callback");
436+ waiter = epicsEventMustCreate(epicsEventError);
437+
438+ testdbPrepare();
439+
440+ testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
441+ dbTestIoc_registerRecordDeviceDriver(pdbbase);
442+ testdbReadDatabase("dbLockTest.db", NULL, NULL);
443+
444+ eltc(0);
445+ testIocInitOk();
446+ eltc(1);
447+
448+ prec = testdbRecordPtr("reca");
449+
450+ testDiag("scanOnce %s", prec->name);
451+ scanOnceCallback(prec, onceComp, &waiter);
452+ testDiag("Waiting");
453+ epicsEventMustWait(waiter);
454+ testOk1(called==1);
455+ if(!called)
456+ testSkip(2, "callback failed to run");
457+
458+ testIocShutdownOk();
459+
460+ testdbCleanup();
461+ epicsEventDestroy(waiter);
462+}
463+
464+MAIN(dbScanTest)
465+{
466+ testPlan(3);
467+ testOnce();
468+ return testDone();
469+}
470
471=== modified file 'src/ioc/db/test/epicsRunDbTests.c'
472--- src/ioc/db/test/epicsRunDbTests.c 2014-11-19 16:22:39 +0000
473+++ src/ioc/db/test/epicsRunDbTests.c 2015-02-17 16:31:52 +0000
474@@ -22,6 +22,7 @@
475 int dbStateTest(void);
476 int dbCaStatsTest(void);
477 int dbShutdownTest(void);
478+int dbScanTest(void);
479 int scanIoTest(void);
480 int dbLockTest(void);
481 int dbPutLinkTest(void);
482@@ -40,6 +41,7 @@
483 runTest(dbStateTest);
484 runTest(dbCaStatsTest);
485 runTest(dbShutdownTest);
486+ runTest(dbScanTest);
487 runTest(scanIoTest);
488 runTest(dbLockTest);
489 runTest(dbPutLinkTest);

Subscribers

People subscribed via source and target branches