Browse Source

nodestore: make the memory handling by the nodestore more intuitive

Nodes are created and destroyed only via the nodestore now.
Also, only the nodestore may make copies of nodes.
Nodes can only be replaced with modified nodes that were originally copied from the current version of the node.
The nodestore internally manages a struct with a node at the end.
Julius Pfrommer 9 years ago
parent
commit
c54348ab9d

+ 1 - 1
include/ua_server.h

@@ -335,11 +335,11 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requested
                                     const UA_VariableAttributes attr, const UA_DataSource dataSource,
                                     UA_NodeId *outNewNodeId);
 
-#ifdef UA_ENABLE_METHODCALLS
 typedef UA_StatusCode (*UA_MethodCallback)(void *methodHandle, const UA_NodeId objectId,
                                            size_t inputSize, const UA_Variant *input,
                                            size_t outputSize, UA_Variant *output);
 
+#ifdef UA_ENABLE_METHODCALLS
 UA_StatusCode UA_EXPORT
 UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
                         const UA_NodeId parentNodeId, const UA_NodeId referenceTypeId,

+ 109 - 345
src/server/ua_nodes.c

@@ -1,233 +1,63 @@
 #include "ua_nodes.h"
 #include "ua_util.h"
 
-/* UA_Node */
-static void UA_Node_deleteMembers(UA_Node *p) {
-	UA_NodeId_deleteMembers(&p->nodeId);
-	UA_QualifiedName_deleteMembers(&p->browseName);
-	UA_LocalizedText_deleteMembers(&p->displayName);
-	UA_LocalizedText_deleteMembers(&p->description);
-	UA_Array_delete(p->references, p->referencesSize, &UA_TYPES[UA_TYPES_REFERENCENODE]);
-}
-
-static UA_StatusCode UA_Node_copy(const UA_Node *src, UA_Node *dst) {
-	UA_StatusCode retval = UA_STATUSCODE_GOOD;
-	retval |= UA_NodeId_copy(&src->nodeId, &dst->nodeId);
-	dst->nodeClass = src->nodeClass;
-	retval |= UA_QualifiedName_copy(&src->browseName, &dst->browseName);
-	retval |= UA_LocalizedText_copy(&src->displayName, &dst->displayName);
-	retval |= UA_LocalizedText_copy(&src->description, &dst->description);
-	dst->writeMask = src->writeMask;
-	dst->userWriteMask = src->userWriteMask;
-	if(retval != UA_STATUSCODE_GOOD) {
-    	UA_Node_deleteMembers(dst);
-        return retval;
-    }
-	retval |= UA_Array_copy(src->references, src->referencesSize, (void**)&dst->references,
-                            &UA_TYPES[UA_TYPES_REFERENCENODE]);
-	if(retval == UA_STATUSCODE_GOOD)
-        dst->referencesSize = src->referencesSize;
-	return retval;
-}
-
-
 void UA_Node_deleteMembersAnyNodeClass(UA_Node *node) {
+    /* delete standard content */
+    UA_NodeId_deleteMembers(&node->nodeId);
+    UA_QualifiedName_deleteMembers(&node->browseName);
+    UA_LocalizedText_deleteMembers(&node->displayName);
+    UA_LocalizedText_deleteMembers(&node->description);
+    UA_Array_delete(node->references, node->referencesSize, &UA_TYPES[UA_TYPES_REFERENCENODE]);
+    node->references = NULL;
+    node->referencesSize = 0;
+
+    /* delete unique content of the nodeclass */
     switch(node->nodeClass) {
     case UA_NODECLASS_OBJECT:
-        UA_ObjectNode_deleteMembers((UA_ObjectNode*)node);
-        break;
-    case UA_NODECLASS_VARIABLE:
-        UA_VariableNode_deleteMembers((UA_VariableNode*)node);
         break;
     case UA_NODECLASS_METHOD:
-        UA_MethodNode_deleteMembers((UA_MethodNode*)node);
         break;
     case UA_NODECLASS_OBJECTTYPE:
-        UA_ObjectTypeNode_deleteMembers((UA_ObjectTypeNode*)node);
-        break;
-    case UA_NODECLASS_VARIABLETYPE:
-        UA_VariableTypeNode_deleteMembers((UA_VariableTypeNode*)node);
-        break;
-    case UA_NODECLASS_REFERENCETYPE:
-        UA_ReferenceTypeNode_deleteMembers((UA_ReferenceTypeNode*)node);
-        break;
-    case UA_NODECLASS_DATATYPE:
-        UA_DataTypeNode_deleteMembers((UA_DataTypeNode*)node);
-        break;
-    case UA_NODECLASS_VIEW:
-        UA_ViewNode_deleteMembers((UA_ViewNode*)node);
-        break;
-    default:
-        break;
-    }
-}
-
-void UA_Node_deleteAnyNodeClass(UA_Node *node) {
-    UA_Node_deleteMembersAnyNodeClass(node);
-    UA_free(node);
-}
-
-typedef UA_Node *(*UA_NewNodeFunction)(void);
-typedef UA_StatusCode (*UA_CopyNodeFunction)(const UA_Node *src, UA_Node *dst);
-typedef void (*UA_DeleteNodeFunction)(UA_Node *p);
-
-UA_Node * UA_Node_copyAnyNodeClass(const UA_Node *node) {
-    UA_NewNodeFunction newNode;
-    UA_CopyNodeFunction copyNode;
-    UA_DeleteNodeFunction deleteNode;
-
-    switch(node->nodeClass) {
-    case UA_NODECLASS_OBJECT:
-        newNode = (UA_NewNodeFunction)UA_ObjectNode_new;
-        copyNode = (UA_CopyNodeFunction)UA_ObjectNode_copy;
-        deleteNode = (UA_DeleteNodeFunction)UA_ObjectNode_delete;
         break;
     case UA_NODECLASS_VARIABLE:
-        newNode = (UA_NewNodeFunction)UA_VariableNode_new;
-        copyNode = (UA_CopyNodeFunction)UA_VariableNode_copy;
-        deleteNode = (UA_DeleteNodeFunction)UA_VariableNode_delete;
-        break;
-    case UA_NODECLASS_METHOD:
-        newNode = (UA_NewNodeFunction)UA_MethodNode_new;
-        copyNode = (UA_CopyNodeFunction)UA_MethodNode_copy;
-        deleteNode = (UA_DeleteNodeFunction)UA_MethodNode_delete;
-        break;
-    case UA_NODECLASS_OBJECTTYPE:
-        newNode = (UA_NewNodeFunction)UA_ObjectTypeNode_new;
-        copyNode = (UA_CopyNodeFunction)UA_ObjectTypeNode_copy;
-        deleteNode = (UA_DeleteNodeFunction)UA_ObjectTypeNode_delete;
-        break;
-    case UA_NODECLASS_VARIABLETYPE:
-        newNode = (UA_NewNodeFunction)UA_VariableTypeNode_new;
-        copyNode = (UA_CopyNodeFunction)UA_VariableTypeNode_copy;
-        deleteNode = (UA_DeleteNodeFunction)UA_VariableTypeNode_delete;
+    case UA_NODECLASS_VARIABLETYPE: {
+        UA_VariableNode *p = (UA_VariableNode*)node;
+        if(p->valueSource == UA_VALUESOURCE_VARIANT)
+            UA_Variant_deleteMembers(&p->value.variant.value);
         break;
-    case UA_NODECLASS_REFERENCETYPE:
-        newNode = (UA_NewNodeFunction)UA_ReferenceTypeNode_new;
-        copyNode = (UA_CopyNodeFunction)UA_ReferenceTypeNode_copy;
-        deleteNode = (UA_DeleteNodeFunction)UA_ReferenceTypeNode_delete;
+    }
+    case UA_NODECLASS_REFERENCETYPE: {
+        UA_ReferenceTypeNode *p = (UA_ReferenceTypeNode*)node;
+        UA_LocalizedText_deleteMembers(&p->inverseName);
         break;
+    }
     case UA_NODECLASS_DATATYPE:
-        newNode = (UA_NewNodeFunction)UA_DataTypeNode_new;
-        copyNode = (UA_CopyNodeFunction)UA_DataTypeNode_copy;
-        deleteNode = (UA_DeleteNodeFunction)UA_DataTypeNode_delete;
         break;
     case UA_NODECLASS_VIEW:
-        newNode = (UA_NewNodeFunction)UA_ViewNode_new;
-        copyNode = (UA_CopyNodeFunction)UA_ViewNode_copy;
-        deleteNode = (UA_DeleteNodeFunction)UA_ViewNode_delete;
         break;
     default:
-        return NULL;
         break;
     }
-
-    UA_Node *copy = newNode();
-    if(!copy)
-        return NULL;
-    if(copyNode(node, copy) != UA_STATUSCODE_GOOD) {
-        deleteNode(copy);
-        return NULL;
-    }
-    return copy;
-}
-
-/* UA_ObjectNode */
-void UA_ObjectNode_init(UA_ObjectNode *p) {
-    memset(p, 0, sizeof(UA_ObjectNode));
-    p->nodeClass = UA_NODECLASS_OBJECT;
-}
-
-UA_ObjectNode * UA_ObjectNode_new(void) {
-    UA_ObjectNode *p = (UA_ObjectNode*)UA_malloc(sizeof(UA_ObjectNode));
-    if(p)
-        UA_ObjectNode_init(p);
-    return p;
-}
-
-void UA_ObjectNode_deleteMembers(UA_ObjectNode *p) {
-    UA_Node_deleteMembers((UA_Node*)p);
-}
-
-void UA_ObjectNode_delete(UA_ObjectNode *p) {
-    UA_ObjectNode_deleteMembers(p);
-    UA_free(p);
 }
 
-UA_StatusCode UA_ObjectNode_copy(const UA_ObjectNode *src, UA_ObjectNode *dst) {
+static UA_StatusCode
+UA_ObjectNode_copy(const UA_ObjectNode *src, UA_ObjectNode *dst) {
     dst->eventNotifier = src->eventNotifier;
     dst->instanceHandle = src->instanceHandle;
-	return UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
-}
-
-/* UA_ObjectTypeNode */
-void UA_ObjectTypeNode_init(UA_ObjectTypeNode *p) {
-    memset(p, 0, sizeof(UA_ObjectTypeNode));
-    p->nodeClass = UA_NODECLASS_OBJECTTYPE;
-}
-
-UA_ObjectTypeNode * UA_ObjectTypeNode_new(void) {
-    UA_ObjectTypeNode *p = (UA_ObjectTypeNode*)UA_malloc(sizeof(UA_ObjectTypeNode));
-    if(p)
-        UA_ObjectTypeNode_init(p);
-    return p;
-}
-
-void UA_ObjectTypeNode_deleteMembers(UA_ObjectTypeNode *p) {
-    UA_Node_deleteMembers((UA_Node*)p);
-}
-
-void UA_ObjectTypeNode_delete(UA_ObjectTypeNode *p) {
-    UA_ObjectTypeNode_deleteMembers(p);
-    UA_free(p);
-}
-
-UA_StatusCode UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectTypeNode *dst) {
-    dst->isAbstract = src->isAbstract;
-    dst->lifecycleManagement = src->lifecycleManagement;
-	return UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
-}
-
-/* UA_VariableNode */
-void UA_VariableNode_init(UA_VariableNode *p) {
-    memset(p, 0, sizeof(UA_VariableNode));
-    p->nodeClass = UA_NODECLASS_VARIABLE;
-    p->valueRank = -2; // scalar or array of any dimension
-    p->accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
-    p->userAccessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
-}
-
-UA_VariableNode * UA_VariableNode_new(void) {
-    UA_VariableNode *p = (UA_VariableNode*)UA_malloc(sizeof(UA_VariableNode));
-    if(p)
-        UA_VariableNode_init(p);
-    return p;
-}
-
-void UA_VariableNode_deleteMembers(UA_VariableNode *p) {
-    if(p->valueSource == UA_VALUESOURCE_VARIANT)
-        UA_Variant_deleteMembers(&p->value.variant.value);
-    UA_Node_deleteMembers((UA_Node*)p);
-}
-
-void UA_VariableNode_delete(UA_VariableNode *p) {
-    UA_VariableNode_deleteMembers(p);
-    UA_free(p);
+    return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) {
-	UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
+static UA_StatusCode
+UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *dst) {
     dst->valueRank = src->valueRank;
     dst->valueSource = src->valueSource;
     if(src->valueSource == UA_VALUESOURCE_VARIANT) {
-        retval = UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
+        UA_StatusCode retval = UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
         dst->value.variant.callback = src->value.variant.callback;
     } else
         dst->value.dataSource = src->value.dataSource;
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_VariableNode_deleteMembers(dst);
-        return retval;
-    }
     dst->accessLevel = src->accessLevel;
     dst->userAccessLevel = src->accessLevel;
     dst->minimumSamplingInterval = src->minimumSamplingInterval;
@@ -235,178 +65,112 @@ UA_StatusCode UA_VariableNode_copy(const UA_VariableNode *src, UA_VariableNode *
     return UA_STATUSCODE_GOOD;
 }
 
