Fix leak in sync filter (while, unless modes)
Always release field logs when we drop them. Adjust how first and after modes work to make them easier to test. Change stream checking code, fix leaks and double frees. Add mustStash(), mustSwap(), streamReset(), drop mustPassOld(). Modify test code to check free-list count and release all of the field-logs returned by the filter; it must release any of the field-logs that it decides to drop.
This commit is contained in:
@@ -109,7 +109,9 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
|
||||
passfl = pfl;
|
||||
pfl = NULL;
|
||||
}
|
||||
break;
|
||||
else
|
||||
db_delete_field_log(pfl);
|
||||
goto save_state;
|
||||
case syncModeLast:
|
||||
if (!actstate && my->laststate) {
|
||||
passfl = my->lastfl;
|
||||
@@ -121,28 +123,34 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
|
||||
passfl = pfl;
|
||||
pfl = NULL;
|
||||
}
|
||||
break;
|
||||
else
|
||||
db_delete_field_log(pfl);
|
||||
goto save_state;
|
||||
case syncModeWhile:
|
||||
if (actstate) {
|
||||
if (actstate)
|
||||
passfl = pfl;
|
||||
}
|
||||
else
|
||||
db_delete_field_log(pfl);
|
||||
goto no_shift;
|
||||
case syncModeUnless:
|
||||
if (!actstate) {
|
||||
if (!actstate)
|
||||
passfl = pfl;
|
||||
}
|
||||
else
|
||||
db_delete_field_log(pfl);
|
||||
goto no_shift;
|
||||
}
|
||||
|
||||
if (my->lastfl)
|
||||
db_delete_field_log(my->lastfl);
|
||||
my->lastfl = pfl;
|
||||
my->laststate = actstate;
|
||||
|
||||
/* since no copy is made we can't keep a reference to the returned fl */
|
||||
assert(my->lastfl != passfl);
|
||||
|
||||
no_shift:
|
||||
save_state:
|
||||
my->laststate = actstate;
|
||||
|
||||
no_shift:
|
||||
return passfl;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,31 +66,92 @@ static void testHead (char* title) {
|
||||
testDiag("--------------------------------------------------------");
|
||||
}
|
||||
|
||||
static void mustDrop(dbChannel *pch, db_field_log *pfl2, char* m) {
|
||||
db_field_log *pfl = dbChannelRunPreChain(pch, pfl2);
|
||||
testOk(NULL == pfl, "filter drops field_log (%s)", m);
|
||||
/*
|
||||
* Use mustDrop() and mustPass() to test filters with no memory
|
||||
* of previous field_log pointers.
|
||||
*/
|
||||
static void mustDrop(dbChannel *pch, db_field_log *pfl, char* m) {
|
||||
int oldFree = db_available_logs();
|
||||
db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
|
||||
int newFree = db_available_logs();
|
||||
|
||||
testOk(NULL == pfl2, "filter drops field_log (%s)", m);
|
||||
testOk(newFree == oldFree + 1, "a field_log was freed - %d+1 => %d",
|
||||
oldFree, newFree);
|
||||
|
||||
db_delete_field_log(pfl2);
|
||||
}
|
||||
|
||||
static void mustPassTwice(dbChannel *pch, db_field_log *pfl2, char* m) {
|
||||
db_field_log *pfl;
|
||||
static void mustPass(dbChannel *pch, db_field_log *pfl, char* m) {
|
||||
int oldFree = db_available_logs();
|
||||
db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
|
||||
int newFree = db_available_logs();
|
||||
|
||||
testOk(pfl == pfl2, "filter passes field_log (%s)", m);
|
||||
testOk(newFree == oldFree, "no field_logs were freed - %d => %d",
|
||||
oldFree, newFree);
|
||||
|
||||
db_delete_field_log(pfl2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use mustStash() and mustSwap() to test filters that save
|
||||
* field_log pointers and return them later.
|
||||
*
|
||||
* mustStash() expects the filter to save the current pointer
|
||||
* (freeing any previously saved pointer) and return NULL.
|
||||
* mustSwap() expects the filter to return the previously
|
||||
* saved pointer and save the current pointer.
|
||||
*/
|
||||
static db_field_log *stashed;
|
||||
|
||||
static void streamReset(void) {
|
||||
stashed = NULL;
|
||||
}
|
||||
|
||||
static void mustStash(dbChannel *pch, db_field_log *pfl, char* m) {
|
||||
int oldFree = db_available_logs();
|
||||
db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
|
||||
int newFree = db_available_logs();
|
||||
|
||||
testOk(NULL == pfl2, "filter stashes field_log (%s)", m);
|
||||
if (stashed) {
|
||||
testOk(newFree == oldFree + 1, "a field_log was freed - %d+1 => %d",
|
||||
oldFree, newFree);
|
||||
}
|
||||
else {
|
||||
testOk(newFree == oldFree, "no field_logs were freed - %d => %d",
|
||||
oldFree, newFree);
|
||||
}
|
||||
stashed = pfl;
|
||||
db_delete_field_log(pfl2);
|
||||
}
|
||||
|
||||
static void mustSwap(dbChannel *pch, db_field_log *pfl, char* m) {
|
||||
int oldFree = db_available_logs();
|
||||
db_field_log *pfl2 = dbChannelRunPreChain(pch, pfl);
|
||||
int newFree = db_available_logs();
|
||||
|
||||
testOk(stashed == pfl2, "filter returns stashed field log (%s)", m);
|
||||
testOk(newFree == oldFree, "no field_logs were freed - %d => %d",
|
||||
oldFree, newFree);
|
||||
|
||||
stashed = pfl;
|
||||
db_delete_field_log(pfl2);
|
||||
}
|
||||
|
||||
static void mustPassTwice(dbChannel *pch, db_field_log *pfl, char* m) {
|
||||
int oldFree = db_available_logs(), newFree;
|
||||
db_field_log *pfl2;
|
||||
|
||||
testDiag("%s: filter must pass twice", m);
|
||||
pfl = dbChannelRunPreChain(pch, pfl2);
|
||||
pfl2 = dbChannelRunPreChain(pch, pfl);
|
||||
testOk(pfl2 == pfl, "call 1 does not drop or replace field_log");
|
||||
pfl = dbChannelRunPreChain(pch, pfl2);
|
||||
pfl2 = dbChannelRunPreChain(pch, pfl);
|
||||
testOk(pfl2 == pfl, "call 2 does not drop or replace field_log");
|
||||
}
|
||||
|
||||
static void mustPassOld(dbChannel *pch, db_field_log *old, db_field_log *cur, char* m) {
|
||||
db_field_log *pfl = dbChannelRunPreChain(pch, cur);
|
||||
|
||||
testOk(old == pfl, "filter passes previous field log (%s)", m);
|
||||
}
|
||||
|
||||
static void mustPass(dbChannel *pch, db_field_log *cur, char* m) {
|
||||
db_field_log *pfl = dbChannelRunPreChain(pch, cur);
|
||||
|
||||
testOk(cur == pfl, "filter passes field_log (%s)", m);
|
||||
newFree = db_available_logs();
|
||||
testOk(newFree == oldFree, "no field_logs were freed - %d => %d",
|
||||
oldFree, newFree);
|
||||
}
|
||||
|
||||
static void checkCtxRead(dbChannel *pch, dbStateId id) {
|
||||
@@ -138,10 +199,10 @@ MAIN(syncTest)
|
||||
const chFilterPlugin *plug;
|
||||
char myname[] = "sync";
|
||||
db_field_log *pfl[10];
|
||||
int i;
|
||||
int i, logsFree, logsFinal;
|
||||
dbEventCtx evtctx;
|
||||
|
||||
testPlan(139);
|
||||
testPlan(214);
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
@@ -176,9 +237,14 @@ MAIN(syncTest)
|
||||
testOk(!!(pch = dbChannelCreate("x.VAL{\"sync\":{\"m\":\"while\",\"s\":\"red\"}}")),
|
||||
"dbChannel with plugin sync (m='while' s='red') created");
|
||||
|
||||
/* Start the free-list */
|
||||
db_delete_field_log(db_create_read_log(pch));
|
||||
logsFree = db_available_logs();
|
||||
testDiag("%d field_logs on free-list", logsFree);
|
||||
|
||||
checkAndOpenChannel(pch, plug);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
for (i = 0; i < 9; i++) {
|
||||
pfl[i] = db_create_read_log(pch);
|
||||
fl_setup(pch, pfl[i], 120 + i);
|
||||
}
|
||||
@@ -198,11 +264,10 @@ MAIN(syncTest)
|
||||
mustDrop(pch, pfl[7], "state=FALSE, log7");
|
||||
mustDrop(pch, pfl[8], "state=FALSE, log8");
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
db_delete_field_log(pfl[i]);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
testDiag("%d field_logs on free-list", db_available_logs());
|
||||
|
||||
/* mode UNLESS */
|
||||
|
||||
testHead("Mode UNLESS (m='unless', s='red')");
|
||||
@@ -211,7 +276,7 @@ MAIN(syncTest)
|
||||
|
||||
checkAndOpenChannel(pch, plug);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
for (i = 0; i < 9; i++) {
|
||||
pfl[i] = db_create_read_log(pch);
|
||||
fl_setup(pch, pfl[i], 120 + i);
|
||||
}
|
||||
@@ -231,11 +296,10 @@ MAIN(syncTest)
|
||||
mustPass(pch, pfl[7], "state=FALSE, log7");
|
||||
mustPass(pch, pfl[8], "state=FALSE, log8");
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
db_delete_field_log(pfl[i]);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
testDiag("%d field_logs on free-list", db_available_logs());
|
||||
|
||||
/* mode BEFORE */
|
||||
|
||||
testHead("Mode BEFORE (m='before', s='red')");
|
||||
@@ -251,24 +315,25 @@ MAIN(syncTest)
|
||||
|
||||
testDiag("Test event stream");
|
||||
|
||||
streamReset();
|
||||
dbStateClear(red);
|
||||
mustDrop(pch, pfl[0], "state=FALSE, log0");
|
||||
mustDrop(pch, pfl[1], "state=FALSE, log1");
|
||||
mustDrop(pch, pfl[2], "state=FALSE, log2");
|
||||
mustStash(pch, pfl[0], "state=FALSE, log0");
|
||||
mustStash(pch, pfl[1], "state=FALSE, log1");
|
||||
mustStash(pch, pfl[2], "state=FALSE, log2");
|
||||
dbStateSet(red);
|
||||
mustPassOld(pch, pfl[2], pfl[3], "state=TRUE, log3, pass=log2");
|
||||
mustDrop(pch, pfl[4], "state=TRUE, log4");
|
||||
mustDrop(pch, pfl[5], "state=TRUE, log5");
|
||||
mustDrop(pch, pfl[6], "state=TRUE, log6");
|
||||
mustSwap(pch, pfl[3], "state=TRUE, log3");
|
||||
mustStash(pch, pfl[4], "state=TRUE, log4");
|
||||
mustStash(pch, pfl[5], "state=TRUE, log5");
|
||||
mustStash(pch, pfl[6], "state=TRUE, log6");
|
||||
dbStateClear(red);
|
||||
mustDrop(pch, pfl[7], "state=FALSE, log7");
|
||||
mustDrop(pch, pfl[8], "state=FALSE, log8");
|
||||
mustDrop(pch, pfl[9], "state=FALSE, log9");
|
||||
|
||||
db_delete_field_log(pfl[2]);
|
||||
mustStash(pch, pfl[7], "state=FALSE, log7");
|
||||
mustStash(pch, pfl[8], "state=FALSE, log8");
|
||||
mustStash(pch, pfl[9], "state=FALSE, log9");
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
testDiag("%d field_logs on free-list", db_available_logs());
|
||||
|
||||
/* mode FIRST */
|
||||
|
||||
testHead("Mode FIRST (m='first', s='red')");
|
||||
@@ -277,13 +342,14 @@ MAIN(syncTest)
|
||||
|
||||
checkAndOpenChannel(pch, plug);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
for (i = 0; i < 9; i++) {
|
||||
pfl[i] = db_create_read_log(pch);
|
||||
fl_setup(pch, pfl[i], 120 + i);
|
||||
}
|
||||
|
||||
testDiag("Test event stream");
|
||||
|
||||
streamReset();
|
||||
dbStateClear(red);
|
||||
mustDrop(pch, pfl[0], "state=FALSE, log0");
|
||||
mustDrop(pch, pfl[1], "state=FALSE, log1");
|
||||
@@ -297,11 +363,10 @@ MAIN(syncTest)
|
||||
mustDrop(pch, pfl[7], "state=FALSE, log7");
|
||||
mustDrop(pch, pfl[8], "state=FALSE, log8");
|
||||
|
||||
db_delete_field_log(pfl[3]);
|
||||
db_delete_field_log(pfl[9]);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
testDiag("%d field_logs on free-list", db_available_logs());
|
||||
|
||||
/* mode LAST */
|
||||
|
||||
testHead("Mode LAST (m='last', s='red')");
|
||||
@@ -317,24 +382,25 @@ MAIN(syncTest)
|
||||
|
||||
testDiag("Test event stream");
|
||||
|
||||
streamReset();
|
||||
dbStateClear(red);
|
||||
mustDrop(pch, pfl[0], "state=FALSE, log0");
|
||||
mustDrop(pch, pfl[1], "state=FALSE, log1");
|
||||
mustDrop(pch, pfl[2], "state=FALSE, log2");
|
||||
mustStash(pch, pfl[0], "state=FALSE, log0");
|
||||
mustStash(pch, pfl[1], "state=FALSE, log1");
|
||||
mustStash(pch, pfl[2], "state=FALSE, log2");
|
||||
dbStateSet(red);
|
||||
mustDrop(pch, pfl[3], "state=TRUE, log3");
|
||||
mustDrop(pch, pfl[4], "state=TRUE, log4");
|
||||
mustDrop(pch, pfl[5], "state=TRUE, log5");
|
||||
mustStash(pch, pfl[3], "state=TRUE, log3");
|
||||
mustStash(pch, pfl[4], "state=TRUE, log4");
|
||||
mustStash(pch, pfl[5], "state=TRUE, log5");
|
||||
dbStateClear(red);
|
||||
mustPassOld(pch, pfl[5], pfl[6], "state=TRUE, log6, pass=log5");
|
||||
mustDrop(pch, pfl[7], "state=FALSE, log7");
|
||||
mustDrop(pch, pfl[8], "state=FALSE, log8");
|
||||
mustDrop(pch, pfl[9], "state=FALSE, log9");
|
||||
|
||||
db_delete_field_log(pfl[5]);
|
||||
mustSwap(pch, pfl[6], "state=TRUE, log6");
|
||||
mustStash(pch, pfl[7], "state=FALSE, log7");
|
||||
mustStash(pch, pfl[8], "state=FALSE, log8");
|
||||
mustStash(pch, pfl[9], "state=FALSE, log9");
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
testDiag("%d field_logs on free-list", db_available_logs());
|
||||
|
||||
/* mode AFTER */
|
||||
|
||||
testHead("Mode AFTER (m='after', s='red')");
|
||||
@@ -343,13 +409,14 @@ MAIN(syncTest)
|
||||
|
||||
checkAndOpenChannel(pch, plug);
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
for (i = 0; i < 9; i++) {
|
||||
pfl[i] = db_create_read_log(pch);
|
||||
fl_setup(pch, pfl[i], 120 + i);
|
||||
}
|
||||
|
||||
testDiag("Test event stream");
|
||||
|
||||
streamReset();
|
||||
dbStateClear(red);
|
||||
mustDrop(pch, pfl[0], "state=FALSE, log0");
|
||||
mustDrop(pch, pfl[1], "state=FALSE, log1");
|
||||
@@ -363,11 +430,11 @@ MAIN(syncTest)
|
||||
mustDrop(pch, pfl[7], "state=FALSE, log7");
|
||||
mustDrop(pch, pfl[8], "state=FALSE, log8");
|
||||
|
||||
db_delete_field_log(pfl[6]);
|
||||
db_delete_field_log(pfl[9]);
|
||||
|
||||
dbChannelDelete(pch);
|
||||
|
||||
logsFinal = db_available_logs();
|
||||
testOk(logsFree == logsFinal, "%d field_logs on free-list", logsFinal);
|
||||
|
||||
db_close_events(evtctx);
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
Reference in New Issue
Block a user