Skip to content

Commit 3e375ab

Browse files
committed
fix(sort): skip non-hierarchical refs in topological sort
The topological sort in nodeRefsReady() previously blocked on ALL inverse non-HasTypeDefinition references. This caused deadlocks when two nodes in the same nodeset referenced each other via a non-hierarchical reference type (e.g. HasSubStateMachine i=117). Non-hierarchical references do not establish parent-child ordering dependencies. Only inverse hierarchical references (subtypes of HierarchicalReferences i=33) and forward HasTypeDefinition require ordering constraints. Add isHierarchicalRef() to identify well-known ns0 hierarchical reference types and update nodeRefsReady() to only block on inverse hierarchical references. Non-ns0 reference types are conservatively treated as hierarchical. Fixes loading of the Glass companion specification, which uses HasSubStateMachine between StateMachine objects in the same nodeset.
1 parent bcfe420 commit 3e375ab

1 file changed

Lines changed: 39 additions & 3 deletions

File tree

src/Nodeset.c

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,41 @@ Nodeset_findByNodeId(Nodeset *nodeset, const UA_NodeId *key) {
136136

137137
static UA_NodeId hasTypeDef = {0, UA_NODEIDTYPE_NUMERIC, {40}};
138138

139+
/* Check whether a ns0 reference type is hierarchical (subtype of
140+
* HierarchicalReferences i=33). Only inverse hierarchical references
141+
* create ordering dependencies for node creation. Non-hierarchical
142+
* references (e.g. HasSubStateMachine i=117, HasCondition i=9006) do
143+
* not create ordering dependencies and must be ignored. Otherwise
144+
* they can cause deadlocks in the topological sort when two nodes in
145+
* the same nodeset reference each other via a non-hierarchical
146+
* reference type.
147+
*
148+
* For non-ns0 reference types, conservatively assume hierarchical.
149+
* This list covers the well-known hierarchical subtypes from OPC UA
150+
* Part 3 / Part 5. */
151+
static bool
152+
isHierarchicalRef(const UA_NodeId *refType) {
153+
if(refType->namespaceIndex != 0 ||
154+
refType->identifierType != UA_NODEIDTYPE_NUMERIC)
155+
return true; /* Unknown: conservatively treat as hierarchical */
156+
switch(refType->identifier.numeric) {
157+
case 33: /* HierarchicalReferences */
158+
case 34: /* HasChild */
159+
case 35: /* Organizes */
160+
case 36: /* HasEventSource */
161+
case 44: /* Aggregates */
162+
case 45: /* HasSubtype */
163+
case 46: /* HasProperty */
164+
case 47: /* HasComponent */
165+
case 48: /* HasNotifier */
166+
case 49: /* HasOrderedComponent */
167+
case 56: /* HasHistoricalConfiguration */
168+
return true;
169+
default:
170+
return false; /* ns0 type not in the hierarchical set */
171+
}
172+
}
173+
139174
static bool
140175
nodeRefsReady(NL_Node *node) {
141176
for(NL_Reference *ref = node->refs; ref != NULL; ref = ref->next) {
@@ -144,11 +179,12 @@ nodeRefsReady(NL_Node *node) {
144179
if(ref->targetPtr->isDone)
145180
continue;
146181
if(UA_NodeId_equal(&hasTypeDef, &ref->refType)) {
182+
/* Forward HasTypeDefinition: the type must exist first */
147183
if(ref->isForward)
148184
return false;
149-
} else {
150-
if(!ref->isForward)
151-
return false;
185+
} else if(!ref->isForward && isHierarchicalRef(&ref->refType)) {
186+
/* Inverse hierarchical reference: the parent must exist first */
187+
return false;
152188
}
153189
}
154190
return true;

0 commit comments

Comments
 (0)