-/* UA_VariableTypeNode */
-void UA_VariableTypeNode_init(UA_VariableTypeNode *p) {
-    memset(p, 0, sizeof(UA_VariableTypeNode));
-    p->nodeClass = UA_NODECLASS_VARIABLETYPE;
-    p->valueRank = -2; // scalar or array of any dimension
-}
-
-UA_VariableTypeNode * UA_VariableTypeNode_new(void) {
-    UA_VariableTypeNode *p = (UA_VariableTypeNode*)UA_malloc(sizeof(UA_VariableTypeNode));
-    if(p)
-        UA_VariableTypeNode_init(p);
-    return p;
-}
-
-void UA_VariableTypeNode_deleteMembers(UA_VariableTypeNode *p) {
-    if(p->valueSource == UA_VALUESOURCE_VARIANT)
-        UA_Variant_deleteMembers(&p->value.variant.value);
-    UA_Node_deleteMembers((UA_Node*)p);
+static UA_StatusCode
+UA_MethodNode_copy(const UA_MethodNode *src, UA_MethodNode *dst) {
+    dst->executable = src->executable;
+    dst->userExecutable = src->userExecutable;
+    dst->methodHandle  = src->methodHandle;
+    dst->attachedMethod = src->attachedMethod;
+    return UA_STATUSCODE_GOOD;
 }
 
-void UA_VariableTypeNode_delete(UA_VariableTypeNode *p) {
-    UA_VariableTypeNode_deleteMembers(p);
-    UA_free(p);
+static UA_StatusCode
+UA_ObjectTypeNode_copy(const UA_ObjectTypeNode *src, UA_ObjectTypeNode *dst) {
+    dst->isAbstract = src->isAbstract;
+    dst->lifecycleManagement = src->lifecycleManagement;
+    return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) {
-	UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
+static UA_StatusCode
+UA_VariableTypeNode_copy(const UA_VariableTypeNode *src, UA_VariableTypeNode *dst) {
     dst->valueRank = src->valueRank;
     dst->valueSource = src->valueSource;
     if(src->valueSource == UA_VALUESOURCE_VARIANT){
-        UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
+        UA_StatusCode retval = UA_Variant_copy(&src->value.variant.value, &dst->value.variant.value);
+        if(retval != UA_STATUSCODE_GOOD)
+            return retval;
         dst->value.variant.callback = src->value.variant.callback;
     } else
         dst->value.dataSource = src->value.dataSource;
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_VariableTypeNode_deleteMembers(dst);
-        return retval;
-    }
     dst->isAbstract = src->isAbstract;
     return UA_STATUSCODE_GOOD;
 }
 
-/* UA_ReferenceTypeNode */
-void UA_ReferenceTypeNode_init(UA_ReferenceTypeNode *p) {
-    memset(p, 0, sizeof(UA_ReferenceTypeNode));
-    p->nodeClass = UA_NODECLASS_REFERENCETYPE;
-}
-
-UA_ReferenceTypeNode * UA_ReferenceTypeNode_new(void) {
-    UA_ReferenceTypeNode *p = (UA_ReferenceTypeNode*)UA_malloc(sizeof(UA_ReferenceTypeNode));
-    if(p)
-        UA_ReferenceTypeNode_init(p);
-    return p;
-}
-
-void UA_ReferenceTypeNode_deleteMembers(UA_ReferenceTypeNode *p) {
-    UA_Node_deleteMembers((UA_Node*)p);
-    UA_LocalizedText_deleteMembers(&p->inverseName);
-}
-
-void UA_ReferenceTypeNode_delete(UA_ReferenceTypeNode *p) {
-    UA_ReferenceTypeNode_deleteMembers(p);
-    UA_free(p);
-}
-
-UA_StatusCode UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, UA_ReferenceTypeNode *dst) {
-    UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-    retval = UA_LocalizedText_copy(&src->inverseName, &dst->inverseName);
-    if(retval != UA_STATUSCODE_GOOD) {
-        UA_ReferenceTypeNode_deleteMembers(dst);
-        return retval;
-    }
+static UA_StatusCode
+UA_ReferenceTypeNode_copy(const UA_ReferenceTypeNode *src, UA_ReferenceTypeNode *dst) {
+    UA_StatusCode retval = UA_LocalizedText_copy(&src->inverseName, &dst->inverseName);
     dst->isAbstract = src->isAbstract;
     dst->symmetric = src->symmetric;
-    return UA_STATUSCODE_GOOD;
-}
-
-/* UA_MethodNode */
-void UA_MethodNode_init(UA_MethodNode *p) {
-    memset(p, 0, sizeof(UA_MethodNode));
-    p->nodeClass = UA_NODECLASS_METHOD;
-}
-
-UA_MethodNode * UA_MethodNode_new(void) {
-    UA_MethodNode *p = (UA_MethodNode*)UA_malloc(sizeof(UA_MethodNode));
-    if(p)
-        UA_MethodNode_init(p);
-    return p;
-}
-
-void UA_MethodNode_deleteMembers(UA_MethodNode *p) {
-#ifdef UA_ENABLE_METHODCALLS
-    p->attachedMethod = NULL;
-#endif
-    UA_Node_deleteMembers((UA_Node*)p);
-}
-
-void UA_MethodNode_delete(UA_MethodNode *p) {
-    UA_MethodNode_deleteMembers(p);
-#ifdef UA_ENABLE_METHODCALLS
-    p->methodHandle   = NULL;
-    p->attachedMethod = NULL;
-#endif
-    UA_free(p);
-}
-
-UA_StatusCode UA_MethodNode_copy(const UA_MethodNode *src, UA_MethodNode *dst) {
-    UA_StatusCode retval = UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
-    if(retval != UA_STATUSCODE_GOOD)
-        return retval;
-    dst->executable = src->executable;
-    dst->userExecutable = src->userExecutable;
-#ifdef UA_ENABLE_METHODCALLS
-    dst->methodHandle  = src->methodHandle;
-    dst->attachedMethod = src->attachedMethod;
-#endif
     return retval;
 }
 
-/* UA_ViewNode */
-void UA_ViewNode_init(UA_ViewNode *p) {
-    memset(p, 0, sizeof(UA_ViewNode));
-    p->nodeClass = UA_NODECLASS_VIEW;
-}
-
-UA_ViewNode * UA_ViewNode_new(void) {
-    UA_ViewNode *p = UA_malloc(sizeof(UA_ViewNode));
-    if(p)
-        UA_ViewNode_init(p);
-    return p;
-}
-
-void UA_ViewNode_deleteMembers(UA_ViewNode *p) {
-    UA_Node_deleteMembers((UA_Node*)p);
-}
-
-void UA_ViewNode_delete(UA_ViewNode *p) {
-    UA_ViewNode_deleteMembers(p);
-    UA_free(p);
+static UA_StatusCode
+UA_DataTypeNode_copy(const UA_DataTypeNode *src, UA_DataTypeNode *dst) {
+    dst->isAbstract = src->isAbstract;
+    return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_ViewNode_copy(const UA_ViewNode *src, UA_ViewNode *dst) {
+static UA_StatusCode
+UA_ViewNode_copy(const UA_ViewNode *src, UA_ViewNode *dst) {
     dst->containsNoLoops = src->containsNoLoops;
     dst->eventNotifier = src->eventNotifier;
-	return UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
-}
-
-/* UA_DataTypeNode */
-void UA_DataTypeNode_init(UA_DataTypeNode *p) {
-    memset(p, 0, sizeof(UA_DataTypeNode));
-    p->nodeClass = UA_NODECLASS_DATATYPE;
-}
-
-UA_DataTypeNode * UA_DataTypeNode_new(void) {
-    UA_DataTypeNode *p = UA_malloc(sizeof(UA_DataTypeNode));
-    if(p)
-        UA_DataTypeNode_init(p);
-    return p;
-}
-
-void UA_DataTypeNode_deleteMembers(UA_DataTypeNode *p) {
-    UA_Node_deleteMembers((UA_Node*)p);
+    return UA_STATUSCODE_GOOD;
 }
 
-void UA_DataTypeNode_delete(UA_DataTypeNode *p) {
-    UA_DataTypeNode_deleteMembers(p);
-    UA_free(p);
-}
+UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst) {
+    if(src->nodeClass != dst->nodeClass)
+        return UA_STATUSCODE_BADINTERNALERROR;
+    
+    /* copy standard content */
+	UA_StatusCode retval = UA_NodeId_copy(&src->nodeId, &dst->nodeId);
+	dst->nodeClass = src->nodeClass;
+	retval |= UA_QualifiedName_copy(&src->browseName, &dst->browseName);
+	retval |= UA_LocalizedText_copy(&src->displayName, &dst->displayName);
+	retval |= UA_LocalizedText_copy(&src->description, &dst->description);
+	dst->writeMask = src->writeMask;
+	dst->userWriteMask = src->userWriteMask;
+	if(retval != UA_STATUSCODE_GOOD) {
+    	UA_Node_deleteMembersAnyNodeClass(dst);
+        return retval;
+    }
+	retval |= UA_Array_copy(src->references, src->referencesSize, (void**)&dst->references,
+                            &UA_TYPES[UA_TYPES_REFERENCENODE]);
+	if(retval != UA_STATUSCODE_GOOD) {
+    	UA_Node_deleteMembersAnyNodeClass(dst);
+        return retval;
+    }
+    dst->referencesSize = src->referencesSize;
 
-UA_StatusCode UA_DataTypeNode_copy(const UA_DataTypeNode *src, UA_DataTypeNode *dst) {
-    dst->isAbstract = src->isAbstract;
-	return UA_Node_copy((const UA_Node*)src, (UA_Node*)dst);
+    /* copy unique content of the nodeclass */
+    switch(src->nodeClass) {
+    case UA_NODECLASS_OBJECT:
+        retval = UA_ObjectNode_copy((const UA_ObjectNode*)src, (UA_ObjectNode*)dst);
+        break;
+    case UA_NODECLASS_VARIABLE:
+        retval = UA_VariableNode_copy((const UA_VariableNode*)src, (UA_VariableNode*)dst);
+        break;
+    case UA_NODECLASS_METHOD:
+        retval = UA_MethodNode_copy((const UA_MethodNode*)src, (UA_MethodNode*)dst);
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        retval = UA_ObjectTypeNode_copy((const UA_ObjectTypeNode*)src, (UA_ObjectTypeNode*)dst);
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        retval = UA_VariableTypeNode_copy((const UA_VariableTypeNode*)src, (UA_VariableTypeNode*)dst);
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        retval = UA_ReferenceTypeNode_copy((const UA_ReferenceTypeNode*)src, (UA_ReferenceTypeNode*)dst);
+        break;
+    case UA_NODECLASS_DATATYPE:
+        retval = UA_DataTypeNode_copy((const UA_DataTypeNode*)src, (UA_DataTypeNode*)dst);
+        break;
+    case UA_NODECLASS_VIEW:
+        retval = UA_ViewNode_copy((const UA_ViewNode*)src, (UA_ViewNode*)dst);
+        break;
+    default:
+        break;
+    }
+	if(retval != UA_STATUSCODE_GOOD)
+    	UA_Node_deleteMembersAnyNodeClass(dst);
+    return retval;
 }

+ 6 - 20
src/server/ua_nodes.h

@@ -5,12 +5,11 @@
 #include "ua_types_generated.h"
 #include "ua_types_encoding_binary.h"
 
-#define UA_TYPE_HANDLING_FUNCTIONS(TYPE)                             \
-    TYPE UA_EXPORT * TYPE##_new(void);                               \
-    void UA_EXPORT TYPE##_init(TYPE * p);                            \
-    void UA_EXPORT TYPE##_delete(TYPE * p);                          \
-    void UA_EXPORT TYPE##_deleteMembers(TYPE * p);                   \
-    UA_StatusCode UA_EXPORT TYPE##_copy(const TYPE *src, TYPE *dst);
+/*
+ * Most APIs take and return UA_EditNode and UA_ConstNode. By looking up the
+ * nodeclass, nodes can be cast to their "true" class, i.e. UA_VariableNode,
+ * UA_ObjectNode, and so on.
+ */
 
 #define UA_STANDARD_NODEMEMBERS                 \
     UA_NodeId nodeId;                           \
@@ -27,9 +26,8 @@ typedef struct {
     UA_STANDARD_NODEMEMBERS
 } UA_Node;
 
-void UA_Node_deleteAnyNodeClass(UA_Node *node);
 void UA_Node_deleteMembersAnyNodeClass(UA_Node *node);
-UA_Node * UA_Node_copyAnyNodeClass(const UA_Node *node);
+UA_StatusCode UA_Node_copyAnyNodeClass(const UA_Node *src, UA_Node *dst);
 
 /**************/
 /* ObjectNode */
@@ -40,7 +38,6 @@ typedef struct {
     UA_Byte eventNotifier;
     void *instanceHandle;
 } UA_ObjectNode;
-UA_TYPE_HANDLING_FUNCTIONS(UA_ObjectNode)
 
 /******************/
 /* ObjectTypeNode */
@@ -51,7 +48,6 @@ typedef struct {
     UA_Boolean isAbstract;
     UA_ObjectLifecycleManagement lifecycleManagement;
 } UA_ObjectTypeNode;
-UA_TYPE_HANDLING_FUNCTIONS(UA_ObjectTypeNode)
 
 typedef enum {
     UA_VALUESOURCE_VARIANT,
@@ -83,9 +79,6 @@ typedef struct {
     UA_Double minimumSamplingInterval;
     UA_Boolean historizing;
 } UA_VariableNode;
-UA_TYPE_HANDLING_FUNCTIONS(UA_VariableNode)
-/** Make a copy but leave out the references and the variable */
-UA_StatusCode UA_VariableNode_copyWithoutRefsAndVariable(const UA_VariableNode *src, UA_VariableNode *dst);
 
 /********************/
 /* VariableTypeNode */
@@ -105,7 +98,6 @@ typedef struct {
     /* <--- similar to variablenodes up to there--->*/
     UA_Boolean isAbstract;
 } UA_VariableTypeNode;
-UA_TYPE_HANDLING_FUNCTIONS(UA_VariableTypeNode)
 
 /*********************/
 /* ReferenceTypeNode */
@@ -117,7 +109,6 @@ typedef struct {
     UA_Boolean symmetric;
     UA_LocalizedText inverseName;
 } UA_ReferenceTypeNode;
-UA_TYPE_HANDLING_FUNCTIONS(UA_ReferenceTypeNode)
 
 /**************/
 /* MethodNode */
@@ -127,12 +118,9 @@ typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Boolean executable;
     UA_Boolean userExecutable;
-#ifdef UA_ENABLE_METHODCALLS
     void *methodHandle;
     UA_MethodCallback attachedMethod;
-#endif
 } UA_MethodNode;
-UA_TYPE_HANDLING_FUNCTIONS(UA_MethodNode)
 
 /************/
 /* ViewNode */
@@ -144,7 +132,6 @@ typedef struct {
     /* <-- the same as objectnode until here --> */
     UA_Boolean containsNoLoops;
 } UA_ViewNode;
-UA_TYPE_HANDLING_FUNCTIONS(UA_ViewNode)
 
 /****************/
 /* DataTypeNode */
@@ -154,6 +141,5 @@ typedef struct {
     UA_STANDARD_NODEMEMBERS
     UA_Boolean isAbstract;
 } UA_DataTypeNode;
-UA_TYPE_HANDLING_FUNCTIONS(UA_DataTypeNode)
 
 #endif /* UA_NODES_H_ */

+ 131 - 116
src/server/ua_nodestore.c

@@ -1,26 +1,17 @@
 #include "ua_nodestore.h"
 #include "ua_util.h"
 #include "ua_statuscodes.h"
+#include <stdio.h>
 
 #define UA_NODESTORE_MINSIZE 64
 
-typedef struct {
-    UA_Boolean taken;
-    union {
-        UA_Node node;
-        UA_ObjectNode objectNode;
-        UA_ObjectTypeNode objectTypeNode;
-        UA_VariableNode variableNode;
-        UA_VariableTypeNode variableTypeNode;
-        UA_ReferenceTypeNode referenceTypeNode;
-        UA_MethodNode methodeNode;
-        UA_ViewNode viewNode;
-        UA_DataTypeNode dataTypeNode;
-    } node;
+typedef struct UA_NodeStoreEntry {
+    struct UA_NodeStoreEntry *orig; // the version this is a copy from (or NULL)
+    UA_Node node;
 } UA_NodeStoreEntry;
 
 struct UA_NodeStore {
-    UA_NodeStoreEntry *entries;
+    UA_NodeStoreEntry **entries;
     UA_UInt32 size;
     UA_UInt32 count;
     UA_UInt32 sizePrimeIndex;
@@ -51,22 +42,64 @@ static UA_Int16 higher_prime_index(hash_t n) {
     return low;
 }
 
+static UA_NodeStoreEntry * instantiateEntry(UA_NodeClass class) {
+    size_t size = sizeof(UA_NodeStoreEntry) - sizeof(UA_Node);
+    switch(class) {
+    case UA_NODECLASS_OBJECT:
+        size += sizeof(UA_ObjectNode);
+        break;
+    case UA_NODECLASS_VARIABLE:
+        size += sizeof(UA_VariableNode);
+        break;
+    case UA_NODECLASS_METHOD:
+        size += sizeof(UA_MethodNode);
+        break;
+    case UA_NODECLASS_OBJECTTYPE:
+        size += sizeof(UA_ObjectTypeNode);
+        break;
+    case UA_NODECLASS_VARIABLETYPE:
+        size += sizeof(UA_VariableTypeNode);
+        break;
+    case UA_NODECLASS_REFERENCETYPE:
+        size += sizeof(UA_ReferenceTypeNode);
+        break;
+    case UA_NODECLASS_DATATYPE:
+        size += sizeof(UA_DataTypeNode);
+        break;
+    case UA_NODECLASS_VIEW:
+        size += sizeof(UA_ViewNode);
+        break;
+    default:
+        return NULL;
+    }
+    UA_NodeStoreEntry *entry = UA_calloc(1, size);
+    if(!entry)
+        return NULL;
+    entry->node.nodeClass = class;
+    return entry;
+}
+
+static void deleteEntry(UA_NodeStoreEntry *entry) {
+    UA_Node_deleteMembersAnyNodeClass(&entry->node);
+    UA_free(entry);
+}
+
 /* Returns UA_TRUE if an entry was found under the nodeid. Otherwise, returns
    false and sets slot to a pointer to the next free slot. */
 static UA_Boolean
-containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid, UA_NodeStoreEntry **entry) {
-    hash_t         h     = hash(nodeid);
-    UA_UInt32      size  = ns->size;
-    hash_t         index = mod(h, size);
-    UA_NodeStoreEntry *e = &ns->entries[index];
+containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid, UA_NodeStoreEntry ***entry) {
+    hash_t h = hash(nodeid);
+    UA_UInt32 size = ns->size;
+    hash_t index = mod(h, size);
+    UA_NodeStoreEntry *e = ns->entries[index];
 
-    if(!e->taken) {
-        *entry = e;
+    if(!e) {
+        *entry = &ns->entries[index];
         return UA_FALSE;
     }
 
-    if(UA_NodeId_equal(&e->node.node.nodeId, nodeid)) {
-        *entry = e;
+    if(UA_NodeId_equal(&e->node.nodeId, nodeid)) {
+        *entry = &ns->entries[index];
         return UA_TRUE;
     }
 
@@ -75,13 +108,13 @@ containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid, UA_NodeStoreEntr
         index += hash2;
         if(index >= size)
             index -= size;
-        e = &ns->entries[index];
-        if(!e->taken) {
-            *entry = e;
+        e = ns->entries[index];
+        if(!e) {
+            *entry = &ns->entries[index];
             return UA_FALSE;
         }
-        if(UA_NodeId_equal(&e->node.node.nodeId, nodeid)) {
-            *entry = e;
+        if(UA_NodeId_equal(&e->node.nodeId, nodeid)) {
+            *entry = &ns->entries[index];
             return UA_TRUE;
         }
     }
@@ -90,33 +123,31 @@ containsNodeId(const UA_NodeStore *ns, const UA_NodeId *nodeid, UA_NodeStoreEntr
     return UA_TRUE;
 }
 
-/* The following function changes size of memory allocated for the entries and
-   repeatedly inserts the table elements. The occupancy of the table after the
-   call will be about 50%. */
+/* The occupancy of the table after the call will be about 50% */
 static UA_StatusCode expand(UA_NodeStore *ns) {
     UA_UInt32 osize = ns->size;
     UA_UInt32 count = ns->count;
-    /* Resize only when table after removal of unused elements is either too full or too empty.  */
+    /* Resize only when table after removal of unused elements is either too full or too empty  */
     if(count * 2 < osize && (count * 8 > osize || osize <= UA_NODESTORE_MINSIZE))
         return UA_STATUSCODE_GOOD;
 
+    UA_NodeStoreEntry **oentries = ns->entries;
     UA_UInt32 nindex = higher_prime_index(count * 2);
     UA_Int32 nsize = primes[nindex];
-    UA_NodeStoreEntry *nentries;
-    if(!(nentries = UA_calloc(nsize, sizeof(UA_NodeStoreEntry))))
+    UA_NodeStoreEntry **nentries;
+    if(!(nentries = UA_calloc(nsize, sizeof(UA_NodeStoreEntry*))))
         return UA_STATUSCODE_BADOUTOFMEMORY;
 
-    UA_NodeStoreEntry *oentries = ns->entries;
     ns->entries = nentries;
-    ns->size    = nsize;
+    ns->size = nsize;
     ns->sizePrimeIndex = nindex;
 
-    // recompute the position of every entry and insert the pointer
+    /* recompute the position of every entry and insert the pointer */
     for(size_t i = 0, j = 0; i < osize && j < count; i++) {
-        if(!oentries[i].taken)
+        if(!oentries[i])
             continue;
-        UA_NodeStoreEntry *e;
-        containsNodeId(ns, &oentries[i].node.node.nodeId, &e);  /* We know this returns an empty entry here */
+        UA_NodeStoreEntry **e;
+        containsNodeId(ns, &oentries[i]->node.nodeId, &e);  /* We know this returns an empty entry here */
         *e = oentries[i];
         j++;
     }
@@ -125,49 +156,6 @@ static UA_StatusCode expand(UA_NodeStore *ns) {
     return UA_STATUSCODE_GOOD;
 }
 
-/* Marks the entry dead and deletes if necessary. */
-static UA_INLINE void
-deleteEntry(UA_NodeStoreEntry *entry) {
-    UA_Node_deleteMembersAnyNodeClass(&entry->node.node);
-    entry->taken = UA_FALSE;
-}
-
-/** Copies the node into the entry. Then free the original node (but not its content). */
-static void fillEntry(UA_NodeStoreEntry *entry, UA_Node *node) {
-    size_t nodesize = 0;
-    switch(node->nodeClass) {
-    case UA_NODECLASS_OBJECT:
-        nodesize = sizeof(UA_ObjectNode);
-        break;
-    case UA_NODECLASS_VARIABLE:
-        nodesize = sizeof(UA_VariableNode);
-        break;
-    case UA_NODECLASS_METHOD:
-        nodesize = sizeof(UA_MethodNode);
-        break;
-    case UA_NODECLASS_OBJECTTYPE:
-        nodesize = sizeof(UA_ObjectTypeNode);
-        break;
-    case UA_NODECLASS_VARIABLETYPE:
-        nodesize = sizeof(UA_VariableTypeNode);
-        break;
-    case UA_NODECLASS_REFERENCETYPE:
-        nodesize = sizeof(UA_ReferenceTypeNode);
-        break;
-    case UA_NODECLASS_DATATYPE:
-        nodesize = sizeof(UA_DataTypeNode);
-        break;
-    case UA_NODECLASS_VIEW:
-        nodesize = sizeof(UA_ViewNode);
-        break;
-    default:
-        UA_assert(UA_FALSE);
-    }
-    memcpy(&entry->node, node, nodesize);
-    UA_free(node);
-    entry->taken = UA_TRUE;
-}
-
 /**********************/
 /* Exported functions */
 /**********************/
@@ -179,7 +167,7 @@ UA_NodeStore * UA_NodeStore_new(void) {
     ns->sizePrimeIndex = higher_prime_index(UA_NODESTORE_MINSIZE);
     ns->size = primes[ns->sizePrimeIndex];
     ns->count = 0;
-    if(!(ns->entries = UA_calloc(ns->size, sizeof(UA_NodeStoreEntry)))) {
+    if(!(ns->entries = UA_calloc(ns->size, sizeof(UA_NodeStoreEntry*)))) {
         UA_free(ns);
         return NULL;
     }
@@ -188,29 +176,40 @@ UA_NodeStore * UA_NodeStore_new(void) {
 
 void UA_NodeStore_delete(UA_NodeStore *ns) {
     UA_UInt32 size = ns->size;
-    UA_NodeStoreEntry *entries = ns->entries;
-    for(UA_UInt32 i = 0;i < size;i++) {
-        if(entries[i].taken)
-            deleteEntry(&entries[i]);
+    UA_NodeStoreEntry **entries = ns->entries;
+    for(UA_UInt32 i = 0; i < size; i++) {
+        if(entries[i])
+            deleteEntry(entries[i]);
     }
     UA_free(ns->entries);
     UA_free(ns);
 }
 
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, UA_Node **inserted) {
+UA_Node * UA_NodeStore_newNode(UA_NodeClass class) {
+    UA_NodeStoreEntry *entry = instantiateEntry(class);
+    if(!entry)
+        return NULL;
+    return (UA_Node*)&entry->node;
+}
+
+void UA_NodeStore_deleteNode(UA_Node *node) {
+    deleteEntry(container_of(node, UA_NodeStoreEntry, node));
+}
+
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node) {
     if(ns->size * 3 <= ns->count * 4) {
         if(expand(ns) != UA_STATUSCODE_GOOD)
             return UA_STATUSCODE_BADINTERNALERROR;
     }
 
-    UA_NodeStoreEntry *entry;
     UA_NodeId tempNodeid;
     tempNodeid = node->nodeId;
     tempNodeid.namespaceIndex = 0;
+    UA_NodeStoreEntry **entry;
     if(UA_NodeId_isNull(&tempNodeid)) {
-        /* find a free nodeid */
-        if(node->nodeId.namespaceIndex == 0) //original request for ns=0 should yield ns=1
+        if(node->nodeId.namespaceIndex == 0)
             node->nodeId.namespaceIndex = 1;
+        /* find a free nodeid */
         UA_Int32 identifier = ns->count+1; // start value
         UA_Int32 size = ns->size;
         hash_t increase = mod2(identifier, size);
@@ -223,45 +222,61 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, UA_Node **ins
                 identifier -= size;
         }
     } else {
-        if(containsNodeId(ns, &node->nodeId, &entry))
+        if(containsNodeId(ns, &node->nodeId, &entry)) {
+            deleteEntry(container_of(node, UA_NodeStoreEntry, node));
             return UA_STATUSCODE_BADNODEIDEXISTS;
+        }
     }
 
-    fillEntry(entry, node);
+    *entry = container_of(node, UA_NodeStoreEntry, node);
     ns->count++;
-    if(inserted)
-        *inserted = &entry->node.node;
     return UA_STATUSCODE_GOOD;
 }
 
 UA_StatusCode
-UA_NodeStore_replace(UA_NodeStore *ns, UA_Node *oldNode,
-                     UA_Node *node, UA_Node **inserted) {
-    UA_NodeStoreEntry *slot;
-    if(!containsNodeId(ns, &node->nodeId, &slot))
+UA_NodeStore_replace(UA_NodeStore *ns, UA_Node *node) {
+    UA_NodeStoreEntry **entry;
+    if(!containsNodeId(ns, &node->nodeId, &entry))
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
-    /* that is not the node you are looking for */
-    if(&slot->node.node != oldNode)
-        return UA_STATUSCODE_BADINTERNALERROR;
-    deleteEntry(slot);
-    fillEntry(slot, node);
-    if(inserted)
-        *inserted = &slot->node.node;
+    UA_NodeStoreEntry *newEntry = container_of(node, UA_NodeStoreEntry, node);
+    if(*entry != newEntry->orig) {
+        deleteEntry(newEntry);
+        return UA_STATUSCODE_BADINTERNALERROR; // the node was replaced since the copy was made
+    }
+    deleteEntry(*entry);
+    *entry = newEntry;
     return UA_STATUSCODE_GOOD;
 }
 
-UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_NodeStoreEntry *slot;
+const UA_Node * UA_NodeStore_get(UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    UA_NodeStoreEntry **entry;
+    if(!containsNodeId(ns, nodeid, &entry))
+        return NULL;
+    return (const UA_Node*)&(*entry)->node;
+}
+
+UA_Node * UA_NodeStore_getCopy(UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    UA_NodeStoreEntry **slot;
     if(!containsNodeId(ns, nodeid, &slot))
         return NULL;
-    return &slot->node.node;
+    UA_NodeStoreEntry *entry = *slot;
+    UA_NodeStoreEntry *new = instantiateEntry(entry->node.nodeClass);
+    if(!new)
+        return NULL;
+    if(UA_Node_copyAnyNodeClass(&entry->node, &new->node) != UA_STATUSCODE_GOOD) {
+        deleteEntry(new);
+        return NULL;
+    }
+    new->orig = entry;
+    return &new->node;
 }
 
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
-    UA_NodeStoreEntry *slot;
+    UA_NodeStoreEntry **slot;
     if(!containsNodeId(ns, nodeid, &slot))
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
-    deleteEntry(slot);
+    deleteEntry(*slot);
+    *slot = NULL;
     ns->count--;
     /* Downsize the hashmap if it is very empty */
     if(ns->count * 8 < ns->size && ns->size > 32)
@@ -269,9 +284,9 @@ UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
     return UA_STATUSCODE_GOOD;
 }
 
-void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
-    for(UA_UInt32 i = 0;i < ns->size;i++) {
-        if(ns->entries[i].taken)
-            visitor(&ns->entries[i].node.node);
+void UA_NodeStore_iterate(UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
+    for(UA_UInt32 i = 0; i < ns->size; i++) {
+        if(ns->entries[i])
+            visitor((UA_Node*)&ns->entries[i]->node);
     }
 }

+ 32 - 48
src/server/ua_nodestore.h

@@ -5,33 +5,10 @@
 #include "ua_nodes.h"
 
 /**
- * @ingroup server
- *
- * @defgroup nodestore NodeStore
- *
- * @brief Stores the nodes in the address space. Internally, it is based on a
- * hash-map that maps nodes to their nodeid.
- *
- * Nodes need to be allocated on the heap before adding them to the nodestore
- * with. When adding, the node is copied to a new (managed) location in the
- * nodestore and the original memory is freed. The nodes in the nodestore are
- * immutable. To change the content of a node, it needs to be replaced as a
- * whole.
- *
- * Every node you _get from the nodestore needs to be _released when it is no
- * longer needed. In the background, reference counting is used to know if
- * somebody still uses the node in multi-threaded environments.
- *
- * @{
+ * Stores the nodes in the address space. Internally, it is based on a hash-map
+ * that maps nodes to their nodeid.
  */
 
-/* For multithreading, nodes in the nodestore are immutable */
-#ifdef UA_ENABLE_MULTITHREADING
-# define UA_MT_CONST const
-#else
-# define UA_MT_CONST
-#endif
-
 struct UA_NodeStore;
 typedef struct UA_NodeStore UA_NodeStore;
 
@@ -42,37 +19,46 @@ UA_NodeStore * UA_NodeStore_new(void);
     critical section (multithreading). */
 void UA_NodeStore_delete(UA_NodeStore *ns);
 
+/** Create an editable node of the given NodeClass. */
+UA_Node * UA_NodeStore_newNode(UA_NodeClass class);
+#define UA_NodeStore_newObjectNode() (UA_ObjectNode*)UA_NodeStore_newNode(UA_NODECLASS_OBJECT)
+#define UA_NodeStore_newVariableNode() (UA_VariableNode*)UA_NodeStore_newNode(UA_NODECLASS_VARIABLE)
+#define UA_NodeStore_newMethodNode() (UA_MethodNode*)UA_NodeStore_newNode(UA_NODECLASS_METHOD)
+#define UA_NodeStore_newObjectTypeNode() (UA_ObjectTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_OBJECTTYPE)
+#define UA_NodeStore_newVariableTypeNode() (UA_VariableTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_VARIABLETYPE)
+#define UA_NodeStore_newReferenceTypeNode() (UA_ReferenceTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_REFERENCETYPE)
+#define UA_NodeStore_newDataTypeNode() (UA_DataTypeNode*)UA_NodeStore_newNode(UA_NODECLASS_DATATYPE)
+#define UA_NodeStore_newViewNode() (UA_ViewNode*)UA_NodeStore_newNode(UA_NODECLASS_VIEW)
+
+/** Delete an editable node. */
+void UA_NodeStore_deleteNode(UA_Node *node);
+
 /**
  * Inserts a new node into the nodestore. If the nodeid is zero, then a fresh
- * numeric nodeid from nodestore 1 is assigned. The memory of the original node
- * is freed and the content is moved to a managed (immutable) node. If inserted
- * is not NULL, then a pointer to the managed node is returned (and must be
- * released).
+ * numeric nodeid from namespace 1 is assigned. If insertion fails, the node is
+ * deleted.
  */
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, UA_MT_CONST UA_Node **inserted);
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node);
 
 /**
- * Replace an existing node in the nodestore. If the node was already replaced,
- * UA_STATUSCODE_BADINTERNALERROR is returned. A pointer to the inserted node is
- * returned. It is important that oldNode is not used afterwards in the same
- * thread.
+ * To replace, get an editable copy, edit and use this function. If the node was
+ * already replaced since the copy was made, UA_STATUSCODE_BADINTERNALERROR is
+ * returned. If the nodeid is not found, UA_STATUSCODE_BADNODEIDUNKNOWN is
+ * returned. In both error cases, the editable node is deleted.
  */
-UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, UA_MT_CONST UA_Node *oldNode,
-                                   UA_Node *node, UA_MT_CONST UA_Node **inserted);
+UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, UA_Node *node);
 
-/**
- * Remove a node from the nodestore. Always succeeds, even if the node was not
- * found.
- */
+/** Remove a node in the nodestore. */
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid);
 
 /**
- * Retrieve a managed node (read-only) from the nodestore. Nodes are reference-
- * counted (for garbage collection) and immutable. They can only be replaced
- * entirely. After the node is no longer used, it needs to be released to decrease
- * the reference count.
+ * The returned pointer is only valid as long as the node has not been replaced
+ * or removed (in the same thread).
  */
-UA_MT_CONST UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid);
+const UA_Node * UA_NodeStore_get(UA_NodeStore *ns, const UA_NodeId *nodeid);
+
+/** Returns the copy of a node. */
+UA_Node * UA_NodeStore_getCopy(UA_NodeStore *ns, const UA_NodeId *nodeid);
 
 /**
  * A function that can be evaluated on all entries in a nodestore via
@@ -81,8 +67,6 @@ UA_MT_CONST UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *
 typedef void (*UA_NodeStore_nodeVisitor)(const UA_Node *node);
 
 /** Iterate over all nodes in a nodestore. */
-void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor);
-
-/** @} */
+void UA_NodeStore_iterate(UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor);
 
 #endif /* UA_NODESTORE_H_ */

+ 96 - 138
src/server/ua_nodestore_concurrent.c

@@ -4,47 +4,53 @@
 struct nodeEntry {
     struct cds_lfht_node htn; ///< Contains the next-ptr for urcu-hashmap
     struct rcu_head rcu_head; ///< For call-rcu
+    struct nodeEntry *orig; //< the version this is a copy from (or NULL)
     UA_Node node; ///< Might be cast from any _bigger_ UA_Node* type. Allocate enough memory!
 };
 
-struct UA_NodeStore {
-    struct cds_lfht *ht;
-};
-
 #include "ua_nodestore_hash.inc"
 
-static void deleteEntry(struct rcu_head *head) {
-    struct nodeEntry *entry = caa_container_of(head, struct nodeEntry, rcu_head);
-    switch(entry->node.nodeClass) {
+static struct nodeEntry * instantiateEntry(UA_NodeClass class) {
+    size_t size = sizeof(struct nodeEntry) - sizeof(UA_Node);
+    switch(class) {
     case UA_NODECLASS_OBJECT:
-        UA_ObjectNode_deleteMembers((UA_ObjectNode*)&entry->node);
+        size += sizeof(UA_ObjectNode);
         break;
     case UA_NODECLASS_VARIABLE:
-        UA_VariableNode_deleteMembers((UA_VariableNode*)&entry->node);
+        size += sizeof(UA_VariableNode);
         break;
     case UA_NODECLASS_METHOD:
-        UA_MethodNode_deleteMembers((UA_MethodNode*)&entry->node);
+        size += sizeof(UA_MethodNode);
         break;
     case UA_NODECLASS_OBJECTTYPE:
-        UA_ObjectTypeNode_deleteMembers((UA_ObjectTypeNode*)&entry->node);
+        size += sizeof(UA_ObjectTypeNode);
         break;
     case UA_NODECLASS_VARIABLETYPE:
-        UA_VariableTypeNode_deleteMembers((UA_VariableTypeNode*)&entry->node);
+        size += sizeof(UA_VariableTypeNode);
         break;
     case UA_NODECLASS_REFERENCETYPE:
-        UA_ReferenceTypeNode_deleteMembers((UA_ReferenceTypeNode*)&entry->node);
+        size += sizeof(UA_ReferenceTypeNode);
         break;
     case UA_NODECLASS_DATATYPE:
-        UA_DataTypeNode_deleteMembers((UA_DataTypeNode*)&entry->node);
+        size += sizeof(UA_DataTypeNode);
         break;
     case UA_NODECLASS_VIEW:
-        UA_ViewNode_deleteMembers((UA_ViewNode*)&entry->node);
+        size += sizeof(UA_ViewNode);
         break;
     default:
-        UA_assert(UA_FALSE);
-        break;
+        return NULL;
     }
-    free(entry);
+    struct nodeEntry *entry = UA_calloc(1, size);
+    if(!entry)
+        return NULL;
+    entry->node.nodeClass = class;
+    return entry;
+}
+
+static void deleteEntry(struct rcu_head *head) {
+    struct nodeEntry *entry = container_of(head, struct nodeEntry, rcu_head);
+    UA_Node_deleteMembersAnyNodeClass(&entry->node);
+    UA_free(entry);
 }
 
 /* We are in a rcu_read lock. So the node will not be freed under our feet. */
@@ -56,25 +62,16 @@ static int compare(struct cds_lfht_node *htn, const void *orig) {
 }
 
 UA_NodeStore * UA_NodeStore_new() {
-    UA_NodeStore *ns;
-    if(!(ns = UA_malloc(sizeof(UA_NodeStore))))
-        return NULL;
-
-    /* 32 is the minimum size for the hashtable. */
-    ns->ht = cds_lfht_new(32, 32, 0, CDS_LFHT_AUTO_RESIZE, NULL);
-    if(!ns->ht) {
-        UA_free(ns);
-        ns = NULL;
-    }
-    return ns;
+    /* 64 is the minimum size for the hashtable. */
+    return (UA_NodeStore*)cds_lfht_new(64, 64, 0, CDS_LFHT_AUTO_RESIZE, NULL);
 }
 
 /* do not call with read-side critical section held!! */
 void UA_NodeStore_delete(UA_NodeStore *ns) {
-    struct cds_lfht *ht = ns->ht;
-    struct cds_lfht_iter  iter;
+    UA_ASSERT_RCU_LOCKED();
+    struct cds_lfht *ht = (struct cds_lfht*)ns;
+    struct cds_lfht_iter iter;
     cds_lfht_first(ht, &iter);
-    rcu_read_lock();
     while(iter.node) {
         if(!cds_lfht_del(ht, iter.node)) {
             /* points to the htn entry, which is first */
@@ -83,49 +80,26 @@ void UA_NodeStore_delete(UA_NodeStore *ns) {
         }
         cds_lfht_next(ht, &iter);
     }
-    rcu_read_unlock();
     cds_lfht_destroy(ht, NULL);
     UA_free(ns);
 }
 
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node **inserted) {
-    size_t nodesize;
-    /* Copy the node into the entry. Then reset the original node. It shall no longer be used. */
-    switch(node->nodeClass) {
-    case UA_NODECLASS_OBJECT:
-        nodesize = sizeof(UA_ObjectNode);
-        break;
-    case UA_NODECLASS_VARIABLE:
-        nodesize = sizeof(UA_VariableNode);
-        break;
-    case UA_NODECLASS_METHOD:
-        nodesize = sizeof(UA_MethodNode);
-        break;
-    case UA_NODECLASS_OBJECTTYPE:
-        nodesize = sizeof(UA_ObjectTypeNode);
-        break;
-    case UA_NODECLASS_VARIABLETYPE:
-        nodesize = sizeof(UA_VariableTypeNode);
-        break;
-    case UA_NODECLASS_REFERENCETYPE:
-        nodesize = sizeof(UA_ReferenceTypeNode);
-        break;
-    case UA_NODECLASS_DATATYPE:
-        nodesize = sizeof(UA_DataTypeNode);
-        break;
-    case UA_NODECLASS_VIEW:
-        nodesize = sizeof(UA_ViewNode);
-        break;
-    default:
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
+UA_Node * UA_NodeStore_newNode(UA_NodeClass class) {
+    struct nodeEntry *entry = instantiateEntry(class);
+    if(!entry)
+        return NULL;
+    return (UA_Node*)&entry->node;
+}
 
-    struct nodeEntry *entry;
-    if(!(entry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    UA_Node *newNode = &entry->node;
-    memcpy(newNode, node, nodesize);
+void UA_NodeStore_deleteNode(UA_Node *node) {
+    struct nodeEntry *entry = container_of(node, struct nodeEntry, node);
+    deleteEntry(&entry->rcu_head);
+}
 
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node) {
+    UA_ASSERT_RCU_LOCKED();
+    struct nodeEntry *entry = container_of(node, struct nodeEntry, node);
+    struct cds_lfht *ht = (struct cds_lfht*)ns;
     cds_lfht_node_init(&entry->htn);
     struct cds_lfht_node *result;
     //namespace index is assumed to be valid
@@ -134,131 +108,115 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node *node, const UA_Node
     tempNodeid.namespaceIndex = 0;
     if(!UA_NodeId_isNull(&tempNodeid)) {
         hash_t h = hash(&node->nodeId);
-        result = cds_lfht_add_unique(ns->ht, h, compare, &newNode->nodeId, &entry->htn);
+        result = cds_lfht_add_unique(ht, h, compare, &node->nodeId, &entry->htn);
         /* If the nodeid exists already */
         if(result != &entry->htn) {
-            UA_free(entry);
+            deleteEntry(&entry->rcu_head);
             return UA_STATUSCODE_BADNODEIDEXISTS;
         }
     } else {
         /* create a unique nodeid */
-        newNode->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
-        if(newNode->nodeId.namespaceIndex == 0) // original request for ns=0 should yield ns=1
-            newNode->nodeId.namespaceIndex = 1;
+        node->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
+        if(node->nodeId.namespaceIndex == 0) // original request for ns=0 should yield ns=1
+            node->nodeId.namespaceIndex = 1;
         /* set namespaceIndex in browseName in case id is generated */
-        if(newNode->nodeClass == UA_NODECLASS_VARIABLE)
-        	((UA_VariableNode*)newNode)->browseName.namespaceIndex = newNode->nodeId.namespaceIndex;
+        if(node->nodeClass == UA_NODECLASS_VARIABLE)
+        	((UA_VariableNode*)node)->browseName.namespaceIndex = node->nodeId.namespaceIndex;
 
         unsigned long identifier;
         long before, after;
-        cds_lfht_count_nodes(ns->ht, &before, &identifier, &after); // current amount of nodes stored
+        cds_lfht_count_nodes(ht, &before, &identifier, &after); // current amount of nodes stored
         identifier++;
 
-        newNode->nodeId.identifier.numeric = identifier;
+        node->nodeId.identifier.numeric = identifier;
         while(UA_TRUE) {
-            hash_t h = hash(&newNode->nodeId);
-            result = cds_lfht_add_unique(ns->ht, h, compare, &newNode->nodeId, &entry->htn);
+            hash_t h = hash(&node->nodeId);
+            result = cds_lfht_add_unique(ht, h, compare, &node->nodeId, &entry->htn);
             if(result == &entry->htn)
                 break;
-            newNode->nodeId.identifier.numeric += (identifier * 2654435761);
+            node->nodeId.identifier.numeric += (identifier * 2654435761);
         }
     }
-
-    UA_free(node);
-    if(inserted)
-        *inserted = &entry->node;
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node *oldNode, UA_Node *node,
-                                   const UA_Node **inserted) {
+UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, UA_Node *node) {
+    UA_ASSERT_RCU_LOCKED();
+    struct nodeEntry *entry = container_of(node, struct nodeEntry, node);
+    struct cds_lfht *ht = (struct cds_lfht*)ns;
+
     /* Get the current version */
     hash_t h = hash(&node->nodeId);
     struct cds_lfht_iter iter;
-    cds_lfht_lookup(ns->ht, h, compare, &node->nodeId, &iter);
+    cds_lfht_lookup(ht, h, compare, &node->nodeId, &iter);
     if(!iter.node)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
 
     /* We try to replace an obsolete version of the node */
     struct nodeEntry *oldEntry = (struct nodeEntry*)iter.node;
-    if(&oldEntry->node != oldNode)
+    if(oldEntry != entry->orig)
         return UA_STATUSCODE_BADINTERNALERROR;
     
-    size_t nodesize;
-    /* Copy the node into the entry. Then reset the original node. It shall no longer be used. */
-    switch(node->nodeClass) {
-    case UA_NODECLASS_OBJECT:
-        nodesize = sizeof(UA_ObjectNode);
-        break;
-    case UA_NODECLASS_VARIABLE:
-        nodesize = sizeof(UA_VariableNode);
-        break;
-    case UA_NODECLASS_METHOD:
-        nodesize = sizeof(UA_MethodNode);
-        break;
-    case UA_NODECLASS_OBJECTTYPE:
-        nodesize = sizeof(UA_ObjectTypeNode);
-        break;
-    case UA_NODECLASS_VARIABLETYPE:
-        nodesize = sizeof(UA_VariableTypeNode);
-        break;
-    case UA_NODECLASS_REFERENCETYPE:
-        nodesize = sizeof(UA_ReferenceTypeNode);
-        break;
-    case UA_NODECLASS_DATATYPE:
-        nodesize = sizeof(UA_DataTypeNode);
-        break;
-    case UA_NODECLASS_VIEW:
-        nodesize = sizeof(UA_ViewNode);
-        break;
-    default:
-        return UA_STATUSCODE_BADINTERNALERROR;
-    }
-
-    struct nodeEntry *newEntry;
-    if(!(newEntry = UA_malloc(sizeof(struct nodeEntry) - sizeof(UA_Node) + nodesize)))
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    memcpy((void*)&newEntry->node, node, nodesize);
-    cds_lfht_node_init(&newEntry->htn);
-
-    if(cds_lfht_replace(ns->ht, &iter, h, compare, &node->nodeId, &newEntry->htn) != 0) {
+    cds_lfht_node_init(&entry->htn);
+    if(cds_lfht_replace(ht, &iter, h, compare, &node->nodeId, &entry->htn) != 0) {
         /* Replacing failed. Maybe the node got replaced just before this thread tried to.*/
-        UA_free(newEntry);
+        deleteEntry(&entry->rcu_head);
         return UA_STATUSCODE_BADINTERNALERROR;
     }
         
     /* If an entry got replaced, mark it as dead. */
     call_rcu(&oldEntry->rcu_head, deleteEntry);
-    UA_free(node);
-
-    if(inserted)
-        *inserted = &newEntry->node;
     return UA_STATUSCODE_GOOD;
 }
 
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    UA_ASSERT_RCU_LOCKED();
+    struct cds_lfht *ht = (struct cds_lfht*)ns;
     hash_t h = hash(nodeid);
     struct cds_lfht_iter iter;
-    cds_lfht_lookup(ns->ht, h, compare, &nodeid, &iter);
-    if(!iter.node || cds_lfht_del(ns->ht, iter.node) != 0)
+    cds_lfht_lookup(ht, h, compare, &nodeid, &iter);
+    if(!iter.node || cds_lfht_del(ht, iter.node) != 0)
         return UA_STATUSCODE_BADNODEIDUNKNOWN;
     struct nodeEntry *entry = (struct nodeEntry*)iter.node;
     call_rcu(&entry->rcu_head, deleteEntry);
     return UA_STATUSCODE_GOOD;
 }
 
-const UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
+const UA_Node * UA_NodeStore_get(UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    UA_ASSERT_RCU_LOCKED();
+    struct cds_lfht *ht = (struct cds_lfht*)ns;
     hash_t h = hash(nodeid);
     struct cds_lfht_iter iter;
-    cds_lfht_lookup(ns->ht, h, compare, nodeid, &iter);
+    cds_lfht_lookup(ht, h, compare, nodeid, &iter);
     struct nodeEntry *found_entry = (struct nodeEntry*)iter.node;
     if(!found_entry)
         return NULL;
     return &found_entry->node;
 }
 
-void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
-    struct cds_lfht *ht = ns->ht;
+UA_Node * UA_NodeStore_getCopy(UA_NodeStore *ns, const UA_NodeId *nodeid) {
+    UA_ASSERT_RCU_LOCKED();
+    struct cds_lfht *ht = (struct cds_lfht*)ns;
+    hash_t h = hash(nodeid);
+    struct cds_lfht_iter iter;
+    cds_lfht_lookup(ht, h, compare, nodeid, &iter);
+    struct nodeEntry *entry = (struct nodeEntry*)iter.node;
+    if(!entry)
+        return NULL;
+    struct nodeEntry *new = instantiateEntry(entry->node.nodeClass);
+    if(!new)
+        return NULL;
+    if(UA_Node_copyAnyNodeClass(&entry->node, &new->node) != UA_STATUSCODE_GOOD) {
+        deleteEntry(&new->rcu_head);
+        return NULL;
+    }
+    new->orig = entry;
+    return &new->node;
+}
+
+void UA_NodeStore_iterate(UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {
+    UA_ASSERT_RCU_LOCKED();
+    struct cds_lfht *ht = (struct cds_lfht*)ns;
     struct cds_lfht_iter iter;
     cds_lfht_first(ht, &iter);
     while(iter.node != NULL) {

+ 101 - 77
src/server/ua_server.c

@@ -72,7 +72,7 @@ static const UA_NodeId nodeIdOrganizes = {
 static const UA_ExpandedNodeId expandedNodeIdBaseDataVariabletype = {
     .nodeId = {.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC,
                .identifier.numeric = UA_NS0ID_BASEDATAVARIABLETYPE},
-    .namespaceUri = {.length = -1, .data = NULL}, .serverIndex = 0};
+    .namespaceUri = {.length = 0, .data = NULL}, .serverIndex = 0};
 
 #ifndef UA_ENABLE_GENERATE_NAMESPACE0
 static const UA_NodeId nodeIdNonHierarchicalReferences = {
@@ -204,7 +204,10 @@ addReferenceInternal(UA_Server *server, const UA_NodeId sourceId, const UA_NodeI
     item.referenceTypeId = refTypeId;
     item.isForward = isForward;
     item.targetNodeId = targetId;
-    return Service_AddReferences_single(server, &adminSession, &item);
+    UA_RCU_LOCK();
+    UA_StatusCode retval = Service_AddReferences_single(server, &adminSession, &item);
+    UA_RCU_UNLOCK();
+    return retval;
 }
 
 static UA_AddNodesResult
@@ -212,8 +215,10 @@ addNodeInternal(UA_Server *server, UA_Node *node, const UA_NodeId parentNodeId,
                 const UA_NodeId referenceTypeId) {
     UA_AddNodesResult res;
     UA_AddNodesResult_init(&res);
+    UA_RCU_LOCK();
     UA_Server_addExistingNode(server, &adminSession, node, &parentNodeId,
                               &referenceTypeId, &res);
+    UA_RCU_UNLOCK();
     return res;
 }
 
@@ -254,13 +259,14 @@ __UA_Server_addNode(UA_Server *server, const UA_NodeClass nodeClass,
 /* The server needs to be stopped before it can be deleted */
 void UA_Server_delete(UA_Server *server) {
     // Delete the timed work
-    UA_RCU_LOCK();
     UA_Server_deleteAllRepeatedJobs(server);
 
     // Delete all internal data
     UA_SecureChannelManager_deleteMembers(&server->secureChannelManager);
     UA_SessionManager_deleteMembers(&server->sessionManager, server);
+    UA_RCU_LOCK();
     UA_NodeStore_delete(server->nodestore);
+    UA_RCU_UNLOCK();
 #ifdef UA_ENABLE_EXTERNAL_NAMESPACES
     UA_Server_deleteExternalNamespaces(server);
 #endif
@@ -268,11 +274,8 @@ void UA_Server_delete(UA_Server *server) {
     UA_Array_delete(server->endpointDescriptions, server->endpointDescriptionsSize,
                     &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
 
-    UA_RCU_UNLOCK();
 #ifdef UA_ENABLE_MULTITHREADING
     pthread_cond_destroy(&server->dispatchQueue_condition);
-    rcu_barrier(); // wait for all scheduled call_rcu work to complete
-   	rcu_unregister_thread();
 #endif
     UA_free(server);
 }
@@ -363,7 +366,7 @@ static void copyNames(UA_Node *node, char *name) {
 
 static void
 addDataTypeNode(UA_Server *server, char* name, UA_UInt32 datatypeid, UA_Int32 parent) {
-    UA_DataTypeNode *datatype = UA_DataTypeNode_new();
+    UA_DataTypeNode *datatype = UA_NodeStore_newDataTypeNode();
     copyNames((UA_Node*)datatype, name);
     datatype->nodeId.identifier.numeric = datatypeid;
     addNodeInternal(server, (UA_Node*)datatype, UA_NODEID_NUMERIC(0, parent), nodeIdOrganizes);
@@ -372,7 +375,7 @@ addDataTypeNode(UA_Server *server, char* name, UA_UInt32 datatypeid, UA_Int32 pa
 static void
 addObjectTypeNode(UA_Server *server, char* name, UA_UInt32 objecttypeid,
                   UA_Int32 parent, UA_Int32 parentreference) {
-    UA_ObjectTypeNode *objecttype = UA_ObjectTypeNode_new();
+    UA_ObjectTypeNode *objecttype = UA_NodeStore_newObjectTypeNode();
     copyNames((UA_Node*)objecttype, name);
     objecttype->nodeId.identifier.numeric = objecttypeid;
     addNodeInternal(server, (UA_Node*)objecttype, UA_NODEID_NUMERIC(0, parent),
@@ -382,7 +385,7 @@ addObjectTypeNode(UA_Server *server, char* name, UA_UInt32 objecttypeid,
 static UA_VariableTypeNode*
 createVariableTypeNode(UA_Server *server, char* name, UA_UInt32 variabletypeid,
                        UA_Int32 parent, UA_Boolean abstract) {
-    UA_VariableTypeNode *variabletype = UA_VariableTypeNode_new();
+    UA_VariableTypeNode *variabletype = UA_NodeStore_newVariableTypeNode();
     copyNames((UA_Node*)variabletype, name);
     variabletype->nodeId.identifier.numeric = variabletypeid;
     variabletype->isAbstract = abstract;
@@ -411,14 +414,13 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
         return NULL;
 
     server->config = config;
-
+    server->nodestore = UA_NodeStore_new();
     LIST_INIT(&server->repeatedJobs);
+
 #ifdef UA_ENABLE_MULTITHREADING
     rcu_init();
-   	rcu_register_thread();
     cds_wfcq_init(&server->dispatchQueue_head, &server->dispatchQueue_tail);
     cds_lfs_init(&server->mainLoopJobs);
-    UA_RCU_LOCK();
 #endif
 
     /* uncomment for non-reproducible server runs */
@@ -483,8 +485,6 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
 #define STARTSESSIONID 1
     UA_SessionManager_init(&server->sessionManager, MAXSESSIONCOUNT, MAXSESSIONLIFETIME, STARTSESSIONID);
 
-    server->nodestore = UA_NodeStore_new();
-
     UA_Job cleanup = {.type = UA_JOBTYPE_METHODCALL,
                       .job.methodCall = {.method = UA_Server_cleanup, .data = NULL} };
     UA_Server_addRepeatedJob(server, cleanup, 10000, NULL);
@@ -500,26 +500,30 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     /**************/
 #ifndef UA_ENABLE_GENERATE_NAMESPACE0
     /* Bootstrap by manually inserting "references" and "hassubtype" */
-    UA_ReferenceTypeNode *references = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *references = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)references, "References");
     references->nodeId.identifier.numeric = UA_NS0ID_REFERENCES;
     references->isAbstract = UA_TRUE;
     references->symmetric = UA_TRUE;
     references->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "References");
     /* The reference to root is later inserted */
-    UA_NodeStore_insert(server->nodestore, (UA_Node*)references, NULL);
+    UA_RCU_LOCK();
+    UA_NodeStore_insert(server->nodestore, (UA_Node*)references);
+    UA_RCU_UNLOCK();
 
-    UA_ReferenceTypeNode *hassubtype = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hassubtype = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hassubtype, "HasSubtype");
     hassubtype->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "HasSupertype");
     hassubtype->nodeId.identifier.numeric = UA_NS0ID_HASSUBTYPE;
     hassubtype->isAbstract = UA_FALSE;
     hassubtype->symmetric = UA_FALSE;
     /* The reference to root is later inserted */
-    UA_NodeStore_insert(server->nodestore, (UA_Node*)hassubtype, NULL);
+    UA_RCU_LOCK();
+    UA_NodeStore_insert(server->nodestore, (UA_Node*)hassubtype);
+    UA_RCU_UNLOCK();
 
     /* Continue adding reference types with normal "addnode" */
-    UA_ReferenceTypeNode *hierarchicalreferences = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hierarchicalreferences = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hierarchicalreferences, "Hierarchicalreferences");
     hierarchicalreferences->nodeId.identifier.numeric = UA_NS0ID_HIERARCHICALREFERENCES;
     hierarchicalreferences->isAbstract = UA_TRUE;
@@ -527,7 +531,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternal(server, (UA_Node*)hierarchicalreferences,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCES), nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *nonhierarchicalreferences = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *nonhierarchicalreferences = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)nonhierarchicalreferences, "NonHierarchicalReferences");
     nonhierarchicalreferences->nodeId.identifier.numeric = UA_NS0ID_NONHIERARCHICALREFERENCES;
     nonhierarchicalreferences->isAbstract = UA_TRUE;
@@ -535,7 +539,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternal(server, (UA_Node*)nonhierarchicalreferences,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCES), nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *haschild = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *haschild = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)haschild, "HasChild");
     haschild->nodeId.identifier.numeric = UA_NS0ID_HASCHILD;
     haschild->isAbstract = UA_TRUE;
@@ -543,7 +547,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternal(server, (UA_Node*)haschild,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES), nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *organizes = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *organizes = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)organizes, "Organizes");
     organizes->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OrganizedBy");
     organizes->nodeId.identifier.numeric = UA_NS0ID_ORGANIZES;
@@ -552,7 +556,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternal(server, (UA_Node*)organizes,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES), nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *haseventsource = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *haseventsource = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)haseventsource, "HasEventSource");
     haseventsource->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "EventSourceOf");
     haseventsource->nodeId.identifier.numeric = UA_NS0ID_HASEVENTSOURCE;
@@ -561,7 +565,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternal(server, (UA_Node*)haseventsource,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES), nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *hasmodellingrule = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hasmodellingrule = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hasmodellingrule, "HasModellingRule");
     hasmodellingrule->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "ModellingRuleOf");
     hasmodellingrule->nodeId.identifier.numeric = UA_NS0ID_HASMODELLINGRULE;
@@ -569,7 +573,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hasmodellingrule->symmetric  = UA_FALSE;
     addNodeInternal(server, (UA_Node*)hasmodellingrule, nodeIdNonHierarchicalReferences, nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *hasencoding = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hasencoding = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hasencoding, "HasEncoding");
     hasencoding->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "EncodingOf");
     hasencoding->nodeId.identifier.numeric = UA_NS0ID_HASENCODING;
@@ -577,7 +581,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hasencoding->symmetric  = UA_FALSE;
     addNodeInternal(server, (UA_Node*)hasencoding, nodeIdNonHierarchicalReferences, nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *hasdescription = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hasdescription = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hasdescription, "HasDescription");
     hasdescription->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "DescriptionOf");
     hasdescription->nodeId.identifier.numeric = UA_NS0ID_HASDESCRIPTION;
@@ -585,7 +589,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hasdescription->symmetric  = UA_FALSE;
     addNodeInternal(server, (UA_Node*)hasdescription, nodeIdNonHierarchicalReferences, nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *hastypedefinition = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hastypedefinition = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hastypedefinition, "HasTypeDefinition");
     hastypedefinition->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "TypeDefinitionOf");
     hastypedefinition->nodeId.identifier.numeric = UA_NS0ID_HASTYPEDEFINITION;
@@ -593,7 +597,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hastypedefinition->symmetric  = UA_FALSE;
     addNodeInternal(server, (UA_Node*)hastypedefinition, nodeIdNonHierarchicalReferences, nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *generatesevent = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *generatesevent = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)generatesevent, "GeneratesEvent");
     generatesevent->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "GeneratedBy");
     generatesevent->nodeId.identifier.numeric = UA_NS0ID_GENERATESEVENT;
@@ -602,7 +606,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternal(server, (UA_Node*)generatesevent, nodeIdNonHierarchicalReferences,
                     nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *aggregates = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *aggregates = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)aggregates, "Aggregates");
     // Todo: Is there an inverse name?
     aggregates->nodeId.identifier.numeric = UA_NS0ID_AGGREGATES;
@@ -615,7 +619,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCHILD), nodeIdHasSubType,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE), UA_TRUE);
 
