Copilot commented on code in PR #7135:
URL: https://github.com/apache/incubator-kie/pull/7135#discussion_r4192774136


##########
drools-core/src/main/java/org/drools/core/phreak/SegmentMemorySupportImpl.java:
##########
@@ -213,14 +213,19 @@ public SegmentMemory 
createChildSegmentLazily(LeftTupleNode node) {
     @Override
     public void initializeChildSegmentsIfNeeded(SegmentMemory smem) {
         LeftTupleSinkPropagator sinkPropagator = 
smem.getTipNode().getSinkPropagator();
-        if (!smem.isEmpty()) {
-            return; // this can happen when multiple threads are trying to 
initialize the segment
+        if (!smem.isEmpty() && sinkPropagator.size() == 1) {
+            return; // threading guard: single-sink child already initialized
         }
+        // When the tip node has multiple sinks (segment split by a sharing 
rule), some children
+        // may already have been added to smem while sibling children are 
still missing.
+        // Iterate all sinks, create any missing child segment, and add it 
only if not already present.
         for (LeftTupleSinkNode sink = sinkPropagator.getFirstLeftTupleSink(); 
sink != null; sink = sink
                 .getNextLeftTupleSinkNode()) {
             SegmentMemory childSmem = PhreakBuilder.isEagerSegmentCreation() ? 
createChildSegment(sink)
                     : createChildSegmentLazily(sink);
-            smem.add(childSmem);
+            if (childSmem != null && !smem.contains(childSmem)) {
+                smem.add(childSmem);

Review Comment:
   The new `contains`/`add` pair is not atomic, even though this method 
explicitly supports concurrent initialization. With multiple sinks, two threads 
can both observe the same child as absent and add the same `SegmentMemory` 
twice, corrupting the intrusive linked list. Serialize the multi-sink 
initialization (with the emptiness/contains checks inside the critical section) 
or provide an atomic add-if-absent operation.



##########
drools-core/src/main/java/org/drools/core/reteoo/SingleObjectSinkAdapter.java:
##########
@@ -120,17 +121,36 @@ public void doUnlinkSubnetwork(ReteEvaluator 
reteEvaluator) {
     
     public static void staticDoUnlinkSubnetwork(ObjectSink sink, ReteEvaluator 
reteEvaluator) {
         BetaMemory bm;
+        BetaNode betaNode;
         if ( sink.getType() == NodeTypeEnums.AccumulateRightAdapterNode ) {
             AccumulateNode accnode = ((AccumulateRight)sink).getBetaNode();
             AccumulateMemory accMem = ( AccumulateMemory ) 
reteEvaluator.getNodeMemory( accnode );
             bm = accMem.getBetaMemory();
-        }  else {
-            BetaNode betaNode = ((RightInputAdapterNode) sink).getBetaNode();
+            betaNode = accnode;
+        } else {
+            betaNode = ((RightInputAdapterNode) sink).getBetaNode();
             bm = RightInputAdapterNode.getBetaMemoryFromRightInput(betaNode, 
reteEvaluator);
         }
 
-        if (sink.getType() == NodeTypeEnums.NotNode) {
-            bm.linkNode( ( BetaNode ) sink, reteEvaluator );
+        if (betaNode.getType() == NodeTypeEnums.NotNode) {
+            bm.linkNode(betaNode, reteEvaluator);

Review Comment:
   This recovery path still depends on `SubnetworkPathMemory.doUnlinkRule()` 
being called, but its inherited `PathMemory.unlinkedSegment()` only calls 
`doUnlinkRule()` when the whole path changes from linked to unlinked. If 
another segment has already made the path unlinked, the downstream sinks are 
never notified and this new relinking logic is not reached. The stated 
`TupleToObjectNode.unlinkedSegment` fix is absent from this change, so that 
failure mode remains.



##########
drools-core/src/main/java/org/drools/core/reteoo/SingleObjectSinkAdapter.java:
##########
@@ -120,17 +121,36 @@ public void doUnlinkSubnetwork(ReteEvaluator 
reteEvaluator) {
     
     public static void staticDoUnlinkSubnetwork(ObjectSink sink, ReteEvaluator 
reteEvaluator) {
         BetaMemory bm;
+        BetaNode betaNode;
         if ( sink.getType() == NodeTypeEnums.AccumulateRightAdapterNode ) {
             AccumulateNode accnode = ((AccumulateRight)sink).getBetaNode();
             AccumulateMemory accMem = ( AccumulateMemory ) 
reteEvaluator.getNodeMemory( accnode );
             bm = accMem.getBetaMemory();
-        }  else {
-            BetaNode betaNode = ((RightInputAdapterNode) sink).getBetaNode();
+            betaNode = accnode;
+        } else {
+            betaNode = ((RightInputAdapterNode) sink).getBetaNode();
             bm = RightInputAdapterNode.getBetaMemoryFromRightInput(betaNode, 
reteEvaluator);
         }
 
-        if (sink.getType() == NodeTypeEnums.NotNode) {
-            bm.linkNode( ( BetaNode ) sink, reteEvaluator );
+        if (betaNode.getType() == NodeTypeEnums.NotNode) {
+            bm.linkNode(betaNode, reteEvaluator);
+            // Stage left tuples that have no match records (contextObject == 
null) as INSERT.
+            // insertLeft in PhreakSubnetworkNotExistsNode only creates a 
child when contextObject
+            // is null, so this is a no-op for tuples that were already 
correctly handled by the
+            // normal deleteRight path (which leaves an empty-but-non-null 
TupleList as contextObject).
+            // This rescues cross-package broken-path cases where deleteRight 
never ran for a rule.
+            SegmentMemory smem = bm.getSegmentMemory();
+            if (smem != null) {
+                TupleMemory ltm = bm.getLeftTupleMemory();
+                if (ltm != null && ltm.size() > 0) {
+                    FastIterator<TupleImpl> it = ltm.fullFastIterator();
+                    for (TupleImpl lt = BetaNode.getFirstTuple(ltm, it); lt != 
null; lt = it.next(lt)) {
+                        if (lt.getStagedType() == Tuple.NONE && 
lt.getContextObject() == null) {
+                            smem.getStagedLeftTuples().addInsert(lt);
+                        }

Review Comment:
   These tuples come directly from `bm.getLeftTupleMemory()`, so staging them 
as inserts causes `PhreakSubnetworkNotExistsNode.insertLeft()` to call 
`ltm.add(leftTuple)` on an entry that is already linked in that memory. For an 
unindexed `TupleList`, re-adding the tail links it to itself and increments the 
size; this can corrupt iteration, and it can also create a second child for a 
normal no-match tuple whose `contextObject` is null. Re-evaluate existing 
tuples without re-inserting them into left memory, and distinguish tuples that 
already have a child.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to