TEST(FunctionScheduler, AddAfterStart) { int total = 0; FunctionScheduler fs; fs.addFunction([&] { total += 2; }, testInterval(2), "add2"); fs.addFunction([&] { total += 3; }, testInterval(2), "add3"); fs.start(); delay(3); EXPECT_EQ(10, total); fs.addFunction([&] { total += 2; }, testInterval(3), "add22"); delay(2); EXPECT_EQ(17, total); }
ExecStreamResult BTreeSearchExecStream::innerFetchLoop( ExecStreamQuantum const &quantum, uint &nTuples) { for (;;) { if (nTuples >= quantum.nTuplesMax) { return EXECRC_QUANTUM_EXPIRED; } if (reachedTupleLimit(nTuples)) { return EXECRC_BUF_OVERFLOW; } if (pOutAccessor->produceTuple(tupleData)) { ++nTuples; } else { return EXECRC_BUF_OVERFLOW; } if (pReader->searchNext()) { if (testInterval()) { projAccessor.unmarshal( tupleData.begin() + nJoinAttributes); // continue with inner fetch loop continue; } } pReader->endSearch(); // break out of this loop to enable a new key search return EXECRC_YIELD; } }
TEST(FunctionScheduler, AddInvalid) { int total = 0; FunctionScheduler fs; // interval may not be negative EXPECT_THROW(fs.addFunction([&] { total += 2; }, testInterval(-1), "add2"), std::exception); EXPECT_FALSE(fs.cancelFunction("addNoFunc")); }
TEST(FunctionScheduler, SimpleAdd) { int total = 0; FunctionScheduler fs; fs.addFunction([&] { total += 2; }, testInterval(2), "add2"); fs.start(); delay(1); EXPECT_EQ(2, total); fs.shutdown(); delay(2); EXPECT_EQ(2, total); }
TEST(FunctionScheduler, AddCancel) { int total = 0; FunctionScheduler fs; fs.addFunction([&] { total += 2; }, testInterval(2), "add2"); fs.start(); delay(1); EXPECT_EQ(2, total); delay(2); EXPECT_EQ(4, total); EXPECT_TRUE(fs.cancelFunction("add2")); EXPECT_FALSE(fs.cancelFunction("NO SUCH FUNC")); delay(2); EXPECT_EQ(4, total); fs.addFunction([&] { total += 1; }, testInterval(2), "add2"); EXPECT_FALSE(fs.start()); // already running delay(1); EXPECT_EQ(5, total); delay(2); EXPECT_EQ(6, total); fs.shutdown(); }
TEST(FunctionScheduler, StartDelay) { int total = 0; FunctionScheduler fs; fs.addFunction([&] { total += 2; }, testInterval(2), "add2", testInterval(2)); fs.addFunction([&] { total += 3; }, testInterval(3), "add3", testInterval(2)); EXPECT_THROW(fs.addFunction([&] { total += 2; }, testInterval(3), "addX", testInterval(-1)), std::exception); fs.start(); delay(1); // t1 EXPECT_EQ(0, total); // t2 : add2 total=2 // t2 : add3 total=5 delay(2); // t3 EXPECT_EQ(5, total); // t4 : add2: total=7 // t5 : add3: total=10 // t6 : add2: total=12 delay(4); // t7 EXPECT_EQ(12, total); fs.cancelFunction("add2"); // t8 : add3: total=15 delay(2); // t9 EXPECT_EQ(15, total); fs.shutdown(); delay(3); EXPECT_EQ(15, total); fs.shutdown(); }
TEST(FunctionScheduler, AddWhileRunning) { int total = 0; FunctionScheduler fs; fs.start(); delay(1); fs.addFunction([&] { total += 2; }, testInterval(2), "add2"); // The function should be invoked nearly immediately when we add it // and the FunctionScheduler is already running usleep(50000); EXPECT_EQ(2, total); delay(2); EXPECT_EQ(4, total); }
TEST(FunctionScheduler, AddMultiple) { int total = 0; FunctionScheduler fs; fs.addFunction([&] { total += 2; }, testInterval(2), "add2"); fs.addFunction([&] { total += 3; }, testInterval(3), "add3"); // function name already exists EXPECT_THROW(fs.addFunction([&] { total += 2; }, testInterval(2), "add2"), std::exception); fs.start(); delay(1); EXPECT_EQ(5, total); delay(4); EXPECT_EQ(12, total); EXPECT_TRUE(fs.cancelFunction("add2")); delay(2); EXPECT_EQ(15, total); fs.shutdown(); delay(3); EXPECT_EQ(15, total); fs.shutdown(); }
TEST(FunctionScheduler, ShutdownStart) { int total = 0; FunctionScheduler fs; fs.addFunction([&] { total += 2; }, testInterval(2), "add2"); fs.start(); delay(1); fs.shutdown(); fs.start(); delay(1); EXPECT_EQ(4, total); EXPECT_FALSE(fs.cancelFunction("add3")); // non existing delay(2); EXPECT_EQ(6, total); }
TEST(FunctionScheduler, NoShutdown) { int total = 0; { FunctionScheduler fs; fs.addFunction([&] { total += 2; }, testInterval(1), "add2"); fs.start(); usleep(50000); EXPECT_EQ(2, total); } // Destroyed the FunctionScheduler without calling shutdown. // Everything should have been cleaned up, and the function will no longer // get called. delay(2); EXPECT_EQ(2, total); }
TEST(FunctionScheduler, AddCancel2) { int total = 0; FunctionScheduler fs; // Test adds and cancels while the scheduler is stopped EXPECT_FALSE(fs.cancelFunction("add2")); fs.addFunction([&] { total += 1; }, testInterval(2), "add2"); EXPECT_TRUE(fs.cancelFunction("add2")); EXPECT_FALSE(fs.cancelFunction("add2")); fs.addFunction([&] { total += 2; }, testInterval(2), "add2"); fs.addFunction([&] { total += 3; }, testInterval(3), "add3"); EXPECT_EQ(0, total); fs.start(); delay(1); EXPECT_EQ(5, total); // Cancel add2 while the scheduler is running EXPECT_TRUE(fs.cancelFunction("add2")); EXPECT_FALSE(fs.cancelFunction("add2")); EXPECT_FALSE(fs.cancelFunction("bogus")); delay(3); EXPECT_EQ(8, total); EXPECT_TRUE(fs.cancelFunction("add3")); // Test a function that cancels itself int selfCancelCount = 0; fs.addFunction( [&] { ++selfCancelCount; if (selfCancelCount > 2) { fs.cancelFunction("selfCancel"); } }, testInterval(1), "selfCancel", testInterval(1)); delay(4); EXPECT_EQ(3, selfCancelCount); EXPECT_FALSE(fs.cancelFunction("selfCancel")); // Test a function that schedules another function int adderCount = 0; int fn2Count = 0; auto fn2 = [&] { ++fn2Count; }; auto fnAdder = [&] { ++adderCount; if (adderCount == 2) { fs.addFunction(fn2, testInterval(3), "fn2", testInterval(2)); } }; fs.addFunction(fnAdder, testInterval(4), "adder"); // t0: adder fires delay(1); // t1 EXPECT_EQ(1, adderCount); EXPECT_EQ(0, fn2Count); // t4: adder fires, schedules fn2 delay(4); // t5 EXPECT_EQ(2, adderCount); EXPECT_EQ(0, fn2Count); // t6: fn2 fires delay(2); // t7 EXPECT_EQ(2, adderCount); EXPECT_EQ(1, fn2Count); // t8: adder fires // t9: fn2 fires delay(3); // t10 EXPECT_EQ(3, adderCount); EXPECT_EQ(2, fn2Count); EXPECT_TRUE(fs.cancelFunction("fn2")); EXPECT_TRUE(fs.cancelFunction("adder")); delay(5); // t10 EXPECT_EQ(3, adderCount); EXPECT_EQ(2, fn2Count); EXPECT_EQ(8, total); EXPECT_EQ(3, selfCancelCount); }
bool BTreeSearchExecStream::searchForKey() { switch (lowerBoundDirective) { case SEARCH_UNBOUNDED_LOWER: if (pSearchKey->size() <= 1) { pReader->searchFirst(); break; } // otherwise, this is the case where we have > 1 key and a // non-equality search on the last key; in this case, we need // to position to the equality portion of the key case SEARCH_CLOSED_LOWER: pReader->searchForKey(*pSearchKey, DUP_SEEK_BEGIN, leastUpper); break; case SEARCH_OPEN_LOWER: pReader->searchForKey(*pSearchKey, DUP_SEEK_END, leastUpper); break; default: permFail( "unexpected lower bound directive: " << (char) lowerBoundDirective); } bool match = true; if (preFilterNulls && pSearchKey->containsNull(searchKeyProj)) { // null never matches when preFilterNulls is true; // TODO: so don't bother searching, but need a way // to fake pReader->isPositioned() match = false; } else { if (pReader->isSingular()) { // Searched past end of tree. match = false; } else { if (preFilterNulls && upperBoundData.containsNull(upperBoundKeyProj)) { match = false; } else { match = testInterval(); } } } if (!match) { if (!outerJoin) { pReader->endSearch(); return false; } // no match, so make up null values for the missing attributes for (uint i = nJoinAttributes; i < tupleData.size(); ++i) { tupleData[i].pData = NULL; } } else { projAccessor.unmarshal( tupleData.begin() + nJoinAttributes); } // propagate join attributes if (inputJoinAccessor.size()) { inputJoinAccessor.unmarshal(tupleData); } return true; }