-    UA_ReferenceTypeNode *hasproperty = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hasproperty = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hasproperty, "HasProperty");
     hasproperty->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "PropertyOf");
     hasproperty->nodeId.identifier.numeric = UA_NS0ID_HASPROPERTY;
@@ -624,7 +628,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternal(server, (UA_Node*)hasproperty,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES), nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *hascomponent = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hascomponent = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hascomponent, "HasComponent");
     hascomponent->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "ComponentOf");
     hascomponent->nodeId.identifier.numeric = UA_NS0ID_HASCOMPONENT;
@@ -633,7 +637,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternal(server, (UA_Node*)hascomponent,
                     UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES), nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *hasnotifier = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hasnotifier = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hasnotifier, "HasNotifier");
     hasnotifier->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "NotifierOf");
     hasnotifier->nodeId.identifier.numeric = UA_NS0ID_HASNOTIFIER;
@@ -642,7 +646,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternal(server, (UA_Node*)hasnotifier, UA_NODEID_NUMERIC(0, UA_NS0ID_HASEVENTSOURCE),
                     nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *hasorderedcomponent = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hasorderedcomponent = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hasorderedcomponent, "HasOrderedComponent");
     hasorderedcomponent->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OrderedComponentOf");
     hasorderedcomponent->nodeId.identifier.numeric = UA_NS0ID_HASORDEREDCOMPONENT;
