Skip to content

Commit 2ad8345

Browse files
committed
queueing: let DynamicClassifier accept a packet before it has any branch
canPushSomePacket() is inherited as "one of the existing branches can take a packet", which is false for a classifier that has not built any branch yet. An active source in front of such a classifier stops, waits for the notification that would tell it packets can be pushed again, and never gets it, because nothing else creates the first branch. Answer true instead: a packet of a class that has not been seen yet is taken by the branch created for it, and the range of the classifier function is not known here, so there may always be such a class. canPushPacket() is worse than useless in its inherited form here: it classifies the packet, and for this classifier classifying creates the branch of a new class, so a query that is supposed to be a query builds submodules, grows gate vectors, wires connections and initializes the new modules. Look the class up instead, and only delegate to the branch that already exists. (The pull side classifies in canPullPacket() too, and is left alone: there the query is what drives branch creation, and this classifier has no pull user.) Classification for the map is taken directly from the classifier function rather than through PacketClassifier::classifyPacket(), whose reverseOrder mapping is relative to the current number of output gates -- which grows with each branch, so the same class would be looked up under a different key later and get a second branch. The module test no longer needs the BackPressureBarrier that hid the missing back pressure answer, so it now covers this as well: without the fix the producer never produces and no branch is built.
1 parent 643a5dd commit 2ad8345

3 files changed

Lines changed: 42 additions & 10 deletions

File tree

src/inet/queueing/classifier/DynamicClassifier.cc

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,19 @@ void DynamicClassifier::initialize(int stage)
3232
}
3333
}
3434

35+
int DynamicClassifier::getClassIndex(Packet *packet) const
36+
{
37+
// the class of the packet, with no side effect -- unlike classifyPacket() below, which
38+
// creates the branch of a class that is seen for the first time. Note that the class index
39+
// is taken as it is, and not mapped through getOutputGateIndex(): that mapping depends on
40+
// the number of output gates, which grows with each branch, so the same class would end up
41+
// under a different key over time, and get a second branch.
42+
return packetClassifierFunction->classifyPacket(packet);
43+
}
44+
3545
int DynamicClassifier::classifyPacket(Packet *packet)
3646
{
37-
int index = PacketClassifier::classifyPacket(packet);
47+
int index = getClassIndex(packet);
3848
auto it = classIndexToGateItMap.find(index);
3949
if (it != classIndexToGateItMap.end())
4050
return it->second;
@@ -43,6 +53,25 @@ int DynamicClassifier::classifyPacket(Packet *packet)
4353
return branchIndex;
4454
}
4555

56+
bool DynamicClassifier::canPushSomePacket(const cGate *gate) const
57+
{
58+
// Not the inherited "one of the existing branches can take a packet": a packet of a class
59+
// that has not been seen yet is taken by the branch created for it, and there may always be
60+
// such a class, the range of the classifier function not being known here. Without this, a
61+
// classifier that has no branch yet answers that it cannot accept anything, and an active
62+
// source in front of it stops before the first branch is ever created. Whether a particular
63+
// packet can be pushed is answered by canPushPacket() below.
64+
return true;
65+
}
66+
67+
bool DynamicClassifier::canPushPacket(Packet *packet, const cGate *gate) const
68+
{
69+
// deliberately not the inherited implementation: that one classifies the packet, which
70+
// creates the branch of a new class as a side effect of what is supposed to be a query
71+
auto it = classIndexToGateItMap.find(getClassIndex(packet));
72+
return it == classIndexToGateItMap.end() || consumers[it->second].canPushPacket(packet);
73+
}
74+
4675
int DynamicClassifier::createBranch()
4776
{
4877
cModule *parent = getParentModule();

src/inet/queueing/classifier/DynamicClassifier.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ class INET_API DynamicClassifier : public PacketClassifier
4747

4848
protected:
4949
virtual void initialize(int stage) override;
50+
virtual int getClassIndex(Packet *packet) const;
5051
virtual int classifyPacket(Packet *packet) override;
5152

5253
virtual int createBranch();
@@ -55,6 +56,10 @@ class INET_API DynamicClassifier : public PacketClassifier
5556
virtual void forwardMatchingParams(cModule *module);
5657
virtual cGate *createModuleBranch(int index, cGate *classifierOutputGate, std::vector<cModule *>& modulesToInitialize);
5758
virtual cGate *spliceBranch(int index, cGate *classifierOutputGate, std::vector<cModule *>& modulesToInitialize);
59+
60+
public:
61+
virtual bool canPushSomePacket(const cGate *gate) const override;
62+
virtual bool canPushPacket(Packet *packet, const cGate *gate) const override;
5863
};
5964

6065
} // namespace queueing

tests/queueing/DynamicClassifier_1.test

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ creates the branch of a class when the first packet of that class arrives. The b
66
(TestBranch) is a linear chain of two packet flow modules (BackPressureBarriers), and it is
77
spliced, so those are created directly in the classifier's parent, as first[k] and second[k].
88

9+
The producer is connected to the classifier directly, so the test also covers that a classifier
10+
which has no branch yet accepts a packet, rather than stopping the producer before the first
11+
branch is created.
12+
913
The test checks that the statistics of the spliced submodules are recorded under their final
1014
module path, and not under the temporary name the branch is built with. Empty output vectors
1115
are turned off, so a vector appears in the result file only if data was recorded into it.
@@ -67,20 +71,14 @@ network TestDynamicClassifier
6771
producer: ActivePacketSource {
6872
@display("p=100,100");
6973
}
70-
// a classifier without any branch reports that it cannot accept a packet, which would
71-
// stop the producer before the first branch is ever created
72-
barrier: BackPressureBarrier {
73-
@display("p=200,100");
74-
}
7574
demultiplexer: TestDemultiplexer {
76-
@display("p=300,100");
75+
@display("p=200,100");
7776
}
7877
consumer: PassivePacketSink {
79-
@display("p=400,100");
78+
@display("p=300,100");
8079
}
8180
connections:
82-
producer.out --> barrier.in;
83-
barrier.out --> demultiplexer.in;
81+
producer.out --> demultiplexer.in;
8482
demultiplexer.out --> consumer.in;
8583
}
8684

0 commit comments

Comments
 (0)