Merge ~anj/epics-base/+git/base-3.15:decimate-filter into ~epics-core/epics-base/+git/epics-base:3.15
- Git
- lp:~anj/epics-base/+git/base-3.15
- decimate-filter
- Merge into 3.15
Status: | Merged |
---|---|
Approved by: | Andrew Johnson |
Approved revision: | 84c86e67e817e17024cc9fff151a8bfbd61cc89e |
Merged at revision: | 9722e707fd507efac23f68dc829936e24c25d000 |
Proposed branch: | ~anj/epics-base/+git/base-3.15:decimate-filter |
Merge into: | ~epics-core/epics-base/+git/epics-base:3.15 |
Diff against target: |
1092 lines (+651/-68) 12 files modified
documentation/RELEASE_NOTES.html (+17/-0) src/ioc/db/dbEvent.c (+5/-0) src/ioc/db/dbEvent.h (+1/-1) src/std/filters/Makefile (+1/-0) src/std/filters/decimate.c (+117/-0) src/std/filters/filters.dbd.pod (+40/-0) src/std/filters/sync.c (+16/-8) src/std/filters/test/Makefile (+6/-0) src/std/filters/test/dbndTest.c (+30/-1) src/std/filters/test/decTest.c (+289/-0) src/std/filters/test/epicsRunFilterTests.c (+2/-0) src/std/filters/test/syncTest.c (+127/-58) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Johnson | Approve | ||
mdavidsaver | Approve | ||
Review via email: mp+368347@code.launchpad.net |
Commit message
Description of the change
Add the decimation channel filter that Hinko and Emanuele were looking for to the 3.15 branch.
This merge request contains the filter code, user documentation in the filters.dbd.pod file and Release Notes, and test code.
Andrew Johnson (anj) wrote : | # |
RL: Ask, if a filter function drops an event it must call db_delete_
Andrew Johnson (anj) wrote : | # |
Revisit tests, MAD and RL to look further.
AI on AJ to fix the leak.
Andrew Johnson (anj) wrote : | # |
Tonight's commit fixes the decimate filter and adds tests to ensure the field-logs get freed properly when dropped by the filter.
Work still to come on fixing the other filters and tests.
mdavidsaver (mdavidsaver) wrote : | # |
It looks like the leak is plugged. valgrind turns up one (I think minor) issue.
> Conditional jump or move depends on uninitialised value(s)
> at 0x4C31CC2: __memcmp_sse4_1 (vg_replace_
> by 0x10A27E: fl_equal (decTest.c:36)
> by 0x10A27E: checkAndOpenCha
> by 0x10985F: main (decTest.c:167)
>
> Conditional jump or move depends on uninitialised value(s)
> at 0x4C31CD6: __memcmp_sse4_1 (vg_replace_
> by 0x10A27E: fl_equal (decTest.c:36)
> by 0x10A27E: checkAndOpenCha
> by 0x10985F: main (decTest.c:167)
Andrew Johnson (anj) wrote : | # |
I tried adding code to set the pfl->field_size member in fl_setup(), but that wasn't enough to fix the valgrind reports and was also missing from the syncTest.c and dbndTest.c programs. I therefor added an initial memset(), which did fix them.
Last night I also committed some additional tests to those 3 programs which check for the expected number of items on the freelist, and I also fixed some leaks in the sync filter.
Andrew Johnson (anj) wrote : | # |
Core Mtg: Suggest splitting off the bug-fixes to their own branch and commit separately.
mdavidsaver (mdavidsaver) wrote : | # |
I haven't rerun valgrind myself, but I think it is good to go as the leak is plugged.
Andrew Johnson (anj) wrote : | # |
Group 10/4: No issues.
Preview Diff
1 | diff --git a/documentation/RELEASE_NOTES.html b/documentation/RELEASE_NOTES.html |
2 | index 8ed4e04..c7fbc02 100644 |
3 | --- a/documentation/RELEASE_NOTES.html |
4 | +++ b/documentation/RELEASE_NOTES.html |
5 | @@ -16,6 +16,23 @@ |
6 | |
7 | <!-- Insert new items immediately below here ... --> |
8 | |
9 | +<h3>Added new decimation channel filter</h3> |
10 | + |
11 | +<p>A new server-side filter has been added to the IOC for reducing the number |
12 | +and frequency of monitor updates from a channel by a client-specified factor. |
13 | +The filter's behaviour is quite simplistic, it passes the first monitor event it |
14 | +sees to the client and then drops the next N-1 events before passing another |
15 | +event. For example to sample a 60Hz channel at 1Hz, a 10Hz channel every 6 |
16 | +seconds, or a 1Hz channel once every minute:</p> |
17 | + |
18 | +<pre> Hal$ camonitor 'test:channel.{"dec":{"n":60}}' |
19 | + ...</pre> |
20 | + |
21 | +<p>More information is included in the |
22 | +<a href="filters.html">filters</a><!-- href for the EPICS website --> |
23 | +<a href="../html/filters.html">documentation</a><!-- href for install tree --> |
24 | +file.</p> |
25 | + |
26 | <h3>Cleaning up with Multiple CA contexts in a Process</h3> |
27 | |
28 | <p>Bruno Martins reported a problem with the CA client library at shutdown in a |
29 | diff --git a/src/ioc/db/dbEvent.c b/src/ioc/db/dbEvent.c |
30 | index fb1f3a1..d1f9548 100644 |
31 | --- a/src/ioc/db/dbEvent.c |
32 | +++ b/src/ioc/db/dbEvent.c |
33 | @@ -1165,3 +1165,8 @@ void db_delete_field_log (db_field_log *pfl) |
34 | freeListFree(dbevFieldLogFreeList, pfl); |
35 | } |
36 | } |
37 | + |
38 | +int db_available_logs(void) |
39 | +{ |
40 | + return (int) freeListItemsAvail(dbevFieldLogFreeList); |
41 | +} |
42 | diff --git a/src/ioc/db/dbEvent.h b/src/ioc/db/dbEvent.h |
43 | index fe0e52f..2e496a7 100644 |
44 | --- a/src/ioc/db/dbEvent.h |
45 | +++ b/src/ioc/db/dbEvent.h |
46 | @@ -78,6 +78,7 @@ epicsShareFunc void db_event_disable (dbEventSubscription es); |
47 | epicsShareFunc struct db_field_log* db_create_event_log (struct evSubscrip *pevent); |
48 | epicsShareFunc struct db_field_log* db_create_read_log (struct dbChannel *chan); |
49 | epicsShareFunc void db_delete_field_log (struct db_field_log *pfl); |
50 | +epicsShareFunc int db_available_logs(void); |
51 | |
52 | #define DB_EVENT_OK 0 |
53 | #define DB_EVENT_ERROR (-1) |
54 | @@ -87,4 +88,3 @@ epicsShareFunc void db_delete_field_log (struct db_field_log *pfl); |
55 | #endif |
56 | |
57 | #endif /*INCLdbEventh*/ |
58 | - |
59 | diff --git a/src/std/filters/Makefile b/src/std/filters/Makefile |
60 | index d453989..3a27361 100644 |
61 | --- a/src/std/filters/Makefile |
62 | +++ b/src/std/filters/Makefile |
63 | @@ -15,6 +15,7 @@ dbRecStd_SRCS += ts.c |
64 | dbRecStd_SRCS += dbnd.c |
65 | dbRecStd_SRCS += arr.c |
66 | dbRecStd_SRCS += sync.c |
67 | +dbRecStd_SRCS += decimate.c |
68 | |
69 | HTMLS += filters.html |
70 | |
71 | diff --git a/src/std/filters/decimate.c b/src/std/filters/decimate.c |
72 | new file mode 100644 |
73 | index 0000000..502422f |
74 | --- /dev/null |
75 | +++ b/src/std/filters/decimate.c |
76 | @@ -0,0 +1,117 @@ |
77 | +/*************************************************************************\ |
78 | +* Copyright (c) 2019 UChicago Argonne LLC, as Operator of Argonne |
79 | +* National Laboratory. |
80 | +* Copyright (c) 2010 Brookhaven National Laboratory. |
81 | +* Copyright (c) 2010 Helmholtz-Zentrum Berlin |
82 | +* fuer Materialien und Energie GmbH. |
83 | +* EPICS BASE is distributed subject to a Software License Agreement found |
84 | +* in file LICENSE that is included with this distribution. |
85 | +\*************************************************************************/ |
86 | + |
87 | +/* |
88 | + * Authors: Ralph Lange <Ralph.Lange@bessy.de>, |
89 | + * Andrew Johnson <anj@anl.gov> |
90 | + */ |
91 | + |
92 | +#include <stdio.h> |
93 | + |
94 | +#include "freeList.h" |
95 | +#include "db_field_log.h" |
96 | +#include "chfPlugin.h" |
97 | +#include "epicsExport.h" |
98 | + |
99 | +typedef struct myStruct { |
100 | + epicsInt32 n, i; |
101 | +} myStruct; |
102 | + |
103 | +static void *myStructFreeList; |
104 | + |
105 | +static const |
106 | +chfPluginArgDef opts[] = { |
107 | + chfInt32(myStruct, n, "n", 1, 0), |
108 | + chfPluginArgEnd |
109 | +}; |
110 | + |
111 | +static void * allocPvt(void) |
112 | +{ |
113 | + myStruct *my = (myStruct*) freeListCalloc(myStructFreeList); |
114 | + return (void *) my; |
115 | +} |
116 | + |
117 | +static void freePvt(void *pvt) |
118 | +{ |
119 | + freeListFree(myStructFreeList, pvt); |
120 | +} |
121 | + |
122 | +static int parse_ok(void *pvt) |
123 | +{ |
124 | + myStruct *my = (myStruct*) pvt; |
125 | + |
126 | + if (my->n < 1) |
127 | + return -1; |
128 | + |
129 | + return 0; |
130 | +} |
131 | + |
132 | +static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { |
133 | + db_field_log *passfl = NULL; |
134 | + myStruct *my = (myStruct*) pvt; |
135 | + epicsInt32 i = my->i; |
136 | + |
137 | + if (pfl->ctx == dbfl_context_read) |
138 | + return pfl; |
139 | + |
140 | + if (i++ == 0) |
141 | + passfl = pfl; |
142 | + else |
143 | + db_delete_field_log(pfl); |
144 | + |
145 | + if (i >= my->n) |
146 | + i = 0; |
147 | + |
148 | + my->i = i; |
149 | + return passfl; |
150 | +} |
151 | + |
152 | +static void channelRegisterPre(dbChannel *chan, void *pvt, |
153 | + chPostEventFunc **cb_out, void **arg_out, db_field_log *probe) |
154 | +{ |
155 | + *cb_out = filter; |
156 | + *arg_out = pvt; |
157 | +} |
158 | + |
159 | +static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent) |
160 | +{ |
161 | + myStruct *my = (myStruct*) pvt; |
162 | + printf("%*sDecimate (dec): n=%d, i=%d\n", indent, "", |
163 | + my->n, my->i); |
164 | +} |
165 | + |
166 | +static chfPluginIf pif = { |
167 | + allocPvt, |
168 | + freePvt, |
169 | + |
170 | + NULL, /* parse_error, */ |
171 | + parse_ok, |
172 | + |
173 | + NULL, /* channel_open, */ |
174 | + channelRegisterPre, |
175 | + NULL, /* channelRegisterPost, */ |
176 | + channel_report, |
177 | + NULL /* channel_close */ |
178 | +}; |
179 | + |
180 | +static void decInitialize(void) |
181 | +{ |
182 | + static int firstTime = 1; |
183 | + |
184 | + if (!firstTime) return; |
185 | + firstTime = 0; |
186 | + |
187 | + if (!myStructFreeList) |
188 | + freeListInitPvt(&myStructFreeList, sizeof(myStruct), 64); |
189 | + |
190 | + chfPluginRegister("dec", &pif, opts); |
191 | +} |
192 | + |
193 | +epicsExportRegistrar(decInitialize); |
194 | diff --git a/src/std/filters/filters.dbd.pod b/src/std/filters/filters.dbd.pod |
195 | index d7ab785..27e356f 100644 |
196 | --- a/src/std/filters/filters.dbd.pod |
197 | +++ b/src/std/filters/filters.dbd.pod |
198 | @@ -14,6 +14,8 @@ The following filters are available in this release: |
199 | |
200 | =item * L<Synchronize|/"Synchronize Filter sync"> |
201 | |
202 | +=item * L<Decimation|/"Decimation Filter dec"> |
203 | + |
204 | =back |
205 | |
206 | =head2 Using Filters |
207 | @@ -245,3 +247,41 @@ periods only when "blue" is true by using |
208 | ... |
209 | |
210 | =cut |
211 | + |
212 | +registrar(decInitialize) |
213 | + |
214 | +=head3 Decimation Filter C<"dec"> |
215 | + |
216 | +This filter is used to reduce the number or rate of monitor updates from a |
217 | +channel by an integer factor C<n> that is provided as a filter argument, |
218 | +discarding the other updates. A true decimation following the original meaning |
219 | +of the word would be achieved by giving C<n> as 10, to only allow every tenth |
220 | +update through. |
221 | + |
222 | +=head4 Parameters |
223 | + |
224 | +=over |
225 | + |
226 | +=item Number C<"n"> |
227 | + |
228 | +The decimation factor, a positive integer. Giving n=1 is equivalent to a no-op |
229 | +that allows all updates to be passed to the client. |
230 | + |
231 | +=back |
232 | + |
233 | +This filter is intentionally very simplistic. It passes on the first monitor |
234 | +event that it sees after the channel connects, then discards the next N-1 events |
235 | +before sending the next event. If several clients connect to a channel using the |
236 | +same filter settings they may see completely different data streams since each |
237 | +client gets its own instance of the filter whose event counter starts when that |
238 | +client connects. |
239 | + |
240 | +=head4 Example |
241 | + |
242 | +To sample a 60Hz channel at 1Hz, a 10Hz channel every 6 seconds or a 1Hz channel |
243 | +once every minute: |
244 | + |
245 | + Hal$ camonitor 'test:channel' 'test:channel.{"dec":{"n":60}}' |
246 | + ... |
247 | + |
248 | +=cut |
249 | diff --git a/src/std/filters/sync.c b/src/std/filters/sync.c |
250 | index c32e9af..6b841ea 100644 |
251 | --- a/src/std/filters/sync.c |
252 | +++ b/src/std/filters/sync.c |
253 | @@ -109,7 +109,9 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { |
254 | passfl = pfl; |
255 | pfl = NULL; |
256 | } |
257 | - break; |
258 | + else |
259 | + db_delete_field_log(pfl); |
260 | + goto save_state; |
261 | case syncModeLast: |
262 | if (!actstate && my->laststate) { |
263 | passfl = my->lastfl; |
264 | @@ -121,28 +123,34 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) { |
265 | passfl = pfl; |
266 | pfl = NULL; |
267 | } |
268 | - break; |
269 | + else |
270 | + db_delete_field_log(pfl); |
271 | + goto save_state; |
272 | case syncModeWhile: |
273 | - if (actstate) { |
274 | + if (actstate) |
275 | passfl = pfl; |
276 | - } |
277 | + else |
278 | + db_delete_field_log(pfl); |
279 | goto no_shift; |
280 | case syncModeUnless: |
281 | - if (!actstate) { |
282 | + if (!actstate) |
283 | passfl = pfl; |
284 | - } |
285 | + else |
286 | + db_delete_field_log(pfl); |
287 | goto no_shift; |
288 | } |
289 | |
290 | if (my->lastfl) |
291 | db_delete_field_log(my->lastfl); |
292 | my->lastfl = pfl; |
293 | - my->laststate = actstate; |
294 | |
295 | /* since no copy is made we can't keep a reference to the returned fl */ |
296 | assert(my->lastfl != passfl); |
297 | |
298 | - no_shift: |
299 | +save_state: |
300 | + my->laststate = actstate; |
301 | + |
302 | +no_shift: |
303 | return passfl; |
304 | } |
305 | |
306 | diff --git a/src/std/filters/test/Makefile b/src/std/filters/test/Makefile |
307 | index 6e6ad79..ad77c5d 100644 |
308 | --- a/src/std/filters/test/Makefile |
309 | +++ b/src/std/filters/test/Makefile |
310 | @@ -56,6 +56,12 @@ syncTest_SRCS += filterTest_registerRecordDeviceDriver.cpp |
311 | testHarness_SRCS += syncTest.c |
312 | TESTS += syncTest |
313 | |
314 | +TESTPROD_HOST += decTest |
315 | +decTest_SRCS += decTest.c |
316 | +decTest_SRCS += filterTest_registerRecordDeviceDriver.cpp |
317 | +testHarness_SRCS += decTest.c |
318 | +TESTS += decTest |
319 | + |
320 | # epicsRunFilterTests runs all the test programs in a known working order. |
321 | testHarness_SRCS += epicsRunFilterTests.c |
322 | |
323 | diff --git a/src/std/filters/test/dbndTest.c b/src/std/filters/test/dbndTest.c |
324 | index b35b9a6..4d70f83 100644 |
325 | --- a/src/std/filters/test/dbndTest.c |
326 | +++ b/src/std/filters/test/dbndTest.c |
327 | @@ -39,12 +39,14 @@ static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { |
328 | static void fl_setup(dbChannel *chan, db_field_log *pfl) { |
329 | struct dbCommon *prec = dbChannelRecord(chan); |
330 | |
331 | + memset(pfl, 0, sizeof(db_field_log)); |
332 | pfl->ctx = dbfl_context_read; |
333 | pfl->type = dbfl_type_val; |
334 | pfl->stat = prec->stat; |
335 | pfl->sevr = prec->sevr; |
336 | pfl->time = prec->time; |
337 | pfl->field_type = dbChannelFieldType(chan); |
338 | + pfl->field_size = dbChannelFieldSize(chan); |
339 | pfl->no_elements = dbChannelElements(chan); |
340 | /* |
341 | * use memcpy to avoid a bus error on |
342 | @@ -62,6 +64,7 @@ static void changeValue(db_field_log *pfl2, long val) { |
343 | } |
344 | |
345 | static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { |
346 | + int oldFree = db_available_logs(), newFree; |
347 | db_field_log *pfl; |
348 | |
349 | changeValue(pfl2, val); |
350 | @@ -71,18 +74,26 @@ static void mustPassOnce(dbChannel *pch, db_field_log *pfl2, char* m, double d, |
351 | testOk(fl_equal(pfl, pfl2), "call 1 does not change field_log data"); |
352 | pfl = dbChannelRunPreChain(pch, pfl2); |
353 | testOk(NULL == pfl, "call 2 drops field_log"); |
354 | + newFree = db_available_logs(); |
355 | + testOk(newFree == oldFree + 1, "field_log was freed - %d+1 => %d", |
356 | + oldFree, newFree); |
357 | } |
358 | |
359 | static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { |
360 | + int oldFree = db_available_logs(), newFree; |
361 | db_field_log *pfl; |
362 | |
363 | changeValue(pfl2, val); |
364 | testDiag("mode=%s delta=%g filter must drop", m, d); |
365 | pfl = dbChannelRunPreChain(pch, pfl2); |
366 | testOk(NULL == pfl, "call 1 drops field_log"); |
367 | + newFree = db_available_logs(); |
368 | + testOk(newFree == oldFree + 1, "field_log was freed - %d+1 => %d", |
369 | + oldFree, newFree); |
370 | } |
371 | |
372 | static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m, double d, long val) { |
373 | + int oldFree = db_available_logs(), newFree; |
374 | db_field_log *pfl; |
375 | |
376 | changeValue(pfl2, val); |
377 | @@ -93,6 +104,9 @@ static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m, double d, |
378 | pfl = dbChannelRunPreChain(pch, pfl2); |
379 | testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); |
380 | testOk(fl_equal(pfl, pfl2), "call 2 does not change field_log data"); |
381 | + newFree = db_available_logs(); |
382 | + testOk(newFree == oldFree, "field_log was not freed - %d => %d", |
383 | + oldFree, newFree); |
384 | } |
385 | |
386 | static void testHead (char* title) { |
387 | @@ -113,8 +127,9 @@ MAIN(dbndTest) |
388 | db_field_log *pfl2; |
389 | db_field_log fl1; |
390 | dbEventCtx evtctx; |
391 | + int logsFree, logsFinal; |
392 | |
393 | - testPlan(59); |
394 | + testPlan(77); |
395 | |
396 | testdbPrepare(); |
397 | |
398 | @@ -135,6 +150,11 @@ MAIN(dbndTest) |
399 | testOk(!!(pch = dbChannelCreate("x.VAL{\"dbnd\":{}}")), "dbChannel with plugin dbnd (delta=0) created"); |
400 | testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); |
401 | |
402 | + /* Start the free-list */ |
403 | + db_delete_field_log(db_create_read_log(pch)); |
404 | + logsFree = db_available_logs(); |
405 | + testDiag("%d field_logs on free-list", logsFree); |
406 | + |
407 | memset(&fl, PATTERN, sizeof(fl)); |
408 | fl1 = fl; |
409 | node = ellFirst(&pch->filters); |
410 | @@ -176,6 +196,8 @@ MAIN(dbndTest) |
411 | |
412 | dbChannelDelete(pch); |
413 | |
414 | + testDiag("%d field_logs on free-list", db_available_logs()); |
415 | + |
416 | /* Delta = -1: pass any update */ |
417 | |
418 | testHead("Delta = -1: pass any update"); |
419 | @@ -192,6 +214,8 @@ MAIN(dbndTest) |
420 | db_delete_field_log(pfl2); |
421 | dbChannelDelete(pch); |
422 | |
423 | + testDiag("%d field_logs on free-list", db_available_logs()); |
424 | + |
425 | /* Delta = absolute */ |
426 | |
427 | testHead("Delta = absolute"); |
428 | @@ -224,6 +248,8 @@ MAIN(dbndTest) |
429 | |
430 | dbChannelDelete(pch); |
431 | |
432 | + testDiag("%d field_logs on free-list", db_available_logs()); |
433 | + |
434 | /* Delta = relative */ |
435 | |
436 | testHead("Delta = relative"); |
437 | @@ -275,6 +301,9 @@ MAIN(dbndTest) |
438 | |
439 | dbChannelDelete(pch); |
440 | |
441 | + logsFinal = db_available_logs(); |
442 | + testOk(logsFree == logsFinal, "%d field_logs on free-list", logsFinal); |
443 | + |
444 | db_close_events(evtctx); |
445 | |
446 | testIocShutdownOk(); |
447 | diff --git a/src/std/filters/test/decTest.c b/src/std/filters/test/decTest.c |
448 | new file mode 100644 |
449 | index 0000000..3b67842 |
450 | --- /dev/null |
451 | +++ b/src/std/filters/test/decTest.c |
452 | @@ -0,0 +1,289 @@ |
453 | +/*************************************************************************\ |
454 | +* Copyright (c) 2019 UChicago Argonne LLC, as Operator of Argonne |
455 | +* National Laboratory. |
456 | +* Copyright (c) 2010 Brookhaven National Laboratory. |
457 | +* Copyright (c) 2010 Helmholtz-Zentrum Berlin |
458 | +* fuer Materialien und Energie GmbH. |
459 | +* EPICS BASE is distributed subject to a Software License Agreement found |
460 | +* in file LICENSE that is included with this distribution. |
461 | +\*************************************************************************/ |
462 | + |
463 | +/* |
464 | + * Authors: Ralph Lange <Ralph.Lange@bessy.de>, |
465 | + * Andrew Johnson <anj@anl.gov> |
466 | + */ |
467 | + |
468 | +#include <string.h> |
469 | + |
470 | +#include "dbStaticLib.h" |
471 | +#include "dbAccessDefs.h" |
472 | +#include "db_field_log.h" |
473 | +#include "dbCommon.h" |
474 | +#include "dbChannel.h" |
475 | +#include "registry.h" |
476 | +#include "chfPlugin.h" |
477 | +#include "errlog.h" |
478 | +#include "dbmf.h" |
479 | +#include "epicsUnitTest.h" |
480 | +#include "dbUnitTest.h" |
481 | +#include "epicsTime.h" |
482 | +#include "testMain.h" |
483 | +#include "osiFileName.h" |
484 | + |
485 | +void filterTest_registerRecordDeviceDriver(struct dbBase *); |
486 | + |
487 | +static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { |
488 | + return !(memcmp(pfl1, pfl2, sizeof(db_field_log))); |
489 | +} |
490 | + |
491 | +static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) { |
492 | + struct dbCommon *prec = dbChannelRecord(chan); |
493 | + |
494 | + memset(pfl, 0, sizeof(db_field_log)); |
495 | + pfl->ctx = dbfl_context_event; |
496 | + pfl->type = dbfl_type_val; |
497 | + pfl->stat = prec->stat; |
498 | + pfl->sevr = prec->sevr; |
499 | + pfl->time = prec->time; |
500 | + pfl->field_type = DBF_LONG; |
501 | + pfl->field_size = sizeof(epicsInt32); |
502 | + pfl->no_elements = 1; |
503 | + /* |
504 | + * use memcpy to avoid a bus error on |
505 | + * union copy of char in the db at an odd |
506 | + * address |
507 | + */ |
508 | + memcpy(&pfl->u.v.field, |
509 | + dbChannelField(chan), |
510 | + dbChannelFieldSize(chan)); |
511 | + pfl->u.v.field.dbf_long = val; |
512 | +} |
513 | + |
514 | +static void testHead (char* title) { |
515 | + testDiag("--------------------------------------------------------"); |
516 | + testDiag("%s", title); |
517 | + testDiag("--------------------------------------------------------"); |
518 | +} |
519 | + |
520 | +static void mustDrop(dbChannel *pch, db_field_log *pfl, char* m) { |
521 | + int oldFree = db_available_logs(); |
522 | + db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); |
523 | + int newFree = db_available_logs(); |
524 | + |
525 | + testOk(NULL == pfl2, "filter drops field_log (%s)", m); |
526 | + testOk(newFree == oldFree + 1, "field_log was freed - %d+1 => %d", |
527 | + oldFree, newFree); |
528 | + |
529 | + db_delete_field_log(pfl2); |
530 | +} |
531 | + |
532 | +static void mustPass(dbChannel *pch, db_field_log *pfl, char* m) { |
533 | + int oldFree = db_available_logs(); |
534 | + db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); |
535 | + int newFree = db_available_logs(); |
536 | + |
537 | + testOk(pfl == pfl2, "filter passes field_log (%s)", m); |
538 | + testOk(newFree == oldFree, "field_log was not freed - %d => %d", |
539 | + oldFree, newFree); |
540 | + |
541 | + db_delete_field_log(pfl2); |
542 | +} |
543 | + |
544 | +static void checkAndOpenChannel(dbChannel *pch, const chFilterPlugin *plug) { |
545 | + ELLNODE *node; |
546 | + chFilter *filter; |
547 | + chPostEventFunc *cb_out = NULL; |
548 | + void *arg_out = NULL; |
549 | + db_field_log fl, fl1; |
550 | + |
551 | + testDiag("Test filter structure and open channel"); |
552 | + |
553 | + testOk((ellCount(&pch->filters) == 1), "channel has one plugin"); |
554 | + |
555 | + fl_setup(pch, &fl, 1); |
556 | + fl1 = fl; |
557 | + node = ellFirst(&pch->filters); |
558 | + filter = CONTAINER(node, chFilter, list_node); |
559 | + plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1); |
560 | + testOk(cb_out && arg_out, |
561 | + "register_pre registers one filter with argument"); |
562 | + testOk(fl_equal(&fl1, &fl), |
563 | + "register_pre does not change field_log data type"); |
564 | + |
565 | + testOk(!(dbChannelOpen(pch)), "dbChannel with plugin dec opened"); |
566 | + node = ellFirst(&pch->pre_chain); |
567 | + filter = CONTAINER(node, chFilter, pre_node); |
568 | + testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg != NULL), |
569 | + "dec has one filter with argument in pre chain"); |
570 | + testOk((ellCount(&pch->post_chain) == 0), |
571 | + "sync has no filter in post chain"); |
572 | +} |
573 | + |
574 | +MAIN(decTest) |
575 | +{ |
576 | + dbChannel *pch; |
577 | + const chFilterPlugin *plug; |
578 | + char myname[] = "dec"; |
579 | + db_field_log *pfl[10]; |
580 | + int i, logsFree, logsFinal; |
581 | + dbEventCtx evtctx; |
582 | + |
583 | + testPlan(104); |
584 | + |
585 | + testdbPrepare(); |
586 | + |
587 | + testdbReadDatabase("filterTest.dbd", NULL, NULL); |
588 | + |
589 | + filterTest_registerRecordDeviceDriver(pdbbase); |
590 | + |
591 | + testdbReadDatabase("xRecord.db", NULL, NULL); |
592 | + |
593 | + eltc(0); |
594 | + testIocInitOk(); |
595 | + eltc(1); |
596 | + |
597 | + evtctx = db_init_events(); |
598 | + |
599 | + testOk(!!(plug = dbFindFilter(myname, strlen(myname))), |
600 | + "plugin '%s' registered correctly", myname); |
601 | + |
602 | + /* N < 1 */ |
603 | + testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":-1}}")), |
604 | + "dbChannel with dec (n=-1) failed"); |
605 | + testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":0}}")), |
606 | + "dbChannel with dec (n=0) failed"); |
607 | + /* Bad parms */ |
608 | + testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{}}")), |
609 | + "dbChannel with dec (no parm) failed"); |
610 | + testOk(!(pch = dbChannelCreate("x.VAL{\"dec\":{\"x\":true}}")), |
611 | + "dbChannel with dec (x=true) failed"); |
612 | + |
613 | + /* No Decimation (N=1) */ |
614 | + |
615 | + testHead("No Decimation (n=1)"); |
616 | + testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":1}}")), |
617 | + "dbChannel with plugin dec (n=1) created"); |
618 | + |
619 | + /* Start the free-list */ |
620 | + db_delete_field_log(db_create_read_log(pch)); |
621 | + logsFree = db_available_logs(); |
622 | + testDiag("%d field_logs on free-list", logsFree); |
623 | + |
624 | + checkAndOpenChannel(pch, plug); |
625 | + |
626 | + for (i = 0; i < 5; i++) { |
627 | + pfl[i] = db_create_read_log(pch); |
628 | + fl_setup(pch, pfl[i], 10 + i); |
629 | + } |
630 | + |
631 | + testDiag("Test event stream"); |
632 | + |
633 | + mustPass(pch, pfl[0], "i=0"); |
634 | + mustPass(pch, pfl[1], "i=1"); |
635 | + mustPass(pch, pfl[2], "i=2"); |
636 | + mustPass(pch, pfl[3], "i=3"); |
637 | + mustPass(pch, pfl[4], "i=4"); |
638 | + |
639 | + dbChannelDelete(pch); |
640 | + |
641 | + testDiag("%d field_logs on free-list", db_available_logs()); |
642 | + |
643 | + /* Decimation (N=2) */ |
644 | + |
645 | + testHead("Decimation (n=2)"); |
646 | + testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":2}}")), |
647 | + "dbChannel with plugin dec (n=2) created"); |
648 | + |
649 | + checkAndOpenChannel(pch, plug); |
650 | + |
651 | + for (i = 0; i < 10; i++) { |
652 | + pfl[i] = db_create_read_log(pch); |
653 | + fl_setup(pch, pfl[i], 20 + i); |
654 | + } |
655 | + |
656 | + testDiag("Test event stream"); |
657 | + |
658 | + mustPass(pch, pfl[0], "i=0"); |
659 | + mustDrop(pch, pfl[1], "i=1"); |
660 | + mustPass(pch, pfl[2], "i=2"); |
661 | + mustDrop(pch, pfl[3], "i=3"); |
662 | + mustPass(pch, pfl[4], "i=4"); |
663 | + mustDrop(pch, pfl[5], "i=5"); |
664 | + mustPass(pch, pfl[6], "i=6"); |
665 | + mustDrop(pch, pfl[7], "i=7"); |
666 | + mustPass(pch, pfl[8], "i=8"); |
667 | + mustDrop(pch, pfl[9], "i=9"); |
668 | + |
669 | + dbChannelDelete(pch); |
670 | + |
671 | + testDiag("%d field_logs on free-list", db_available_logs()); |
672 | + |
673 | + /* Decimation (N=3) */ |
674 | + |
675 | + testHead("Decimation (n=3)"); |
676 | + testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":3}}")), |
677 | + "dbChannel with plugin dec (n=3) created"); |
678 | + |
679 | + checkAndOpenChannel(pch, plug); |
680 | + |
681 | + for (i = 0; i < 10; i++) { |
682 | + pfl[i] = db_create_read_log(pch); |
683 | + fl_setup(pch, pfl[i], 30 + i); |
684 | + } |
685 | + |
686 | + testDiag("Test event stream"); |
687 | + |
688 | + mustPass(pch, pfl[0], "i=0"); |
689 | + mustDrop(pch, pfl[1], "i=1"); |
690 | + mustDrop(pch, pfl[2], "i=2"); |
691 | + mustPass(pch, pfl[3], "i=3"); |
692 | + mustDrop(pch, pfl[4], "i=4"); |
693 | + mustDrop(pch, pfl[5], "i=5"); |
694 | + mustPass(pch, pfl[6], "i=6"); |
695 | + mustDrop(pch, pfl[7], "i=7"); |
696 | + mustDrop(pch, pfl[8], "i=8"); |
697 | + mustPass(pch, pfl[9], "i=9"); |
698 | + |
699 | + dbChannelDelete(pch); |
700 | + |
701 | + testDiag("%d field_logs on free-list", db_available_logs()); |
702 | + |
703 | + /* Decimation (N=4) */ |
704 | + |
705 | + testHead("Decimation (n=4)"); |
706 | + testOk(!!(pch = dbChannelCreate("x.VAL{\"dec\":{\"n\":4}}")), |
707 | + "dbChannel with plugin dec (n=4) created"); |
708 | + |
709 | + checkAndOpenChannel(pch, plug); |
710 | + |
711 | + for (i = 0; i < 10; i++) { |
712 | + pfl[i] = db_create_read_log(pch); |
713 | + fl_setup(pch, pfl[i], 40 + i); |
714 | + } |
715 | + |
716 | + testDiag("Test event stream"); |
717 | + |
718 | + mustPass(pch, pfl[0], "i=0"); |
719 | + mustDrop(pch, pfl[1], "i=1"); |
720 | + mustDrop(pch, pfl[2], "i=2"); |
721 | + mustDrop(pch, pfl[3], "i=3"); |
722 | + mustPass(pch, pfl[4], "i=4"); |
723 | + mustDrop(pch, pfl[5], "i=5"); |
724 | + mustDrop(pch, pfl[6], "i=6"); |
725 | + mustDrop(pch, pfl[7], "i=7"); |
726 | + mustPass(pch, pfl[8], "i=8"); |
727 | + mustDrop(pch, pfl[9], "i=9"); |
728 | + |
729 | + dbChannelDelete(pch); |
730 | + |
731 | + logsFinal = db_available_logs(); |
732 | + testOk(logsFree == logsFinal, "%d field_logs on free-list", logsFinal); |
733 | + |
734 | + db_close_events(evtctx); |
735 | + |
736 | + testIocShutdownOk(); |
737 | + |
738 | + testdbCleanup(); |
739 | + |
740 | + return testDone(); |
741 | +} |
742 | diff --git a/src/std/filters/test/epicsRunFilterTests.c b/src/std/filters/test/epicsRunFilterTests.c |
743 | index 2363643..5737d77 100644 |
744 | --- a/src/std/filters/test/epicsRunFilterTests.c |
745 | +++ b/src/std/filters/test/epicsRunFilterTests.c |
746 | @@ -17,6 +17,7 @@ int tsTest(void); |
747 | int dbndTest(void); |
748 | int syncTest(void); |
749 | int arrTest(void); |
750 | +int decTest(void); |
751 | |
752 | void epicsRunFilterTests(void) |
753 | { |
754 | @@ -26,6 +27,7 @@ void epicsRunFilterTests(void) |
755 | runTest(dbndTest); |
756 | runTest(syncTest); |
757 | runTest(arrTest); |
758 | + runTest(decTest); |
759 | |
760 | dbmfFreeChunks(); |
761 | |
762 | diff --git a/src/std/filters/test/syncTest.c b/src/std/filters/test/syncTest.c |
763 | index 9af44af..9be23b0 100644 |
764 | --- a/src/std/filters/test/syncTest.c |
765 | +++ b/src/std/filters/test/syncTest.c |
766 | @@ -42,12 +42,14 @@ static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) { |
767 | static void fl_setup(dbChannel *chan, db_field_log *pfl, long val) { |
768 | struct dbCommon *prec = dbChannelRecord(chan); |
769 | |
770 | + memset(pfl, 0, sizeof(db_field_log)); |
771 | pfl->ctx = dbfl_context_event; |
772 | pfl->type = dbfl_type_val; |
773 | pfl->stat = prec->stat; |
774 | pfl->sevr = prec->sevr; |
775 | pfl->time = prec->time; |
776 | pfl->field_type = DBF_LONG; |
777 | + pfl->field_size = sizeof(epicsInt32); |
778 | pfl->no_elements = 1; |
779 | /* |
780 | * use memcpy to avoid a bus error on |
781 | @@ -66,31 +68,92 @@ static void testHead (char* title) { |
782 | testDiag("--------------------------------------------------------"); |
783 | } |
784 | |
785 | -static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m) { |
786 | - db_field_log *pfl = dbChannelRunPreChain(pch, pfl2); |
787 | - testOk(NULL == pfl, "filter drops field_log (%s)", m); |
788 | +/* |
789 | + * Use mustDrop() and mustPass() to test filters with no memory |
790 | + * of previous field_log pointers. |
791 | + */ |
792 | +static void mustDrop(dbChannel *pch, db_field_log *pfl, char* m) { |
793 | + int oldFree = db_available_logs(); |
794 | + db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); |
795 | + int newFree = db_available_logs(); |
796 | + |
797 | + testOk(NULL == pfl2, "filter drops field_log (%s)", m); |
798 | + testOk(newFree == oldFree + 1, "a field_log was freed - %d+1 => %d", |
799 | + oldFree, newFree); |
800 | + |
801 | + db_delete_field_log(pfl2); |
802 | } |
803 | |
804 | -static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m) { |
805 | - db_field_log *pfl; |
806 | +static void mustPass(dbChannel *pch, db_field_log *pfl, char* m) { |
807 | + int oldFree = db_available_logs(); |
808 | + db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); |
809 | + int newFree = db_available_logs(); |
810 | |
811 | - testDiag("%s: filter must pass twice", m); |
812 | - pfl = dbChannelRunPreChain(pch, pfl2); |
813 | - testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); |
814 | - pfl = dbChannelRunPreChain(pch, pfl2); |
815 | - testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); |
816 | + testOk(pfl == pfl2, "filter passes field_log (%s)", m); |
817 | + testOk(newFree == oldFree, "no field_logs were freed - %d => %d", |
818 | + oldFree, newFree); |
819 | + |
820 | + db_delete_field_log(pfl2); |
821 | } |
822 | |
823 | -static void mustPassOld(dbChannel *pch, db_field_log *old, db_field_log *cur, char* m) { |
824 | - db_field_log *pfl = dbChannelRunPreChain(pch, cur); |
825 | +/* |
826 | + * Use mustStash() and mustSwap() to test filters that save |
827 | + * field_log pointers and return them later. |
828 | + * |
829 | + * mustStash() expects the filter to save the current pointer |
830 | + * (freeing any previously saved pointer) and return NULL. |
831 | + * mustSwap() expects the filter to return the previously |
832 | + * saved pointer and save the current pointer. |
833 | + */ |
834 | +static db_field_log *stashed; |
835 | |
836 | - testOk(old == pfl, "filter passes previous field log (%s)", m); |
837 | +static void streamReset(void) { |
838 | + stashed = NULL; |
839 | } |
840 | |
841 | -static void mustPass(dbChannel *pch, db_field_log *cur, char* m) { |
842 | - db_field_log *pfl = dbChannelRunPreChain(pch, cur); |
843 | +static void mustStash(dbChannel *pch, db_field_log *pfl, char* m) { |
844 | + int oldFree = db_available_logs(); |
845 | + db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); |
846 | + int newFree = db_available_logs(); |
847 | |
848 | - testOk(cur == pfl, "filter passes field_log (%s)", m); |
849 | + testOk(NULL == pfl2, "filter stashes field_log (%s)", m); |
850 | + if (stashed) { |
851 | + testOk(newFree == oldFree + 1, "a field_log was freed - %d+1 => %d", |
852 | + oldFree, newFree); |
853 | + } |
854 | + else { |
855 | + testOk(newFree == oldFree, "no field_logs were freed - %d => %d", |
856 | + oldFree, newFree); |
857 | + } |
858 | + stashed = pfl; |
859 | + db_delete_field_log(pfl2); |
860 | +} |
861 | + |
862 | +static void mustSwap(dbChannel *pch, db_field_log *pfl, char* m) { |
863 | + int oldFree = db_available_logs(); |
864 | + db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl); |
865 | + int newFree = db_available_logs(); |
866 | + |
867 | + testOk(stashed == pfl2, "filter returns stashed field log (%s)", m); |
868 | + testOk(newFree == oldFree, "no field_logs were freed - %d => %d", |
869 | + oldFree, newFree); |
870 | + |
871 | + stashed = pfl; |
872 | + db_delete_field_log(pfl2); |
873 | +} |
874 | + |
875 | +static void mustPassTwice(dbChannel *pch, db_field_log *pfl, char* m) { |
876 | + int oldFree = db_available_logs(), newFree; |
877 | + db_field_log *pfl2; |
878 | + |
879 | + testDiag("%s: filter must pass twice", m); |
880 | + pfl2 = dbChannelRunPreChain(pch, pfl); |
881 | + testOk(pfl2 == pfl, "call 1 does not drop or replace field_log"); |
882 | + pfl2 = dbChannelRunPreChain(pch, pfl); |
883 | + testOk(pfl2 == pfl, "call 2 does not drop or replace field_log"); |
884 | + newFree = db_available_logs(); |
885 | + testOk(newFree == oldFree, "no field_logs were freed - %d => %d", |
886 | + oldFree, newFree); |
887 | } |
888 | |
889 | static void checkCtxRead(dbChannel *pch, dbStateId id) { |
890 | @@ -138,10 +201,10 @@ MAIN(syncTest) |
891 | const chFilterPlugin *plug; |
892 | char myname[] = "sync"; |
893 | db_field_log *pfl[10]; |
894 | - int i; |
895 | + int i, logsFree, logsFinal; |
896 | dbEventCtx evtctx; |
897 | |
898 | - testPlan(139); |
899 | + testPlan(214); |
900 | |
901 | testdbPrepare(); |
902 | |
903 | @@ -176,9 +239,14 @@ MAIN(syncTest) |
904 | testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"red\"}}")), |
905 | "dbChannel with plugin sync (m='while' s='red') created"); |
906 | |
907 | + /* Start the free-list */ |
908 | + db_delete_field_log(db_create_read_log(pch)); |
909 | + logsFree = db_available_logs(); |
910 | + testDiag("%d field_logs on free-list", logsFree); |
911 | + |
912 | checkAndOpenChannel(pch, plug); |
913 | |
914 | - for (i = 0; i < 10; i++) { |
915 | + for (i = 0; i < 9; i++) { |
916 | pfl[i] = db_create_read_log(pch); |
917 | fl_setup(pch, pfl[i], 120 + i); |
918 | } |
919 | @@ -198,11 +266,10 @@ MAIN(syncTest) |
920 | mustDrop(pch, pfl[7], "state=FALSE, log7"); |
921 | mustDrop(pch, pfl[8], "state=FALSE, log8"); |
922 | |
923 | - for (i = 0; i < 10; i++) |
924 | - db_delete_field_log(pfl[i]); |
925 | - |
926 | dbChannelDelete(pch); |
927 | |
928 | + testDiag("%d field_logs on free-list", db_available_logs()); |
929 | + |
930 | /* mode UNLESS */ |
931 | |
932 | testHead("Mode UNLESS (m='unless', s='red')"); |
933 | @@ -211,7 +278,7 @@ MAIN(syncTest) |
934 | |
935 | checkAndOpenChannel(pch, plug); |
936 | |
937 | - for (i = 0; i < 10; i++) { |
938 | + for (i = 0; i < 9; i++) { |
939 | pfl[i] = db_create_read_log(pch); |
940 | fl_setup(pch, pfl[i], 120 + i); |
941 | } |
942 | @@ -231,11 +298,10 @@ MAIN(syncTest) |
943 | mustPass(pch, pfl[7], "state=FALSE, log7"); |
944 | mustPass(pch, pfl[8], "state=FALSE, log8"); |
945 | |
946 | - for (i = 0; i < 10; i++) |
947 | - db_delete_field_log(pfl[i]); |
948 | - |
949 | dbChannelDelete(pch); |
950 | |
951 | + testDiag("%d field_logs on free-list", db_available_logs()); |
952 | + |
953 | /* mode BEFORE */ |
954 | |
955 | testHead("Mode BEFORE (m='before', s='red')"); |
956 | @@ -251,24 +317,25 @@ MAIN(syncTest) |
957 | |
958 | testDiag("Test event stream"); |
959 | |
960 | + streamReset(); |
961 | dbStateClear(red); |
962 | - mustDrop(pch, pfl[0], "state=FALSE, log0"); |
963 | - mustDrop(pch, pfl[1], "state=FALSE, log1"); |
964 | - mustDrop(pch, pfl[2], "state=FALSE, log2"); |
965 | + mustStash(pch, pfl[0], "state=FALSE, log0"); |
966 | + mustStash(pch, pfl[1], "state=FALSE, log1"); |
967 | + mustStash(pch, pfl[2], "state=FALSE, log2"); |
968 | dbStateSet(red); |
969 | - mustPassOld(pch, pfl[2], pfl[3], "state=TRUE, log3, pass=log2"); |
970 | - mustDrop(pch, pfl[4], "state=TRUE, log4"); |
971 | - mustDrop(pch, pfl[5], "state=TRUE, log5"); |
972 | - mustDrop(pch, pfl[6], "state=TRUE, log6"); |
973 | + mustSwap(pch, pfl[3], "state=TRUE, log3"); |
974 | + mustStash(pch, pfl[4], "state=TRUE, log4"); |
975 | + mustStash(pch, pfl[5], "state=TRUE, log5"); |
976 | + mustStash(pch, pfl[6], "state=TRUE, log6"); |
977 | dbStateClear(red); |
978 | - mustDrop(pch, pfl[7], "state=FALSE, log7"); |
979 | - mustDrop(pch, pfl[8], "state=FALSE, log8"); |
980 | - mustDrop(pch, pfl[9], "state=FALSE, log9"); |
981 | - |
982 | - db_delete_field_log(pfl[2]); |
983 | + mustStash(pch, pfl[7], "state=FALSE, log7"); |
984 | + mustStash(pch, pfl[8], "state=FALSE, log8"); |
985 | + mustStash(pch, pfl[9], "state=FALSE, log9"); |
986 | |
987 | dbChannelDelete(pch); |
988 | |
989 | + testDiag("%d field_logs on free-list", db_available_logs()); |
990 | + |
991 | /* mode FIRST */ |
992 | |
993 | testHead("Mode FIRST (m='first', s='red')"); |
994 | @@ -277,13 +344,14 @@ MAIN(syncTest) |
995 | |
996 | checkAndOpenChannel(pch, plug); |
997 | |
998 | - for (i = 0; i < 10; i++) { |
999 | + for (i = 0; i < 9; i++) { |
1000 | pfl[i] = db_create_read_log(pch); |
1001 | fl_setup(pch, pfl[i], 120 + i); |
1002 | } |
1003 | |
1004 | testDiag("Test event stream"); |
1005 | |
1006 | + streamReset(); |
1007 | dbStateClear(red); |
1008 | mustDrop(pch, pfl[0], "state=FALSE, log0"); |
1009 | mustDrop(pch, pfl[1], "state=FALSE, log1"); |
1010 | @@ -297,11 +365,10 @@ MAIN(syncTest) |
1011 | mustDrop(pch, pfl[7], "state=FALSE, log7"); |
1012 | mustDrop(pch, pfl[8], "state=FALSE, log8"); |
1013 | |
1014 | - db_delete_field_log(pfl[3]); |
1015 | - db_delete_field_log(pfl[9]); |
1016 | - |
1017 | dbChannelDelete(pch); |
1018 | |
1019 | + testDiag("%d field_logs on free-list", db_available_logs()); |
1020 | + |
1021 | /* mode LAST */ |
1022 | |
1023 | testHead("Mode LAST (m='last', s='red')"); |
1024 | @@ -317,24 +384,25 @@ MAIN(syncTest) |
1025 | |
1026 | testDiag("Test event stream"); |
1027 | |
1028 | + streamReset(); |
1029 | dbStateClear(red); |
1030 | - mustDrop(pch, pfl[0], "state=FALSE, log0"); |
1031 | - mustDrop(pch, pfl[1], "state=FALSE, log1"); |
1032 | - mustDrop(pch, pfl[2], "state=FALSE, log2"); |
1033 | + mustStash(pch, pfl[0], "state=FALSE, log0"); |
1034 | + mustStash(pch, pfl[1], "state=FALSE, log1"); |
1035 | + mustStash(pch, pfl[2], "state=FALSE, log2"); |
1036 | dbStateSet(red); |
1037 | - mustDrop(pch, pfl[3], "state=TRUE, log3"); |
1038 | - mustDrop(pch, pfl[4], "state=TRUE, log4"); |
1039 | - mustDrop(pch, pfl[5], "state=TRUE, log5"); |
1040 | + mustStash(pch, pfl[3], "state=TRUE, log3"); |
1041 | + mustStash(pch, pfl[4], "state=TRUE, log4"); |
1042 | + mustStash(pch, pfl[5], "state=TRUE, log5"); |
1043 | dbStateClear(red); |
1044 | - mustPassOld(pch, pfl[5], pfl[6], "state=TRUE, log6, pass=log5"); |
1045 | - mustDrop(pch, pfl[7], "state=FALSE, log7"); |
1046 | - mustDrop(pch, pfl[8], "state=FALSE, log8"); |
1047 | - mustDrop(pch, pfl[9], "state=FALSE, log9"); |
1048 | - |
1049 | - db_delete_field_log(pfl[5]); |
1050 | + mustSwap(pch, pfl[6], "state=TRUE, log6"); |
1051 | + mustStash(pch, pfl[7], "state=FALSE, log7"); |
1052 | + mustStash(pch, pfl[8], "state=FALSE, log8"); |
1053 | + mustStash(pch, pfl[9], "state=FALSE, log9"); |
1054 | |
1055 | dbChannelDelete(pch); |
1056 | |
1057 | + testDiag("%d field_logs on free-list", db_available_logs()); |
1058 | + |
1059 | /* mode AFTER */ |
1060 | |
1061 | testHead("Mode AFTER (m='after', s='red')"); |
1062 | @@ -343,13 +411,14 @@ MAIN(syncTest) |
1063 | |
1064 | checkAndOpenChannel(pch, plug); |
1065 | |
1066 | - for (i = 0; i < 10; i++) { |
1067 | + for (i = 0; i < 9; i++) { |
1068 | pfl[i] = db_create_read_log(pch); |
1069 | fl_setup(pch, pfl[i], 120 + i); |
1070 | } |
1071 | |
1072 | testDiag("Test event stream"); |
1073 | |
1074 | + streamReset(); |
1075 | dbStateClear(red); |
1076 | mustDrop(pch, pfl[0], "state=FALSE, log0"); |
1077 | mustDrop(pch, pfl[1], "state=FALSE, log1"); |
1078 | @@ -363,11 +432,11 @@ MAIN(syncTest) |
1079 | mustDrop(pch, pfl[7], "state=FALSE, log7"); |
1080 | mustDrop(pch, pfl[8], "state=FALSE, log8"); |
1081 | |
1082 | - db_delete_field_log(pfl[6]); |
1083 | - db_delete_field_log(pfl[9]); |
1084 | - |
1085 | dbChannelDelete(pch); |
1086 | |
1087 | + logsFinal = db_available_logs(); |
1088 | + testOk(logsFree == logsFinal, "%d field_logs on free-list", logsFinal); |
1089 | + |
1090 | db_close_events(evtctx); |
1091 | |
1092 | testIocShutdownOk(); |
I think the dropped db_field_log are being leaked. See comments inline.