@@ -651,7 +655,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addNodeInternal(server, (UA_Node*)hasorderedcomponent, UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
                     nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *hasmodelparent = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hasmodelparent = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hasmodelparent, "HasModelParent");
     hasmodelparent->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "ModelParentOf");
     hasmodelparent->nodeId.identifier.numeric = UA_NS0ID_HASMODELPARENT;
@@ -659,7 +663,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hasmodelparent->symmetric  = UA_FALSE;
     addNodeInternal(server, (UA_Node*)hasmodelparent, nodeIdNonHierarchicalReferences, nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *fromstate = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *fromstate = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)fromstate, "FromState");
     fromstate->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "ToTransition");
     fromstate->nodeId.identifier.numeric = UA_NS0ID_FROMSTATE;
@@ -667,7 +671,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     fromstate->symmetric  = UA_FALSE;
     addNodeInternal(server, (UA_Node*)fromstate, nodeIdNonHierarchicalReferences, nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *tostate = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *tostate = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)tostate, "ToState");
     tostate->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "FromTransition");
     tostate->nodeId.identifier.numeric = UA_NS0ID_TOSTATE;
@@ -675,7 +679,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     tostate->symmetric  = UA_FALSE;
     addNodeInternal(server, (UA_Node*)tostate, nodeIdNonHierarchicalReferences, nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *hascause = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hascause = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hascause, "HasCause");
     hascause->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "MayBeCausedBy");
     hascause->nodeId.identifier.numeric = UA_NS0ID_HASCAUSE;
