%description: Stress test for the FES data structure, with special regard to the optimization for zero-delay events (circbuf). %file: test.ned simple Test { @isNetwork(true); } %file: test.cc #include #include #include using namespace omnetpp; namespace @TESTNAME@ { class Test : public cSimpleModule { protected: cEventHeap *fes; // the real FES std::vector shadowFes; simtime_t lastEventTime = -1; public: virtual void initialize() override; virtual void handleMessage(cMessage *msg) override; virtual void scheduleAt(simtime_t t, cMessage *msg) override; virtual cMessage *cancelEvent(cMessage *msg) override; void compareFes(); void dumpFes(); }; Define_Module(Test); void Test::initialize() { fes = check_and_cast(getSimulation()->getFES()); scheduleAt(simTime(), new cMessage()); } void Test::handleMessage(cMessage *msg) { if (getSimulation()->getEventNumber() > 100000) endSimulation(); EV << "processing " << msg->getName() << endl; if (shadowFes.empty() || shadowFes.front() != msg) throw cRuntimeError("Wrong message delivered"); if (msg->getArrivalTime() < lastEventTime) // note: the same does not work for priority, because it's possible to schedule an event for the current simtime with a smaller priority than the current event throw cRuntimeError("Out-of-order message delivered"); lastEventTime = msg->getArrivalTime(); delete msg; shadowFes.erase(shadowFes.begin()); compareFes(); // cancel a random msg if (!fes->isEmpty() && dblrand() < 0.1) { int k = intrand(fes->getLength()); //fes.sort(); -- add this when viewing in Qtenv, to make Cmdenv and Qtenv are consistent (Qtenv inspectors also sort!) delete cancelEvent(check_and_cast(fes->get(k))); } // schedule a random number of messages int n = fes->isEmpty() ? intuniform(1,3) : fes->getLength() < 20 ? intuniform(0,2) : 0; for (int i = 0; i < n; i++) { simtime_t t = dblrand() < 0.7 ? simTime() : simTime() + intuniform(1,3); // t=now is typical in real workloads int prio = dblrand() < 0.7 ? 0 : intuniform(-2,2); // prio=0 is typical in real workloads char name[100]; sprintf(name, "msg t=%s prio=%d cause=#%d", t.str().c_str(), prio, (int)getSimulation()->getEventNumber()); cMessage *msg = new cMessage(name); msg->setSchedulingPriority(prio); scheduleAt(t, msg); } } void Test::scheduleAt(simtime_t t, cMessage *msg) { EV << "scheduling " << msg->getName() << endl; cSimpleModule::scheduleAt(t, msg); shadowFes.push_back(msg); std::sort(shadowFes.begin(), shadowFes.end(), [] (const cMessage *a, const cMessage *b) {return a->shouldPrecede(b);}); compareFes(); } cMessage *Test::cancelEvent(cMessage *msg) { EV << "cancelling " << msg->getName() << endl; cSimpleModule::cancelEvent(msg); auto it = std::find(shadowFes.begin(), shadowFes.end(), msg); if (it != shadowFes.end()) shadowFes.erase(it); compareFes(); return msg; } void Test::compareFes() { fes->sort(); int n = fes->getLength(); ASSERT((int)shadowFes.size() == n); for (int i = 0; i < n; i++) { if (fes->get(i) != shadowFes[i]) { dumpFes(); throw cRuntimeError("Inconsistency!"); } } } void Test::dumpFes() { fes->sort(); int n = fes->getLength(); ASSERT((int)shadowFes.size() == n); EV << "FES\t\t\t\t\tshadow FES\n"; for (int i = 0; i < n; i++) { cMessage *fesMsg = check_and_cast(fes->get(i)); cMessage *shadowMsg = shadowFes[i]; EV << fesMsg->getName() << " insOrder=" << fesMsg->getInsertOrder() << "\t\t" << shadowMsg->getName() << " insOrder=" << shadowMsg->getInsertOrder(); if (fesMsg != shadowMsg) EV << " <------- MISMATCH"; EV << endl; } } }; //namespace