@@ -683,7 +687,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     hascause->symmetric  = UA_FALSE;
     addNodeInternal(server, (UA_Node*)hascause, nodeIdNonHierarchicalReferences, nodeIdHasSubType);
     
-    UA_ReferenceTypeNode *haseffect = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *haseffect = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)haseffect, "HasEffect");
     haseffect->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "MayBeEffectedBy");
     haseffect->nodeId.identifier.numeric = UA_NS0ID_HASEFFECT;
@@ -691,7 +695,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     haseffect->symmetric  = UA_FALSE;
     addNodeInternal(server, (UA_Node*)haseffect, nodeIdNonHierarchicalReferences, nodeIdHasSubType);
 
-    UA_ReferenceTypeNode *hashistoricalconfiguration = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *hashistoricalconfiguration = UA_NodeStore_newReferenceTypeNode();
     copyNames((UA_Node*)hashistoricalconfiguration, "HasHistoricalConfiguration");
     hashistoricalconfiguration->inverseName = UA_LOCALIZEDTEXT_ALLOC("en_US", "HistoricalConfigurationOf");
     hashistoricalconfiguration->nodeId.identifier.numeric = UA_NS0ID_HASHISTORICALCONFIGURATION;
@@ -704,30 +708,32 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     /* Basic Folders */
     /*****************/
 
-    UA_ObjectNode *root = UA_ObjectNode_new();
+    UA_ObjectNode *root = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)root, "Root");
     root->nodeId.identifier.numeric = UA_NS0ID_ROOTFOLDER;
-    UA_NodeStore_insert(server->nodestore, (UA_Node*)root, NULL);
+    UA_RCU_LOCK();
+    UA_NodeStore_insert(server->nodestore, (UA_Node*)root);
+    UA_RCU_UNLOCK();
 
-    UA_ObjectNode *objects = UA_ObjectNode_new();
+    UA_ObjectNode *objects = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)objects, "Objects");
     objects->nodeId.identifier.numeric = UA_NS0ID_OBJECTSFOLDER;
     addNodeInternal(server, (UA_Node*)objects, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
                     nodeIdOrganizes);
 
-    UA_ObjectNode *types = UA_ObjectNode_new();
+    UA_ObjectNode *types = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)types, "Types");
     types->nodeId.identifier.numeric = UA_NS0ID_TYPESFOLDER;
     addNodeInternal(server, (UA_Node*)types, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
                     nodeIdOrganizes);
 
-    UA_ObjectNode *views = UA_ObjectNode_new();
+    UA_ObjectNode *views = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)views, "Views");
     views->nodeId.identifier.numeric = UA_NS0ID_VIEWSFOLDER;
     addNodeInternal(server, (UA_Node*)views, UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
                     nodeIdOrganizes);
 
-    UA_ObjectNode *referencetypes = UA_ObjectNode_new();
+    UA_ObjectNode *referencetypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)referencetypes, "ReferenceTypes");
     referencetypes->nodeId.identifier.numeric = UA_NS0ID_REFERENCETYPESFOLDER;
     addNodeInternal(server, (UA_Node*)referencetypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
@@ -740,7 +746,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     /* Basic Object Types */
     /**********************/
 
-    UA_ObjectNode *objecttypes = UA_ObjectNode_new();
+    UA_ObjectNode *objecttypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)objecttypes, "ObjectTypes");
     objecttypes->nodeId.identifier.numeric = UA_NS0ID_OBJECTTYPESFOLDER;
     addNodeInternal(server, (UA_Node*)objecttypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
@@ -775,7 +781,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     /* Data Types */
     /**************/
 
-    UA_ObjectNode *datatypes = UA_ObjectNode_new();
+    UA_ObjectNode *datatypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)datatypes, "DataTypes");
     datatypes->nodeId.identifier.numeric = UA_NS0ID_DATATYPESFOLDER;
     addNodeInternal(server, (UA_Node*)datatypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER), nodeIdOrganizes);
@@ -815,7 +821,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addDataTypeNode(server, "Enumeration", UA_NS0ID_ENUMERATION, UA_NS0ID_BASEDATATYPE);
         addDataTypeNode(server, "ServerState", UA_NS0ID_SERVERSTATE, UA_NS0ID_ENUMERATION);
 
-    UA_ObjectNode *variabletypes = UA_ObjectNode_new();
+    UA_ObjectNode *variabletypes = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)variabletypes, "VariableTypes");
     variabletypes->nodeId.identifier.numeric = UA_NS0ID_VARIABLETYPESFOLDER;
     addNodeInternal(server, (UA_Node*)variabletypes, UA_NODEID_NUMERIC(0, UA_NS0ID_TYPESFOLDER),
@@ -839,7 +845,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     /* The Server Object */
     /*********************/
 
-    UA_ObjectNode *servernode = UA_ObjectNode_new();
+    UA_ObjectNode *servernode = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)servernode, "Server");
     servernode->nodeId.identifier.numeric = UA_NS0ID_SERVER;
     addNodeInternal(server, (UA_Node*)servernode, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
@@ -847,7 +853,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), nodeIdHasTypeDefinition,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERTYPE), UA_TRUE);
 
-    UA_VariableNode *namespaceArray = UA_VariableNode_new();
+    UA_VariableNode *namespaceArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)namespaceArray, "NamespaceArray");
     namespaceArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_NAMESPACEARRAY;
     namespaceArray->valueSource = UA_VALUESOURCE_DATASOURCE;
@@ -859,7 +865,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
                          nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), UA_TRUE);
 
-    UA_VariableNode *serverArray = UA_VariableNode_new();
+    UA_VariableNode *serverArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)serverArray, "ServerArray");
     serverArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERARRAY;
     UA_Variant_setArrayCopy(&serverArray->value.variant.value,
@@ -871,7 +877,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERARRAY), nodeIdHasTypeDefinition,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), UA_TRUE);
 
-    UA_ObjectNode *servercapablities = UA_ObjectNode_new();
+    UA_ObjectNode *servercapablities = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)servercapablities, "ServerCapabilities");
     servercapablities->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES;
     addNodeInternal(server, (UA_Node*)servercapablities, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
@@ -879,7 +885,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES), nodeIdHasTypeDefinition,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERCAPABILITIESTYPE), UA_TRUE);
 
-    UA_VariableNode *localeIdArray = UA_VariableNode_new();
+    UA_VariableNode *localeIdArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)localeIdArray, "LocaleIdArray");
     localeIdArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY;
     localeIdArray->value.variant.value.data = UA_Array_new(1, &UA_TYPES[UA_TYPES_STRING]);
@@ -893,7 +899,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_LOCALEIDARRAY),
                          nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), UA_TRUE);
 
-    UA_VariableNode *maxBrowseContinuationPoints = UA_VariableNode_new();
+    UA_VariableNode *maxBrowseContinuationPoints = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)maxBrowseContinuationPoints, "MaxBrowseContinuationPoints");
     maxBrowseContinuationPoints->nodeId.identifier.numeric =
         UA_NS0ID_SERVER_SERVERCAPABILITIES_MAXBROWSECONTINUATIONPOINTS;
@@ -922,7 +928,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/EmbeddedDataChangeSubscription");
 #endif
 
-    UA_VariableNode *serverProfileArray = UA_VariableNode_new();
+    UA_VariableNode *serverProfileArray = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)serverProfileArray, "ServerProfileArray");
     serverProfileArray->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY;
     serverProfileArray->value.variant.value.arrayLength = profileArraySize;
@@ -937,7 +943,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY),
                          nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), UA_TRUE);
 
-    UA_ObjectNode *serverdiagnostics = UA_ObjectNode_new();
+    UA_ObjectNode *serverdiagnostics = UA_NodeStore_newObjectNode();
     copyNames((UA_Node*)serverdiagnostics, "ServerDiagnostics");
     serverdiagnostics->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS;
     addNodeInternal(server, (UA_Node*)serverdiagnostics,
@@ -945,7 +951,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS),
                          nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERDIAGNOSTICSTYPE), UA_TRUE);
 
-    UA_VariableNode *enabledFlag = UA_VariableNode_new();
+    UA_VariableNode *enabledFlag = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)enabledFlag, "EnabledFlag");
     enabledFlag->nodeId.identifier.numeric = UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG;
     enabledFlag->value.variant.value.data = UA_Boolean_new(); //initialized as false
@@ -957,7 +963,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERDIAGNOSTICS_ENABLEDFLAG),
                          nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), UA_TRUE);
 
-    UA_VariableNode *serverstatus = UA_VariableNode_new();
+    UA_VariableNode *serverstatus = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)serverstatus, "ServerStatus");
     serverstatus->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS);
     serverstatus->valueSource = UA_VALUESOURCE_DATASOURCE;
@@ -966,7 +972,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasTypeDefinition,
                          UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_SERVERSTATUSTYPE), UA_TRUE);
 
-    UA_VariableNode *starttime = UA_VariableNode_new();
+    UA_VariableNode *starttime = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)starttime, "StartTime");
     starttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME);
     starttime->value.variant.value.storageType = UA_VARIANT_DATA_NODELETE;
@@ -977,7 +983,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STARTTIME),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
 
-    UA_VariableNode *currenttime = UA_VariableNode_new();
+    UA_VariableNode *currenttime = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)currenttime, "CurrentTime");
     currenttime->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
     currenttime->valueSource = UA_VALUESOURCE_DATASOURCE;
@@ -988,7 +994,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
 
-    UA_VariableNode *state = UA_VariableNode_new();
+    UA_VariableNode *state = UA_NodeStore_newVariableNode();
     UA_ServerState *stateEnum = UA_ServerState_new();
     *stateEnum = UA_SERVERSTATE_RUNNING;
     copyNames((UA_Node*)state, "State");
@@ -1001,7 +1007,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_STATE),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
 
-    UA_VariableNode *buildinfo = UA_VariableNode_new();
+    UA_VariableNode *buildinfo = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)buildinfo, "BuildInfo");
     buildinfo->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO);
     UA_Variant_setScalarCopy(&buildinfo->value.variant.value,
@@ -1012,7 +1018,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO),
                          nodeIdHasTypeDefinition, UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_BUILDINFOTYPE), UA_TRUE);
 
-    UA_VariableNode *producturi = UA_VariableNode_new();
+    UA_VariableNode *producturi = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)producturi, "ProductUri");
     producturi->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI);
     producturi->value.variant.value.data = UA_String_new();
@@ -1023,7 +1029,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTURI),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
 
-    UA_VariableNode *manufacturername = UA_VariableNode_new();
+    UA_VariableNode *manufacturername = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)manufacturername, "ManufacturererName");
     manufacturername->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME);
     UA_Variant_setScalarCopy(&manufacturername->value.variant.value,
@@ -1034,7 +1040,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_MANUFACTURERNAME),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
 
-    UA_VariableNode *productname = UA_VariableNode_new();
+    UA_VariableNode *productname = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)productname, "ProductName");
     productname->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME);
     UA_Variant_setScalarCopy(&productname->value.variant.value, &server->config.buildInfo.productName,
@@ -1044,7 +1050,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_PRODUCTNAME),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
 
-    UA_VariableNode *softwareversion = UA_VariableNode_new();
+    UA_VariableNode *softwareversion = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)softwareversion, "SoftwareVersion");
     softwareversion->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION);
     UA_Variant_setScalarCopy(&softwareversion->value.variant.value, &server->config.buildInfo.softwareVersion,
@@ -1054,7 +1060,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_SOFTWAREVERSION),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
 
-    UA_VariableNode *buildnumber = UA_VariableNode_new();
+    UA_VariableNode *buildnumber = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)buildnumber, "BuildNumber");
     buildnumber->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER);
     UA_Variant_setScalarCopy(&buildnumber->value.variant.value, &server->config.buildInfo.buildNumber,
@@ -1064,7 +1070,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
 
-    UA_VariableNode *builddate = UA_VariableNode_new();
+    UA_VariableNode *builddate = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)builddate, "BuildDate");
     builddate->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDDATE);
     UA_Variant_setScalarCopy(&builddate->value.variant.value, &server->config.buildInfo.buildDate,
@@ -1074,7 +1080,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_BUILDINFO_BUILDNUMBER),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
 
-    UA_VariableNode *secondstillshutdown = UA_VariableNode_new();
+    UA_VariableNode *secondstillshutdown = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)secondstillshutdown, "SecondsTillShutdown");
     secondstillshutdown->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN);
     secondstillshutdown->value.variant.value.data = UA_UInt32_new();
@@ -1084,7 +1090,7 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SECONDSTILLSHUTDOWN),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
 
-    UA_VariableNode *shutdownreason = UA_VariableNode_new();
+    UA_VariableNode *shutdownreason = UA_NodeStore_newVariableNode();
     copyNames((UA_Node*)shutdownreason, "ShutdownReason");
     shutdownreason->nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON);
     shutdownreason->value.variant.value.data = UA_LocalizedText_new();
@@ -1093,7 +1099,6 @@ UA_Server * UA_Server_new(const UA_ServerConfig config) {
                     UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS), nodeIdHasComponent);
     addReferenceInternal(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_SHUTDOWNREASON),
                          nodeIdHasTypeDefinition, expandedNodeIdBaseDataVariabletype, UA_TRUE);
-    UA_RCU_UNLOCK();
     return server;
 }
 
@@ -1114,7 +1119,9 @@ __UA_Server_write(UA_Server *server, const UA_NodeId *nodeId,
         wvalue.value.value = *(const UA_Variant*)value;
     }
     wvalue.value.hasValue = UA_TRUE;
+    UA_RCU_LOCK();
     UA_StatusCode retval = Service_Write_single(server, &adminSession, &wvalue);
+    UA_RCU_UNLOCK();
     return retval;
 }
 
@@ -1129,8 +1136,11 @@ setValueCallback(UA_Server *server, UA_Session *session, UA_VariableNode *node,
 UA_StatusCode UA_EXPORT
 UA_Server_setVariableNode_valueCallback(UA_Server *server, const UA_NodeId nodeId,
                                         const UA_ValueCallback callback) {
-    return UA_Server_editNode(server, &adminSession, &nodeId,
-                              (UA_EditNodeCallback)setValueCallback, &callback);
+    UA_RCU_LOCK();
+    UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
+                                              (UA_EditNodeCallback)setValueCallback, &callback);
+    UA_RCU_UNLOCK();
+    return retval;
 }
 
 static UA_StatusCode
@@ -1148,8 +1158,11 @@ setDataSource(UA_Server *server, UA_Session *session,
 UA_StatusCode
 UA_Server_setVariableNode_dataSource(UA_Server *server, const UA_NodeId nodeId,
                                      const UA_DataSource dataSource) {
-    return UA_Server_editNode(server, &adminSession, &nodeId,
-                              (UA_EditNodeCallback)setDataSource, &dataSource);
+    UA_RCU_LOCK();
+    UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
+                                              (UA_EditNodeCallback)setDataSource, &dataSource);
+    UA_RCU_UNLOCK();
+    return retval;
 }
 
 static UA_StatusCode
@@ -1164,8 +1177,11 @@ setObjectTypeLifecycleManagement(UA_Server *server, UA_Session *session, UA_Obje
 UA_StatusCode UA_EXPORT
 UA_Server_setObjectTypeNode_lifecycleManagement(UA_Server *server, UA_NodeId nodeId,
                                                 UA_ObjectLifecycleManagement olm) {
-    return UA_Server_editNode(server, &adminSession, &nodeId,
-                              (UA_EditNodeCallback)setObjectTypeLifecycleManagement, &olm);
+    UA_RCU_LOCK();
+    UA_StatusCode retval = UA_Server_editNode(server, &adminSession, &nodeId,
+                                              (UA_EditNodeCallback)setObjectTypeLifecycleManagement, &olm);
+    UA_RCU_UNLOCK();
+    return retval;
 }
 
 UA_StatusCode
@@ -1176,8 +1192,10 @@ __UA_Server_read(UA_Server *server, const UA_NodeId *nodeId, const UA_AttributeI
     item.attributeId = attributeId;
     UA_DataValue dv;
     UA_DataValue_init(&dv);
+    UA_RCU_LOCK();
     Service_Read_single(server, &adminSession, UA_TIMESTAMPSTORETURN_NEITHER,
                         &item, &dv);
+    UA_RCU_UNLOCK();
     UA_StatusCode retval = UA_STATUSCODE_GOOD;
     if(dv.hasStatus)
         retval = dv.hasStatus;
@@ -1203,7 +1221,9 @@ UA_BrowseResult
 UA_Server_browse(UA_Server *server, UA_UInt32 maxrefs, const UA_BrowseDescription *descr) {
     UA_BrowseResult result;
     UA_BrowseResult_init(&result);
+    UA_RCU_LOCK();
     Service_Browse_single(server, &adminSession, NULL, descr, maxrefs, &result);
+    UA_RCU_UNLOCK();
     return result;
 }
 
@@ -1212,8 +1232,10 @@ UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
                      const UA_ByteString *continuationPoint) {
     UA_BrowseResult result;
     UA_BrowseResult_init(&result);
+    UA_RCU_LOCK();
     UA_Server_browseNext_single(server, &adminSession, releaseContinuationPoint,
                                 continuationPoint, &result);
+    UA_RCU_UNLOCK();
     return result;
 }
 
@@ -1221,7 +1243,9 @@ UA_Server_browseNext(UA_Server *server, UA_Boolean releaseContinuationPoint,
 UA_CallMethodResult UA_Server_call(UA_Server *server, const UA_CallMethodRequest *request) {
     UA_CallMethodResult result;
     UA_CallMethodResult_init(&result);
+    UA_RCU_LOCK();
     Service_Call_single(server, &adminSession, request, &result);
+    UA_RCU_UNLOCK();
     return result;
 }
 #endif

+ 44 - 59
src/server/ua_server_worker.c

@@ -37,13 +37,10 @@
 #define MAXTIMEOUT 50000 // max timeout in microsec until the next main loop iteration
 #define BATCHSIZE 20 // max number of jobs that are dispatched at once to workers
 
-/**
- * server:		UA server context
- * jobs: 		pointer to array of jobs or NULL if jobsSize == -1
- * jobsSize: 	nr. of valid jobs or -1
-*/
-static void processJobs(UA_Server *server, UA_Job *jobs, UA_Int32 jobsSize) {
-    for (UA_Int32 i = 0; i < jobsSize; i++) {
+static void processJobs(UA_Server *server, UA_Job *jobs, size_t jobsSize) {
+    UA_ASSERT_RCU_UNLOCKED();
+    UA_RCU_LOCK();
+    for(size_t i = 0; i < jobsSize; i++) {
         UA_Job *job = &jobs[i];
         switch(job->type) {
         case UA_JOBTYPE_NOTHING:
@@ -72,6 +69,7 @@ static void processJobs(UA_Server *server, UA_Job *jobs, UA_Int32 jobsSize) {
             break;
         }
     }
+    UA_RCU_UNLOCK();
 }
 
 /*******************************/
@@ -92,34 +90,7 @@ struct DispatchJobsList {
     UA_Job *jobs;
 };
 
-/** Dispatch jobs to workers. Slices the job array up if it contains more than BATCHSIZE items. The jobs
-    array is freed in the worker threads. */
-static void
-dispatchJobs(UA_Server *server, UA_Job *jobs, size_t jobsSize) {
-    size_t startIndex = jobsSize; // start at the end
-    while(jobsSize > 0) {
-        size_t size = BATCHSIZE;
-        if(size > jobsSize)
-            size = jobsSize;
-        startIndex = startIndex - size;
-        struct DispatchJobsList *wln = UA_malloc(sizeof(struct DispatchJobsList));
-        if(startIndex > 0) {
-            wln->jobs = UA_malloc(size * sizeof(UA_Job));
-            memcpy(wln->jobs, &jobs[startIndex], size * sizeof(UA_Job));
-            wln->jobsSize = size;
-        } else {
-            /* forward the original array */
-            wln->jobsSize = size;
-            wln->jobs = jobs;
-        }
-        cds_wfcq_node_init(&wln->node);
-        cds_wfcq_enqueue(&server->dispatchQueue_head, &server->dispatchQueue_tail, &wln->node);
-        jobsSize -= size;
-    }
-}
-
-static void *
-workerLoop(UA_Worker *worker) {
+static void * workerLoop(UA_Worker *worker) {
     UA_Server *server = worker->server;
     UA_UInt32 *counter = &worker->counter;
     volatile UA_Boolean *running = &worker->running;
@@ -141,21 +112,45 @@ workerLoop(UA_Worker *worker) {
             pthread_cond_wait(&server->dispatchQueue_condition, &mutex);
             continue;
         }
-        UA_RCU_LOCK();
         processJobs(server, wln->jobs, wln->jobsSize);
         UA_free(wln->jobs);
         UA_free(wln);
-        UA_RCU_UNLOCK();
         uatomic_inc(counter);
     }
 
     pthread_mutex_unlock(&mutex);
     pthread_mutex_destroy(&mutex);
-    rcu_barrier();
+    UA_ASSERT_RCU_UNLOCKED();
+    rcu_barrier(); // wait for all scheduled call_rcu work to complete
    	rcu_unregister_thread();
     return NULL;
 }
 
+/** Dispatch jobs to workers. Slices the job array up if it contains more than
+    BATCHSIZE items. The jobs array is freed in the worker threads. */
+static void dispatchJobs(UA_Server *server, UA_Job *jobs, size_t jobsSize) {
+    size_t startIndex = jobsSize; // start at the end
+    while(jobsSize > 0) {
+        size_t size = BATCHSIZE;
+        if(size > jobsSize)
+            size = jobsSize;
+        startIndex = startIndex - size;
+        struct DispatchJobsList *wln = UA_malloc(sizeof(struct DispatchJobsList));
+        if(startIndex > 0) {
+            wln->jobs = UA_malloc(size * sizeof(UA_Job));
+            memcpy(wln->jobs, &jobs[startIndex], size * sizeof(UA_Job));
+            wln->jobsSize = size;
+        } else {
+            /* forward the original array */
+            wln->jobsSize = size;
+            wln->jobs = jobs;
+        }
+        cds_wfcq_node_init(&wln->node);
+        cds_wfcq_enqueue(&server->dispatchQueue_head, &server->dispatchQueue_tail, &wln->node);
+        jobsSize -= size;
+    }
+}
+
 static void
 emptyDispatchQueue(UA_Server *server) {
     while(!cds_wfcq_empty(&server->dispatchQueue_head, &server->dispatchQueue_tail)) {
@@ -567,11 +562,9 @@ UA_StatusCode UA_Server_run_startup(UA_Server *server) {
 
 UA_StatusCode UA_Server_run_iterate(UA_Server *server) {
 #ifdef UA_ENABLE_MULTITHREADING
-    /* Run Work in the main loop */
-    processMainLoopJobs(server);
+    processMainLoopJobs(server); /* Run work assigned for the main thread */
 #endif
-    /* Process repeated work */
-    UA_UInt16 timeout = processRepeatedJobs(server);
+    UA_UInt16 timeout = processRepeatedJobs(server); /* Process repeated work */
 
     /* Get work from the networklayer */
     for(size_t i = 0; i < server->config.networkLayersSize; i++) {
@@ -593,10 +586,9 @@ UA_StatusCode UA_Server_run_iterate(UA_Server *server) {
             jobs[k].type = UA_JOBTYPE_NOTHING;
         }
 
-        /* Dispatch work to the worker threads */
-        dispatchJobs(server, jobs, jobsSize);
+        dispatchJobs(server, jobs, jobsSize); /* Dispatch work to worker threads */
 
-        /* Trigger sleeping worker threads */
+        /* Wake up worker threads */
         if(jobsSize > 0)
             pthread_cond_broadcast(&server->dispatchQueue_condition);
 #else
@@ -605,17 +597,19 @@ UA_StatusCode UA_Server_run_iterate(UA_Server *server) {
             UA_free(jobs);
 #endif
     }
+
     return UA_STATUSCODE_GOOD;
 }
 
 UA_StatusCode UA_Server_run_shutdown(UA_Server *server) {
-    UA_Job *stopJobs;
     for(size_t i = 0; i < server->config.networkLayersSize; i++) {
         UA_ServerNetworkLayer *nl = &server->config.networkLayers[i];
+        UA_Job *stopJobs;
         size_t stopJobsSize = nl->stop(nl, &stopJobs);
         processJobs(server, stopJobs, stopJobsSize);
         UA_free(stopJobs);
     }
+
 #ifdef UA_ENABLE_MULTITHREADING
     UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
                 "Shutting down %u worker thread(s)", server->config.nThreads);
@@ -626,21 +620,12 @@ UA_StatusCode UA_Server_run_shutdown(UA_Server *server) {
     for(size_t i = 0; i < server->config.nThreads; i++)
         pthread_join(server->workers[i].thr, NULL);
     UA_free(server->workers);
-    UA_LOG_INFO(server->config.logger, UA_LOGCATEGORY_SERVER,
-                "Workers shut down");
 
-    /* Manually finish the work still enqueued */
+    /* Manually finish the work still enqueued.
+       This especially contains delayed frees */
     emptyDispatchQueue(server);
-
-    /* Process the remaining delayed work */
-    struct DelayedJobs *dw = server->delayedJobs;
-    while(dw) {
-        processJobs(server, dw->jobs, dw->jobsCount);
-        struct DelayedJobs *next = dw->next;
-        UA_free(dw->workerCounters);
-        UA_free(dw);
-        dw = next;
-    }
+    UA_ASSERT_RCU_UNLOCKED();
+    rcu_barrier(); // wait for all scheduled call_rcu work to complete
 #endif
     return UA_STATUSCODE_GOOD;
 }

+ 7 - 9
src/server/ua_services_attribute.c

@@ -435,24 +435,23 @@ UA_StatusCode UA_Server_editNode(UA_Server *server, UA_Session *session, const U
                                  UA_EditNodeCallback callback, const void *data) {
     UA_StatusCode retval;
     do {
-        UA_MT_CONST UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
+#ifndef UA_ENABLE_MULTITHREADING
+        const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
         if(!node)
             return UA_STATUSCODE_BADNODEIDUNKNOWN;
-#ifndef UA_ENABLE_MULTITHREADING
-        retval = callback(server, session, node, data);
+        UA_Node *editNode = (UA_Node*)(uintptr_t)node; // dirty cast. use only here.
+        retval = callback(server, session, editNode, data);
         return retval;
 #else
-        UA_Node *copy = UA_Node_copyAnyNodeClass(node);
+        UA_Node *copy = UA_NodeStore_getCopy(server->nodestore, nodeId);
         if(!copy)
             return UA_STATUSCODE_BADOUTOFMEMORY;
         retval = callback(server, session, copy, data);
         if(retval != UA_STATUSCODE_GOOD) {
-            UA_Node_deleteAnyNodeClass(copy);
+            UA_NodeStore_deleteNode(copy);
             return retval;
         }
-        retval = UA_NodeStore_replace(server->nodestore, node, copy, NULL);
-        if(retval != UA_STATUSCODE_GOOD)
-            UA_Node_deleteAnyNodeClass(copy);
+        retval = UA_NodeStore_replace(server->nodestore, copy);
 #endif
     } while(retval != UA_STATUSCODE_GOOD);
     return UA_STATUSCODE_GOOD;
@@ -514,7 +513,6 @@ static enum type_equivalence typeEquivalence(const UA_DataType *type) {
     return TYPE_EQUIVALENCE_NONE;
 }
 
-/* In the multithreaded case, node is a copy */
 static UA_StatusCode
 CopyValueIntoNode(UA_VariableNode *node, const UA_WriteValue *wvalue) {
     UA_assert(wvalue->attributeId == UA_ATTRIBUTEID_VALUE);

+ 3 - 1
src/server/ua_services_discovery.c

@@ -95,8 +95,10 @@ void Service_GetEndpoints(UA_Server *server, UA_Session *session, const UA_GetEn
         if(!relevant_endpoints[j])
             continue;
         retval = UA_EndpointDescription_copy(&server->endpointDescriptions[j], &response->endpoints[k]);
+        if(retval != UA_STATUSCODE_GOOD)
+            break;
         UA_String_deleteMembers(&response->endpoints[k].endpointUrl);
-        retval |= UA_String_copy(&request->endpointUrl, &response->endpoints[k].endpointUrl);
+        retval = UA_String_copy(&request->endpointUrl, &response->endpoints[k].endpointUrl);
         k++;
     }
 

+ 42 - 72
src/server/ua_services_nodemanagement.c

@@ -44,33 +44,16 @@ UA_Server_addExistingNode(UA_Server *server, UA_Session *session, UA_Node *node,
 
     // todo: test if the referencetype is hierarchical
     // todo: namespace index is assumed to be valid
-    UA_MT_CONST UA_Node *managed = NULL;
-    UA_NodeId tempNodeid = node->nodeId;
-    tempNodeid.namespaceIndex = 0;
-    if(UA_NodeId_isNull(&tempNodeid)) {
-        if(UA_NodeStore_insert(server->nodestore, node, &managed) != UA_STATUSCODE_GOOD) {
-            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
-            return;
-        }
-        result->addedNodeId = managed->nodeId; // cannot fail as unique nodeids are numeric
-    } else {
-        if(UA_NodeId_copy(&node->nodeId, &result->addedNodeId) != UA_STATUSCODE_GOOD) {
-            result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
-            return;
-        }
-
-        if(UA_NodeStore_insert(server->nodestore, node, &managed) != UA_STATUSCODE_GOOD) {
-            result->statusCode = UA_STATUSCODE_BADNODEIDEXISTS;
-            UA_NodeId_deleteMembers(&result->addedNodeId);
-            return;
-        }
-    }
-    /* Careful. The node is inserted. If the nodestore makes an expand, nodes change their address */
+    result->statusCode = UA_NodeStore_insert(server->nodestore, node);
+    if(result->statusCode == UA_STATUSCODE_GOOD)
+        result->statusCode = UA_NodeId_copy(&node->nodeId, &result->addedNodeId);
+    else
+        return;
     
     // reference back to the parent
     UA_AddReferencesItem item;
     UA_AddReferencesItem_init(&item);
-    item.sourceNodeId = managed->nodeId;
+    item.sourceNodeId = node->nodeId;
     item.referenceTypeId = *referenceTypeId;
     item.isForward = UA_FALSE;
     item.targetNodeId.nodeId = *parentNodeId;
@@ -91,16 +74,14 @@ instantiateObjectNode(UA_Server *server, UA_Session *session,
    variable for all hastypedefinitions of the original version. */
 static UA_StatusCode
 copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *variable,
-                     const UA_NodeId *referenceType, const UA_NodeId *parent, UA_InstantiationCallback *instantiationCallback) {
-    UA_VariableNode *node = UA_VariableNode_new();
-    const UA_VariableNode *storednode = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, variable);
-    if(!storednode)
+                     const UA_NodeId *referenceType, const UA_NodeId *parent,
+                     UA_InstantiationCallback *instantiationCallback) {
+    const UA_VariableNode *node = (const UA_VariableNode*)UA_NodeStore_get(server->nodestore, variable);
+    if(!node)
         return UA_STATUSCODE_BADNODEIDINVALID;
-    if(storednode->nodeClass != UA_NODECLASS_VARIABLE)
+    if(node->nodeClass != UA_NODECLASS_VARIABLE)
         return UA_STATUSCODE_BADNODECLASSINVALID;
     
-    UA_VariableNode_copy(storednode, node);
-    
     // copy the variable attributes
     UA_VariableAttributes attr;
     UA_VariableAttributes_init(&attr);
@@ -151,7 +132,6 @@ copyExistingVariable(UA_Server *server, UA_Session *session, const UA_NodeId *va
       instantiationCallback->method(res.addedNodeId, node->nodeId, instantiationCallback->handle);
     
     UA_AddNodesResult_deleteMembers(&res);
-    UA_VariableNode_delete(node);
     return UA_STATUSCODE_GOOD;
 }
 
@@ -161,13 +141,11 @@ static UA_StatusCode
 copyExistingObject(UA_Server *server, UA_Session *session, const UA_NodeId *variable,
                    const UA_NodeId *referenceType, const UA_NodeId *parent, 
                    UA_InstantiationCallback *instantiationCallback) {
-    UA_ObjectNode *node = UA_ObjectNode_new();
-    const UA_ObjectNode *storednode = (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, variable);  
+    const UA_ObjectNode *node = (const UA_ObjectNode*)UA_NodeStore_get(server->nodestore, variable);  
     if(!node)
         return UA_STATUSCODE_BADNODEIDINVALID;
     if(node->nodeClass != UA_NODECLASS_OBJECT)
         return UA_STATUSCODE_BADNODECLASSINVALID;
-    UA_ObjectNode_copy(storednode, node);
     
     // copy the variable attributes
     UA_ObjectAttributes attr;
@@ -211,7 +189,6 @@ copyExistingObject(UA_Server *server, UA_Session *session, const UA_NodeId *vari
       instantiationCallback->method(res.addedNodeId, node->nodeId, instantiationCallback->handle);
     
     UA_AddNodesResult_deleteMembers(&res);
-    UA_ObjectNode_delete(node);
     return UA_STATUSCODE_GOOD;
 }
 
@@ -227,13 +204,11 @@ static UA_StatusCode
 instantiateObjectNode(UA_Server *server, UA_Session *session,
                       const UA_NodeId *nodeId, const UA_NodeId *typeId, 
                       UA_InstantiationCallback *instantiationCallback) {   
-    UA_ObjectTypeNode *type = UA_ObjectTypeNode_new();
-    const UA_ObjectTypeNode *storedtype = (const UA_ObjectTypeNode*)UA_NodeStore_get(server->nodestore, typeId);
-    if(!storedtype)
+    const UA_ObjectTypeNode *type = (const UA_ObjectTypeNode*)UA_NodeStore_get(server->nodestore, typeId);
+    if(!type)
       return UA_STATUSCODE_BADNODEIDINVALID;
-    if(storedtype->nodeClass != UA_NODECLASS_OBJECTTYPE)
+    if(type->nodeClass != UA_NODECLASS_OBJECTTYPE)
       return UA_STATUSCODE_BADNODECLASSINVALID;
-    UA_ObjectTypeNode_copy(storedtype, type);
     
     /* Add all the child nodes */
     UA_BrowseDescription browseChildren;
@@ -283,8 +258,6 @@ instantiateObjectNode(UA_Server *server, UA_Session *session,
     if(olm->constructor)
         UA_Server_editNode(server, session, nodeId,
                            (UA_EditNodeCallback)setObjectInstanceHandle, olm->constructor(*nodeId));
-    
-    UA_ObjectTypeNode_delete(type);
     return UA_STATUSCODE_GOOD;
 }
 
@@ -345,7 +318,7 @@ copyStandardAttributes(UA_Node *node, const UA_AddNodesItem *item, const UA_Node
 
 static UA_Node *
 variableNodeFromAttributes(const UA_AddNodesItem *item, const UA_VariableAttributes *attr) {
-    UA_VariableNode *vnode = UA_VariableNode_new();
+    UA_VariableNode *vnode = UA_NodeStore_newVariableNode();
     if(!vnode)
         return NULL;
     UA_StatusCode retval = copyStandardAttributes((UA_Node*)vnode, item, (const UA_NodeAttributes*)attr);
@@ -357,7 +330,7 @@ variableNodeFromAttributes(const UA_AddNodesItem *item, const UA_VariableAttribu
     vnode->valueRank = attr->valueRank;
     retval |= UA_Variant_copy(&attr->value, &vnode->value.variant.value);
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Node_deleteAnyNodeClass((UA_Node*)vnode);
+        UA_NodeStore_deleteNode((UA_Node*)vnode);
         return NULL;
     }
     return (UA_Node*)vnode;
@@ -365,13 +338,13 @@ variableNodeFromAttributes(const UA_AddNodesItem *item, const UA_VariableAttribu
 
 static UA_Node *
 objectNodeFromAttributes(const UA_AddNodesItem *item, const UA_ObjectAttributes *attr) {
-    UA_ObjectNode *onode = UA_ObjectNode_new();
+    UA_ObjectNode *onode = UA_NodeStore_newObjectNode();
     if(!onode)
         return NULL;
     UA_StatusCode retval = copyStandardAttributes((UA_Node*)onode, item, (const UA_NodeAttributes*)attr);
     onode->eventNotifier = attr->eventNotifier;
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Node_deleteAnyNodeClass((UA_Node*)onode);
+        UA_NodeStore_deleteNode((UA_Node*)onode);
         return NULL;
     }
     return (UA_Node*)onode;
@@ -379,7 +352,7 @@ objectNodeFromAttributes(const UA_AddNodesItem *item, const UA_ObjectAttributes
 
 static UA_Node *
 referenceTypeNodeFromAttributes(const UA_AddNodesItem *item, const UA_ReferenceTypeAttributes *attr) {
-    UA_ReferenceTypeNode *rtnode = UA_ReferenceTypeNode_new();
+    UA_ReferenceTypeNode *rtnode = UA_NodeStore_newReferenceTypeNode();
     if(!rtnode)
         return NULL;
     UA_StatusCode retval = copyStandardAttributes((UA_Node*)rtnode, item, (const UA_NodeAttributes*)attr);
@@ -387,7 +360,7 @@ referenceTypeNodeFromAttributes(const UA_AddNodesItem *item, const UA_ReferenceT
     rtnode->symmetric = attr->symmetric;
     retval |= UA_LocalizedText_copy(&attr->inverseName, &rtnode->inverseName);
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Node_deleteAnyNodeClass((UA_Node*)rtnode);
+        UA_NodeStore_deleteNode((UA_Node*)rtnode);
         return NULL;
     }
     return (UA_Node*)rtnode;
@@ -395,13 +368,13 @@ referenceTypeNodeFromAttributes(const UA_AddNodesItem *item, const UA_ReferenceT
 
 static UA_Node *
 objectTypeNodeFromAttributes(const UA_AddNodesItem *item, const UA_ObjectTypeAttributes *attr) {
-    UA_ObjectTypeNode *otnode = UA_ObjectTypeNode_new();
+    UA_ObjectTypeNode *otnode = UA_NodeStore_newObjectTypeNode();
     if(!otnode)
         return NULL;
     UA_StatusCode retval = copyStandardAttributes((UA_Node*)otnode, item, (const UA_NodeAttributes*)attr);
     otnode->isAbstract = attr->isAbstract;
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Node_deleteAnyNodeClass((UA_Node*)otnode);
+        UA_NodeStore_deleteNode((UA_Node*)otnode);
         return NULL;
     }
     return (UA_Node*)otnode;
@@ -409,7 +382,7 @@ objectTypeNodeFromAttributes(const UA_AddNodesItem *item, const UA_ObjectTypeAtt
 
 static UA_Node *
 variableTypeNodeFromAttributes(const UA_AddNodesItem *item, const UA_VariableTypeAttributes *attr) {
-    UA_VariableTypeNode *vtnode = UA_VariableTypeNode_new();
+    UA_VariableTypeNode *vtnode = UA_NodeStore_newVariableTypeNode();
     if(!vtnode)
         return NULL;
     UA_StatusCode retval = copyStandardAttributes((UA_Node*)vtnode, item, (const UA_NodeAttributes*)attr);
@@ -419,7 +392,7 @@ variableTypeNodeFromAttributes(const UA_AddNodesItem *item, const UA_VariableTyp
     // array dimensions are taken from the value
     vtnode->isAbstract = attr->isAbstract;
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Node_deleteAnyNodeClass((UA_Node*)vtnode);
+        UA_NodeStore_deleteNode((UA_Node*)vtnode);
         return NULL;
     }
     return (UA_Node*)vtnode;
@@ -427,14 +400,14 @@ variableTypeNodeFromAttributes(const UA_AddNodesItem *item, const UA_VariableTyp
 
 static UA_Node *
 viewNodeFromAttributes(const UA_AddNodesItem *item, const UA_ViewAttributes *attr) {
-    UA_ViewNode *vnode = UA_ViewNode_new();
+    UA_ViewNode *vnode = UA_NodeStore_newViewNode();
     if(!vnode)
         return NULL;
     UA_StatusCode retval = copyStandardAttributes((UA_Node*)vnode, item, (const UA_NodeAttributes*)attr);
     vnode->containsNoLoops = attr->containsNoLoops;
     vnode->eventNotifier = attr->eventNotifier;
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Node_deleteAnyNodeClass((UA_Node*)vnode);
+        UA_NodeStore_deleteNode((UA_Node*)vnode);
         return NULL;
     }
     return (UA_Node*)vnode;
@@ -442,13 +415,13 @@ viewNodeFromAttributes(const UA_AddNodesItem *item, const UA_ViewAttributes *att
 
 static UA_Node *
 dataTypeNodeFromAttributes(const UA_AddNodesItem *item, const UA_DataTypeAttributes *attr) {
-    UA_DataTypeNode *dtnode = UA_DataTypeNode_new();
+    UA_DataTypeNode *dtnode = UA_NodeStore_newDataTypeNode();
     if(!dtnode)
         return NULL;
     UA_StatusCode retval = copyStandardAttributes((UA_Node*)dtnode, item, (const UA_NodeAttributes*)attr);
     dtnode->isAbstract = attr->isAbstract;
     if(retval != UA_STATUSCODE_GOOD) {
-        UA_Node_deleteAnyNodeClass((UA_Node*)dtnode);
+        UA_NodeStore_deleteNode((UA_Node*)dtnode);
         return NULL;
     }
     return (UA_Node*)dtnode;
@@ -529,15 +502,14 @@ void Service_AddNodes_single(UA_Server *server, UA_Session *session, const UA_Ad
     /* add it to the server */
     UA_Server_addExistingNode(server, session, node, &item->parentNodeId.nodeId,
                               &item->referenceTypeId, result);
-    if(result->statusCode != UA_STATUSCODE_GOOD) {
-        UA_Node_deleteAnyNodeClass(node);
+    if(result->statusCode != UA_STATUSCODE_GOOD)
         return;
-    }
     
     /* instantiate if it has a type */
     if(!UA_NodeId_isNull(&item->typeDefinition.nodeId)) {
         if (instantiationCallback != NULL)
-          instantiationCallback->method(result->addedNodeId, item->typeDefinition.nodeId, instantiationCallback->handle); 
+          instantiationCallback->method(result->addedNodeId, item->typeDefinition.nodeId,
+                                        instantiationCallback->handle); 
         
         if(item->nodeClass == UA_NODECLASS_OBJECT)
             result->statusCode = instantiateObjectNode(server, session, &result->addedNodeId,
@@ -632,7 +604,7 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requested
         return result.statusCode;
     }
 
-    UA_VariableNode *node = UA_VariableNode_new();
+    UA_VariableNode *node = UA_NodeStore_newVariableNode();
     if(!node) {
         UA_AddNodesItem_deleteMembers(&item);
         UA_VariableAttributes_deleteMembers(&attrCopy);
@@ -647,15 +619,13 @@ UA_Server_addDataSourceVariableNode(UA_Server *server, const UA_NodeId requested
     node->historizing = attr.historizing;
     node->minimumSamplingInterval = attr.minimumSamplingInterval;
     node->valueRank = attr.valueRank;
-
+    UA_RCU_LOCK();
     UA_Server_addExistingNode(server, &adminSession, (UA_Node*)node, &item.parentNodeId.nodeId,
                               &item.referenceTypeId, &result);
+    UA_RCU_UNLOCK();
     UA_AddNodesItem_deleteMembers(&item);
     UA_VariableAttributes_deleteMembers(&attrCopy);
 
-    if(result.statusCode != UA_STATUSCODE_GOOD)
-        UA_VariableNode_delete(node);
-
     if(outNewNodeId && result.statusCode == UA_STATUSCODE_GOOD)
         *outNewNodeId = result.addedNodeId;
     else
@@ -691,7 +661,7 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
         return result.statusCode;
     }
 
-    UA_MethodNode *node = UA_MethodNode_new();
+    UA_MethodNode *node = UA_NodeStore_newMethodNode();
     if(!node) {
         result.statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
         UA_AddNodesItem_deleteMembers(&item);
@@ -707,12 +677,12 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
     UA_AddNodesItem_deleteMembers(&item);
     UA_MethodAttributes_deleteMembers(&attrCopy);
 
+    UA_RCU_LOCK();
     UA_Server_addExistingNode(server, &adminSession, (UA_Node*)node, &item.parentNodeId.nodeId,
                               &item.referenceTypeId, &result);
-    if(result.statusCode != UA_STATUSCODE_GOOD) {
-        UA_MethodNode_delete(node);
+    UA_RCU_UNLOCK();
+    if(result.statusCode != UA_STATUSCODE_GOOD)
         return result.statusCode;
-    }
     
     UA_ExpandedNodeId parent;
     UA_ExpandedNodeId_init(&parent);
@@ -729,7 +699,7 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
      */
     const UA_NodeId hasproperty = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
     if (inputArgumentsSize >= 0) {
-      UA_VariableNode *inputArgumentsVariableNode = UA_VariableNode_new();
+      UA_VariableNode *inputArgumentsVariableNode = UA_NodeStore_newVariableNode();
       inputArgumentsVariableNode->nodeId.namespaceIndex = result.addedNodeId.namespaceIndex;
       inputArgumentsVariableNode->browseName = UA_QUALIFIEDNAME_ALLOC(0,"InputArguments");
       inputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "InputArguments");
@@ -747,7 +717,7 @@ UA_Server_addMethodNode(UA_Server *server, const UA_NodeId requestedNewNodeId,
     /* create OutputArguments */
     /* FIXME:   See comment in inputArguments */
     if (outputArgumentsSize >= 0) {
-      UA_VariableNode *outputArgumentsVariableNode  = UA_VariableNode_new();
+      UA_VariableNode *outputArgumentsVariableNode  = UA_NodeStore_newVariableNode();
       outputArgumentsVariableNode->nodeId.namespaceIndex = result.addedNodeId.namespaceIndex;
       outputArgumentsVariableNode->browseName  = UA_QUALIFIEDNAME_ALLOC(0,"OutputArguments");
       outputArgumentsVariableNode->displayName = UA_LOCALIZEDTEXT_ALLOC("en_US", "OutputArguments");
@@ -877,7 +847,7 @@ void Service_AddReferences(UA_Server *server, UA_Session *session, const UA_AddR
 UA_StatusCode
 Service_DeleteNodes_single(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
                            UA_Boolean deleteReferences) {
-    UA_MT_CONST UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
+    const UA_Node *node = UA_NodeStore_get(server->nodestore, nodeId);
     if(!node)
         return UA_STATUSCODE_BADNODEIDINVALID;
     if(deleteReferences == UA_TRUE) {

+ 2 - 2
src/ua_securechannel.c

@@ -23,8 +23,8 @@ void UA_SecureChannel_deleteMembersCleanup(UA_SecureChannel *channel) {
     UA_ByteString_deleteMembers(&channel->serverNonce);
     UA_AsymmetricAlgorithmSecurityHeader_deleteMembers(&channel->clientAsymAlgSettings);
     UA_ByteString_deleteMembers(&channel->clientNonce);
-    UA_ChannelSecurityToken_deleteMembers(&channel->securityToken); //FIXME: not really needed
-    UA_ChannelSecurityToken_deleteMembers(&channel->nextSecurityToken); //FIXME: not really needed
+    UA_ChannelSecurityToken_deleteMembers(&channel->securityToken);
+    UA_ChannelSecurityToken_deleteMembers(&channel->nextSecurityToken);
     UA_Connection *c = channel->connection;
     if(c) {
         UA_Connection_detachSecureChannel(c);

+ 18 - 9
src/ua_util.h

@@ -44,6 +44,9 @@
 # endif
 #endif
 
+#define container_of(ptr, type, member) \
+    (type *)((uintptr_t)ptr - offsetof(type,member))
+
 /************************/
 /* Thread Local Storage */
 /************************/
@@ -96,22 +99,28 @@ int gettimeofday(struct timeval *tp, struct timezone *tzp);
 # include <urcu/rculfhash.h>
 # include <urcu/lfstack.h>
 # ifdef NDEBUG
-# define UA_RCU_LOCK() rcu_read_lock()
-# define UA_RCU_UNLOCK() rcu_read_unlock()
+#  define UA_RCU_LOCK() rcu_read_lock()
+#  define UA_RCU_UNLOCK() rcu_read_unlock()
+#  define UA_ASSERT_RCU_LOCKED()
+#  define UA_ASSERT_RCU_UNLOCKED()
 # else
-extern UA_THREAD_LOCAL bool rcu_locked;
-# define UA_RCU_LOCK() do {     \
-        assert(!rcu_locked);    \
-        rcu_locked = UA_TRUE;   \
+   extern UA_THREAD_LOCAL bool rcu_locked;
+#   define UA_ASSERT_RCU_LOCKED() assert(rcu_locked)
+#   define UA_ASSERT_RCU_UNLOCKED() assert(!rcu_locked)
+#   define UA_RCU_LOCK() do {                     \
+        UA_ASSERT_RCU_UNLOCKED();                 \
+        rcu_locked = UA_TRUE;                     \
         rcu_read_lock(); } while(0)
-# define UA_RCU_UNLOCK() do { \
-        assert(rcu_locked);   \
-        rcu_locked = UA_FALSE;    \
+#   define UA_RCU_UNLOCK() do {                   \
+        UA_ASSERT_RCU_LOCKED();                   \
+        rcu_locked = UA_FALSE;                    \
         rcu_read_lock(); } while(0)
 # endif
 #else
 # define UA_RCU_LOCK()
 # define UA_RCU_UNLOCK()
+# define UA_ASSERT_RCU_LOCKED()
+# define UA_ASSERT_RCU_UNLOCKED()
 #endif
 
 #endif /* UA_UTIL_H_ */

+ 53 - 48
tests/check_nodestore.c

@@ -24,7 +24,7 @@ static void printVisitor(const UA_Node* node) {
 }
 
 static UA_Node* createNode(UA_Int16 nsid, UA_Int32 id) {
-	UA_Node *p = (UA_Node *)UA_VariableNode_new();
+	UA_Node *p = (UA_Node *)UA_NodeStore_newVariableNode();
 	p->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
 	p->nodeId.namespaceIndex = nsid;
 	p->nodeId.identifier.numeric = id;
@@ -35,10 +35,10 @@ static UA_Node* createNode(UA_Int16 nsid, UA_Int32 id) {
 START_TEST(replaceExistingNode) {
 	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Node* n1 = createNode(0,2253);
-    UA_MT_CONST UA_Node *inserted;
-	UA_NodeStore_insert(ns, n1, &inserted);
-	UA_Node* n2 = createNode(0,2253);
-    UA_StatusCode retval = UA_NodeStore_replace(ns, inserted, n2, NULL);
+	UA_NodeStore_insert(ns, n1);
+    UA_NodeId in1 = UA_NODEID_NUMERIC(0, 2253);
+	UA_Node* n2 = UA_NodeStore_getCopy(ns, &in1);
+    UA_StatusCode retval = UA_NodeStore_replace(ns, n2);
     
 	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
     
@@ -46,16 +46,22 @@ START_TEST(replaceExistingNode) {
 }
 END_TEST
 
-START_TEST(replaceNonExistingNode) {
+START_TEST(replaceOldNode) {
 	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Node* n1 = createNode(0,2253);
-	UA_Node* n2 = createNode(0,2253);
-    UA_StatusCode retval = UA_NodeStore_replace(ns, n1, n2, NULL);
-    
+	UA_NodeStore_insert(ns, n1);
+    UA_NodeId in1 = UA_NODEID_NUMERIC(0,2253);
+	UA_Node* n2 = UA_NodeStore_getCopy(ns, &in1);
+	UA_Node* n3 = UA_NodeStore_getCopy(ns, &in1);
+
+    /* shall succeed */
+    UA_StatusCode retval = UA_NodeStore_replace(ns, n2);
+	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+
+    /* shall fail */
+    retval = UA_NodeStore_replace(ns, n3);
 	ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
     
-    UA_VariableNode_delete((UA_VariableNode*)n1);
-    UA_VariableNode_delete((UA_VariableNode*)n2);
 	UA_NodeStore_delete(ns);
 }
 END_TEST
@@ -66,12 +72,12 @@ START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
 #endif
 	// given
 	UA_NodeStore *ns = UA_NodeStore_new();
-    UA_MT_CONST UA_Node *inserted;
 	UA_Node* n1 = createNode(0,2253);
-	UA_NodeStore_insert(ns, n1, &inserted);
-	const UA_Node* nr = UA_NodeStore_get(ns,&inserted->nodeId);
+	UA_NodeStore_insert(ns, n1);
+    UA_NodeId in1 = UA_NODEID_NUMERIC(0,2253);
+	const UA_Node* nr = UA_NodeStore_get(ns, &in1);
 	// then
-	ck_assert_int_eq((uintptr_t)inserted, (uintptr_t)nr);
+	ck_assert_int_eq((uintptr_t)n1, (uintptr_t)nr);
 	// finally
 	UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
@@ -88,15 +94,14 @@ START_TEST(failToFindNodeInOtherUA_NodeStore) {
 	UA_NodeStore *ns = UA_NodeStore_new();
 
 	UA_Node* n1 = createNode(0,2255);
-    UA_NodeStore_insert(ns, n1, NULL);
+    UA_NodeStore_insert(ns, n1);
 
 	// when
-	UA_Node* n = createNode(1,2255);
-	const UA_Node* nr = UA_NodeStore_get(ns,&n->nodeId);
+	UA_NodeId in1 = UA_NODEID_NUMERIC(1, 2255);
+	const UA_Node* nr = UA_NodeStore_get(ns, &in1);
 	// then
 	ck_assert_int_eq((uintptr_t)nr, 0);
 	// finally
-	UA_VariableNode_delete((UA_VariableNode*)n);
 	UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
 	rcu_unregister_thread();
@@ -111,23 +116,23 @@ START_TEST(findNodeInUA_NodeStoreWithSeveralEntries) {
 	// given
 	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Node* n1 = createNode(0,2253);
-    UA_NodeStore_insert(ns, n1, NULL);
+    UA_NodeStore_insert(ns, n1);
 	UA_Node* n2 = createNode(0,2255);
-    UA_NodeStore_insert(ns, n2, NULL);
+    UA_NodeStore_insert(ns, n2);
 	UA_Node* n3 = createNode(0,2257);
-    UA_MT_CONST UA_Node *inserted;
-    UA_NodeStore_insert(ns, n3, &inserted);
+    UA_NodeStore_insert(ns, n3);
 	UA_Node* n4 = createNode(0,2200);
-    UA_NodeStore_insert(ns, n4, NULL);
+    UA_NodeStore_insert(ns, n4);
 	UA_Node* n5 = createNode(0,1);
-    UA_NodeStore_insert(ns, n5, NULL);
+    UA_NodeStore_insert(ns, n5);
 	UA_Node* n6 = createNode(0,12);
-    UA_NodeStore_insert(ns, n6, NULL);
+    UA_NodeStore_insert(ns, n6);
 
 	// when
-	const UA_Node* nr = UA_NodeStore_get(ns,&inserted->nodeId);
+    UA_NodeId in3 = UA_NODEID_NUMERIC(0, 2257);
+	const UA_Node* nr = UA_NodeStore_get(ns, &in3);
 	// then
-	ck_assert_int_eq((uintptr_t)nr, (uintptr_t)inserted);
+	ck_assert_int_eq((uintptr_t)nr, (uintptr_t)n3);
 	// finally
 	UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
@@ -143,17 +148,17 @@ START_TEST(iterateOverUA_NodeStoreShallNotVisitEmptyNodes) {
 	// given
 	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Node* n1 = createNode(0,2253);
-    UA_NodeStore_insert(ns, n1, NULL);
+    UA_NodeStore_insert(ns, n1);
 	UA_Node* n2 = createNode(0,2255);
-    UA_NodeStore_insert(ns, n2, NULL);
+    UA_NodeStore_insert(ns, n2);
 	UA_Node* n3 = createNode(0,2257);
-    UA_NodeStore_insert(ns, n3, NULL);
+    UA_NodeStore_insert(ns, n3);
 	UA_Node* n4 = createNode(0,2200);
-    UA_NodeStore_insert(ns, n4, NULL);
+    UA_NodeStore_insert(ns, n4);
 	UA_Node* n5 = createNode(0,1);
-    UA_NodeStore_insert(ns, n5, NULL);
+    UA_NodeStore_insert(ns, n5);
 	UA_Node* n6 = createNode(0,12);
-    UA_NodeStore_insert(ns, n6, NULL);
+    UA_NodeStore_insert(ns, n6);
 
 	// when
 	zeroCnt = 0;
@@ -180,7 +185,7 @@ START_TEST(findNodeInExpandedNamespace) {
 	UA_Int32 i=0;
 	for (; i<200; i++) {
 		n = createNode(0,i);
-        UA_NodeStore_insert(ns, n, NULL);
+        UA_NodeStore_insert(ns, n);
 	}
 	// when
 	UA_Node *n2 = createNode(0,25);
@@ -188,7 +193,7 @@ START_TEST(findNodeInExpandedNamespace) {
 	// then
 	ck_assert_int_eq(nr->nodeId.identifier.numeric,n2->nodeId.identifier.numeric);
 	// finally
-	UA_free((void*)n2);
+    UA_NodeStore_deleteNode(n2);
 	UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
 	rcu_unregister_thread();
@@ -206,7 +211,7 @@ START_TEST(iterateOverExpandedNamespaceShallNotVisitEmptyNodes) {
 	UA_Int32 i=0;
 	for (; i<200; i++) {
 		n = createNode(0,i);
-        UA_NodeStore_insert(ns, n, NULL);
+        UA_NodeStore_insert(ns, n);
 	}
 	// when
 	zeroCnt = 0;
@@ -230,23 +235,23 @@ START_TEST(failToFindNonExistantNodeInUA_NodeStoreWithSeveralEntries) {
 	// given
 	UA_NodeStore *ns = UA_NodeStore_new();
 	UA_Node* n1 = createNode(0,2253);
-    UA_NodeStore_insert(ns, n1, NULL);
+    UA_NodeStore_insert(ns, n1);
 	UA_Node* n2 = createNode(0,2255);
-    UA_NodeStore_insert(ns, n2, NULL);
+    UA_NodeStore_insert(ns, n2);
 	UA_Node* n3 = createNode(0,2257);
-    UA_NodeStore_insert(ns, n3, NULL);
+    UA_NodeStore_insert(ns, n3);
 	UA_Node* n4 = createNode(0,2200);
-    UA_NodeStore_insert(ns, n4, NULL);
+    UA_NodeStore_insert(ns, n4);
 	UA_Node* n5 = createNode(0,1);
-    UA_NodeStore_insert(ns, n5, NULL);
-	UA_Node* n6 = createNode(0,12); 
+    UA_NodeStore_insert(ns, n5);
+
+    UA_NodeId id = UA_NODEID_NUMERIC(0, 12);
 
 	// when
-	const UA_Node* nr = UA_NodeStore_get(ns, &n6->nodeId);
+	const UA_Node* nr = UA_NodeStore_get(ns, &id);
 	// then
 	ck_assert_int_eq((uintptr_t)nr, 0);
 	// finally
-	UA_free((void *)n6);
 	UA_NodeStore_delete(ns);
 #ifdef UA_ENABLE_MULTITHREADING
 	rcu_unregister_thread();
@@ -295,7 +300,7 @@ START_TEST(profileGetDelete) {
 	UA_Node *n;
 	for (int i=0; i<N; i++) {
 		n = createNode(0,i);
-        UA_NodeStore_insert(ns, n, NULL);
+        UA_NodeStore_insert(ns, n);
 	}
 	clock_t begin, end;
 	begin = clock();
@@ -345,7 +350,7 @@ static Suite * namespace_suite (void) {
 
 	TCase *tc_replace = tcase_create("Replace");
 	tcase_add_test (tc_replace, replaceExistingNode);
-	tcase_add_test (tc_replace, replaceNonExistingNode);
+	tcase_add_test (tc_replace, replaceOldNode);
 	suite_add_tcase (s, tc_replace);
 
 	TCase* tc_iterate = tcase_create ("Iterate");
@@ -365,7 +370,7 @@ int main (void) {
 	int number_failed = 0;
 	Suite *s = namespace_suite();
 	SRunner *sr = srunner_create(s);
-	//srunner_set_fork_status(sr,CK_NOFORK);
+	srunner_set_fork_status(sr,CK_NOFORK);
 	srunner_run_all(sr, CK_NORMAL);
 	number_failed += srunner_ntests_failed (sr);
 	srunner_free(sr);

+ 4 - 4
tests/check_services_attributes.c

@@ -122,7 +122,7 @@ static UA_Server* makeTestSequence(void) {
 }
 
 static UA_VariableNode* makeCompareSequence(void) {
-	UA_VariableNode *node = UA_VariableNode_new();
+	UA_VariableNode *node = UA_NodeStore_newVariableNode();
 
 	UA_Int32 myInteger = 42;
 	UA_Variant_setScalarCopy(&node->value.variant.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
@@ -262,7 +262,7 @@ START_TEST(ReadSingleAttributeDisplayNameWithoutTimestamp) {
     UA_Server_delete(server);
     UA_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
-    UA_VariableNode_delete(compNode);
+    UA_NodeStore_deleteNode((UA_Node*)compNode);
 } END_TEST
 
 START_TEST(ReadSingleAttributeDescriptionWithoutTimestamp) {
@@ -284,7 +284,7 @@ START_TEST(ReadSingleAttributeDescriptionWithoutTimestamp) {
     ck_assert(UA_String_equal(&compNode->description.text, &respval->text));
     UA_ReadRequest_deleteMembers(&rReq);
     UA_DataValue_deleteMembers(&resp);
-    UA_VariableNode_delete(compNode);
+    UA_NodeStore_deleteNode((UA_Node*)compNode);
     UA_Server_delete(server);
 } END_TEST
 
@@ -545,7 +545,7 @@ START_TEST(ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp) {
     ck_assert(*respval == comp);
     UA_DataValue_deleteMembers(&resp);
     UA_ReadRequest_deleteMembers(&rReq);
-    UA_VariableNode_delete(compNode);
+    UA_NodeStore_deleteNode((UA_Node*)compNode);
     UA_Server_delete(server);
 } END_TEST