Przeglądaj źródła

update concurrent nodestore

Julius Pfrommer 10 lat temu
rodzic
commit
bda9cd58f5

+ 18 - 27
CMakeLists.txt

@@ -106,8 +106,8 @@ if(ENABLE_JSON_ENCODING)
 endif(ENABLE_JSON_ENCODING)
 
 ## multithreading
-option(ENABLE_MULTITHREADING "Enable multithreading" OFF)
-if(ENABLE_MULTITHREADING)
+option(MULTITHREADING "Enable multithreading" OFF)
+if(MULTITHREADING)
     find_package(Threads REQUIRED)
     list(APPEND lib_sources src/server/ua_nodestore_concurrent.c)
 else()
@@ -133,9 +133,22 @@ if(ENABLE_COVERAGE)
     set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
 endif()
 
+# set the precompiler flags
 configure_file("src/ua_config.h.in" "${PROJECT_BINARY_DIR}/src_generated/ua_config.h")
 
-# build generated code
+# download queue.h if required
+if(WIN32)
+    if(NOT EXISTS "${PROJECT_BINARY_DIR}/src_generated/queue.h")
+        file(DOWNLOAD "http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/~checkout~/src/sys/sys/queue.h" "${PROJECT_BINARY_DIR}/src_generated/queue.h" STATUS result)
+        list(GET result 0 download_ok)
+        if(NOT ${download_ok} MATCHES 0)
+            file(REMOVE "${PROJECT_BINARY_DIR}/src_generated/queue.h") # remove empty file if created
+            message(FATAL_ERROR "queue.h could not be downloaded")
+        endif()
+    endif()
+endif(WIN32)
+
+# generate code from xml definitions
 file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/src_generated")
 include_directories("${PROJECT_BINARY_DIR}/src_generated") 
 add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_types_generated.c
@@ -174,7 +187,7 @@ option(EXAMPLESERVER "Build a test server" OFF)
 if(EXAMPLESERVER)
 set(server_sources examples/opcuaServer.c
                    examples/logger_stdout.c)
-if(NOT ENABLE_MULTITHREADING)
+if(NOT MULTITHREADING)
     list(APPEND server_sources examples/networklayer_tcp.c)
 else()
     list(APPEND server_sources examples/networklayer_tcp_concurrent.c)
@@ -184,7 +197,7 @@ target_link_libraries(exampleServer open62541)
 if(WIN32)
     target_link_libraries(exampleServer ws2_32)
 endif(WIN32)
-if(ENABLE_MULTITHREADING)
+if(MULTITHREADING)
     find_package(LibUV REQUIRED)
     target_link_libraries(exampleServer urcu-cds urcu uv)
 endif()
@@ -210,25 +223,3 @@ if(GENERATE_DOCUMENTATION)
                       WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
                       COMMENT "Generating API documentation with Doxygen")
 endif()
-
-# download queue.h if required
-if(WIN32)
-    if(NOT EXISTS "${PROJECT_BINARY_DIR}/src_generated/queue.h")
-        file(DOWNLOAD "http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/~checkout~/src/sys/sys/queue.h" "${PROJECT_BINARY_DIR}/src_generated/queue.h" STATUS result)
-        list(GET result 0 download_ok)
-        if(NOT ${download_ok} MATCHES 0)
-            file(REMOVE "${PROJECT_BINARY_DIR}/src_generated/queue.h") # remove empty file if created
-            message(FATAL_ERROR "queue.h could not be downloaded")
-        endif()
-    endif()
-endif(WIN32)
-
-# build api server specificaion
-#add_executable(api-design examples/api-design/server.c)
-#target_link_libraries(api-design open62541)
-#if(WIN32)
-#    target_link_libraries(api-design ws2_32)
-#endif(WIN32)
-#if(MULTITHREADING)
-#    target_link_libraries(api-design urcu-cds urcu)
-#endif(MULTITHREADING)

+ 10 - 0
examples/opcuaServer.c

@@ -16,6 +16,10 @@
 #include "logger_stdout.h"
 #include "networklayer_tcp.h"
 
+#ifdef MULTITHREADING
+#include <urcu.h>
+#endif
+
 UA_Boolean running = 1;
 
 void stopHandler(int sign) {
@@ -49,6 +53,9 @@ UA_ByteString loadCertificate() {
     return certificate;
 }
 int main(int argc, char** argv) {
+#ifdef MULTITHREADING
+    rcu_register_thread();
+#endif
 	signal(SIGINT, stopHandler); /* catches ctrl-c */
 
 	UA_String endpointUrl;
@@ -95,5 +102,8 @@ int main(int argc, char** argv) {
 	UA_Server_delete(server);
 	NetworklayerTCP_delete(nl);
     UA_String_deleteMembers(&endpointUrl);
+#ifdef MULTITHREADING
+    rcu_unregister_thread();
+#endif
 	return retval;
 }

+ 15 - 15
include/ua_types.h

@@ -68,8 +68,8 @@ typedef _Bool UA_Boolean;
 
 /** @brief An integer value between -129 and 127. */
 typedef int8_t UA_SByte;
-#define UA_SBYTE_MAX -128
-#define UA_SBYTE_MIN 127
+#define UA_SBYTE_MAX 127
+#define UA_SBYTE_MIN -128
 
 /** @brief An integer value between 0 and 256. */
 typedef uint8_t UA_Byte;
@@ -113,7 +113,7 @@ typedef float UA_Float;
 typedef double UA_Double;
 
 /** @brief A sequence of Unicode characters. */
-typedef struct UA_String {
+typedef struct {
     UA_Int32 length;
     UA_Byte *data;
 } UA_String;
@@ -122,7 +122,7 @@ typedef struct UA_String {
 typedef UA_Int64 UA_DateTime; //100 nanosecond resolution
 
 /** @brief A 16 byte value that can be used as a globally unique identifier. */
-typedef struct UA_Guid {
+typedef struct {
     UA_UInt32 data1;
     UA_UInt16 data2;
     UA_UInt16 data3;
@@ -130,14 +130,14 @@ typedef struct UA_Guid {
 } UA_Guid;
 
 /** @brief A sequence of octets. */
-typedef struct UA_String UA_ByteString;
+typedef UA_String UA_ByteString;
 
 /** @brief An XML element. */
-typedef struct UA_String UA_XmlElement;
+typedef UA_String UA_XmlElement;
 
 /** @brief An identifier for a node in the address space of an OPC UA Server. */
 /* The shortened numeric types are introduced during encoding. */
-typedef struct UA_NodeId {
+typedef struct {
     UA_UInt16 namespaceIndex;
     enum {
         UA_NODEIDTYPE_NUMERIC    = 2,
@@ -154,7 +154,7 @@ typedef struct UA_NodeId {
 } UA_NodeId;
 
 /** @brief A NodeId that allows the namespace URI to be specified instead of an index. */
-typedef struct UA_ExpandedNodeId {
+typedef struct {
     UA_NodeId nodeId;
     UA_String namespaceUri; // not encoded if length=-1
     UA_UInt32 serverIndex;  // not encoded if 0
@@ -165,20 +165,20 @@ typedef struct UA_ExpandedNodeId {
 typedef enum UA_StatusCode UA_StatusCode; // StatusCodes aren't an enum(=int) since 32 unsigned bits are needed. See also ua_statuscodes.h */
 
 /** @brief A name qualified by a namespace. */
-typedef struct UA_QualifiedName {
+typedef struct {
     UA_UInt16 namespaceIndex;
     UA_String name;
 } UA_QualifiedName;
 
 /** @brief Human readable text with an optional locale identifier. */
-typedef struct UA_LocalizedText {
+typedef struct {
     UA_String locale;
     UA_String text;
 } UA_LocalizedText;
 
 /** @brief A structure that contains an application specific data type that may
     not be recognized by the receiver. */
-typedef struct UA_ExtensionObject {
+typedef struct {
     UA_NodeId typeId;
     enum {
         UA_EXTENSIONOBJECT_ENCODINGMASK_NOBODYISENCODED  = 0,
@@ -193,7 +193,7 @@ typedef struct UA_TypeVTable UA_TypeVTable;
 
 /** @brief Pointers to data that is stored in memory. The "type" of the data is
     stored in the variant itself. */
-typedef struct UA_VariantData {
+typedef struct {
     UA_Int32  arrayLength;        // total number of elements in the data-pointer
     void     *dataPtr;
     UA_Int32  arrayDimensionsLength;
@@ -205,7 +205,7 @@ typedef struct UA_VariantData {
  *  Implementors of datasources need to provide functions for the callbacks in
  *  this structure. As a rule, datasources are never copied, but only their
  *  content. The only way to write into a datasource is via the write-service. */
-typedef struct UA_VariantDataSource {
+typedef struct {
     const void *identifier; /**< whatever id the datasource uses internally. Can be ignored if the datasource functions do not use it. */
     UA_Int32 (*read)(const void *identifier, const UA_VariantData **); /**< Get the current data from the datasource. When it is no longer used, the data has to be relased. */
     void (*release)(const void *identifier, const UA_VariantData *); /**< For concurrent access, the datasource needs to know when the last reader releases replaced/deleted data. Release decreases the reference count. */
@@ -216,7 +216,7 @@ typedef struct UA_VariantDataSource {
 /** @brief Variants store (arrays of) any data type. Either they provide a
     pointer to the data in memory, or functions from which the data can be
     accessed. */
-typedef struct UA_Variant {
+typedef struct {
     const UA_TypeVTable *vt; /// The VTable of the datatype in question
     enum {
         UA_VARIANT_DATA, ///< The data is stored in memory and "owned" by this variant
@@ -230,7 +230,7 @@ typedef struct UA_Variant {
 } UA_Variant;
 
 /** @brief A data value with an associated status code and timestamps. */
-typedef struct UA_DataValue {
+typedef struct {
     UA_Byte       encodingMask;
     UA_Variant    value;
     UA_StatusCode status;

+ 2 - 68
src/server/ua_nodestore.c

@@ -23,9 +23,10 @@ struct UA_NodeStore {
     UA_UInt32          sizePrimeIndex;
 };
 
+#include "ua_nodestore_hash.inc"
+
 /* The size of the hash-map is always a prime number. They are chosen to be
    close to the next power of 2. So the size ca. doubles with each prime. */
-typedef UA_UInt32 hash_t;
 static hash_t const primes[] = {
     7,         13,         31,         61,         127,         251,
     509,       1021,       2039,       4093,       8191,        16381,
@@ -34,8 +35,6 @@ static hash_t const primes[] = {
     134217689, 268435399,  536870909,  1073741789, 2147483647,  4294967291
 };
 
-static INLINE hash_t mod(hash_t h, hash_t size) { return h % size; }
-static INLINE hash_t mod2(hash_t h, hash_t size) { return 1 + (h % (size - 2)); }
 static INLINE UA_Int16 higher_prime_index(hash_t n) {
     UA_UInt16 low  = 0;
     UA_UInt16 high = sizeof(primes) / sizeof(hash_t);
@@ -49,71 +48,6 @@ static INLINE UA_Int16 higher_prime_index(hash_t n) {
     return low;
 }
 
-/* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
-static hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
-    if(data == UA_NULL)
-        return 0;
-
-    const int32_t   nblocks = len / 4;
-    const uint32_t *blocks;
-    static const uint32_t c1 = 0xcc9e2d51;
-    static const uint32_t c2 = 0x1b873593;
-    static const uint32_t r1 = 15;
-    static const uint32_t r2 = 13;
-    static const uint32_t m  = 5;
-    static const uint32_t n  = 0xe6546b64;
-    hash_t hash = seed;
-    blocks = (const uint32_t *)data;
-    for(int32_t i = 0;i < nblocks;i++) {
-        uint32_t k = blocks[i];
-        k    *= c1;
-        k     = (k << r1) | (k >> (32 - r1));
-        k    *= c2;
-        hash ^= k;
-        hash  = ((hash << r2) | (hash >> (32 - r2))) * m + n;
-    }
-
-    const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
-    uint32_t       k1   = 0;
-    switch(len & 3) {
-    case 3:
-        k1 ^= tail[2] << 16;
-    case 2:
-        k1 ^= tail[1] << 8;
-    case 1:
-        k1   ^= tail[0];
-        k1   *= c1;
-        k1    = (k1 << r1) | (k1 >> (32 - r1));
-        k1   *= c2;
-        hash ^= k1;
-    }
-
-    hash ^= len;
-    hash ^= (hash >> 16);
-    hash *= 0x85ebca6b;
-    hash ^= (hash >> 13);
-    hash *= 0xc2b2ae35;
-    hash ^= (hash >> 16);
-    return hash;
-}
-
-static hash_t hash(const UA_NodeId *n) {
-    switch(n->identifierType) {
-    case UA_NODEIDTYPE_NUMERIC:
-        /*  Knuth's multiplicative hashing */
-        return (n->identifier.numeric + n->namespaceIndex) * 2654435761;   // mod(2^32) is implicit
-    case UA_NODEIDTYPE_STRING:
-        return hash_array(n->identifier.string.data, n->identifier.string.length, n->namespaceIndex);
-    case UA_NODEIDTYPE_GUID:
-        return hash_array((UA_Byte *)&(n->identifier.guid), sizeof(UA_Guid), n->namespaceIndex);
-    case UA_NODEIDTYPE_BYTESTRING:
-        return hash_array((UA_Byte *)n->identifier.byteString.data, n->identifier.byteString.length, n->namespaceIndex);
-    default:
-        UA_assert(UA_FALSE);
-        return 0;
-    }
-}
-
 /* 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, struct nodeEntry ***entry) {

+ 108 - 116
src/server/ua_nodestore_concurrent.c

@@ -19,87 +19,7 @@ struct UA_NodeStore {
     struct cds_lfht *ht; /* Hash table */
 };
 
-/********/
-/* Hash */
-/********/
-
-typedef UA_UInt32 hash_t;
-
-/* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
-static hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
-    static const uint32_t c1 = 0xcc9e2d51;
-    static const uint32_t c2 = 0x1b873593;
-    static const uint32_t r1 = 15;
-    static const uint32_t r2 = 13;
-    static const uint32_t m  = 5;
-    static const uint32_t n  = 0xe6546b64;
-    hash_t hash = seed;
-
-    if(data == UA_NULL) return 0;
-
-    const int32_t   nblocks = len / 4;
-    const uint32_t *blocks  = (const uint32_t *)data;
-    for(int32_t i = 0;i < nblocks;i++) {
-        uint32_t k = blocks[i];
-        k    *= c1;
-        k     = (k << r1) | (k >> (32 - r1));
-        k    *= c2;
-        hash ^= k;
-        hash  = ((hash << r2) | (hash >> (32 - r2))) * m + n;
-    }
-
-    const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
-    uint32_t       k1   = 0;
-
-    switch(len & 3) {
-    case 3:
-        k1 ^= tail[2] << 16;
-
-    case 2:
-        k1 ^= tail[1] << 8;
-
-    case 1:
-        k1   ^= tail[0];
-        k1   *= c1;
-        k1    = (k1 << r1) | (k1 >> (32 - r1));
-        k1   *= c2;
-        hash ^= k1;
-    }
-
-    hash ^= len;
-    hash ^= (hash >> 16);
-    hash *= 0x85ebca6b;
-    hash ^= (hash >> 13);
-    hash *= 0xc2b2ae35;
-    hash ^= (hash >> 16);
-
-    return hash;
-}
-
-static hash_t hash(const UA_NodeId *n) {
-    switch(n->identifierType) {
-    case UA_NODEIDTYPE_NUMERIC:
-        /*  Knuth's multiplicative hashing */
-        return (n->identifier.numeric + n->namespaceIndex) * 2654435761;   // mod(2^32) is implicit
-
-    case UA_NODEIDTYPE_STRING:
-        return hash_array(n->identifier.string.data, n->identifier.string.length, n->namespaceIndex);
-
-    case UA_NODEIDTYPE_GUID:
-        return hash_array((UA_Byte *)&(n->identifier.guid), sizeof(UA_Guid), n->namespaceIndex);
-
-    case UA_NODEIDTYPE_BYTESTRING:
-        return hash_array((UA_Byte *)n->identifier.byteString.data, n->identifier.byteString.length, n->namespaceIndex);
-
-    default:
-        UA_assert(UA_FALSE);
-        return 0;
-    }
-}
-
-/****************/
-/* UA_NodeStore */
-/****************/
+#include "ua_nodestore_hash.inc"
 
 static inline void node_deleteMembers(const UA_Node *node) {
     switch(node->nodeClass) {
@@ -143,8 +63,8 @@ static inline void node_deleteMembers(const UA_Node *node) {
 
 /* We are in a rcu_read lock. So the node will not be freed under our feet. */
 static int compare(struct cds_lfht_node *htn, const void *orig) {
-    UA_NodeId *origid = (UA_NodeId *)orig;
-    UA_NodeId *newid  = &((UA_NodeStore_Entry *)htn)->node.nodeId;   /* The htn is first in the entry structure. */
+    const UA_NodeId *origid = (const UA_NodeId *)orig;
+    const UA_NodeId *newid  = &((UA_NodeStore_Entry *)htn)->node.nodeId;   /* The htn is first in the entry structure. */
 
     return UA_NodeId_equal(newid, origid);
 }
@@ -155,7 +75,8 @@ static int compare(struct cds_lfht_node *htn, const void *orig) {
    to reach zero. */
 static void markDead(struct rcu_head *head) {
     UA_NodeStore_Entry *entry = caa_container_of(head, UA_NodeStore_Entry, rcu_head);
-    if(uatomic_and(&entry->refcount, ~ALIVE_BIT) > 0)
+    uatomic_and(&entry->refcount, ~ALIVE_BIT); // set the alive bit to zero
+    if(uatomic_read(&entry->refcount) > 0)
         return;
 
     node_deleteMembers(&entry->node);
@@ -174,20 +95,18 @@ void UA_NodeStore_release(const UA_Node *managed) {
     return;
 }
 
-UA_StatusCode UA_NodeStore_new(UA_NodeStore **result) {
+UA_NodeStore * UA_NodeStore_new() {
     UA_NodeStore *ns;
     if(!(ns = UA_alloc(sizeof(UA_NodeStore))))
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+        return UA_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);
-        return UA_STATUSCODE_BADOUTOFMEMORY;
+        return UA_NULL;
     }
-
-    *result = ns;
-    return UA_STATUSCODE_GOOD;
+    return ns;
 }
 
 void UA_NodeStore_delete(UA_NodeStore *ns) {
@@ -211,42 +130,34 @@ void UA_NodeStore_delete(UA_NodeStore *ns) {
     UA_free(ns);
 }
 
-UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flags) {
+UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged) {
     UA_UInt32 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;
     }
@@ -254,18 +165,18 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flag
     UA_NodeStore_Entry *entry;
     if(!(entry = UA_alloc(sizeof(UA_NodeStore_Entry) - sizeof(UA_Node) + nodesize)))
         return UA_STATUSCODE_BADOUTOFMEMORY;
-    memcpy(&entry->node, *node, nodesize);
+    memcpy((void*)&entry->node, *node, nodesize);
 
     cds_lfht_node_init(&entry->htn);
     entry->refcount = ALIVE_BIT;
-    if(flags & UA_NODESTORE_INSERT_GETMANAGED)
+    if(getManaged) // increase the counter before adding the node
         entry->refcount++;
 
-    hash_t nhash = hash(&(*node)->nodeId);
     struct cds_lfht_node *result;
-    if(flags & UA_NODESTORE_INSERT_UNIQUE) {
+    if(!UA_NodeId_isNull(&(*node)->nodeId)) {
+        hash_t h = hash(&(*node)->nodeId);
         rcu_read_lock();
-        result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
+        result = cds_lfht_add_unique(ns->ht, h, compare, &entry->node.nodeId, &entry->htn);
         rcu_read_unlock();
 
         /* If the nodeid exists already */
@@ -274,23 +185,106 @@ UA_StatusCode UA_NodeStore_insert(UA_NodeStore *ns, UA_Node **node, UA_Byte flag
             return UA_STATUSCODE_BADNODEIDEXISTS;
         }
     } else {
+        /* create a unique nodeid */
+        ((UA_Node *)&entry->node)->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
+        ((UA_Node *)&entry->node)->nodeId.namespaceIndex = 1; // namespace 1 is always in the local nodestore
+        unsigned long identifier;
+        long before, after;
         rcu_read_lock();
-        result = cds_lfht_add_replace(ns->ht, nhash, compare, &(*node)->nodeId, &entry->htn);
-        /* If an entry got replaced, mark it as dead. */
-        if(result) {
-            UA_NodeStore_Entry *entry = caa_container_of(result, UA_NodeStore_Entry, htn);
-            call_rcu(&entry->rcu_head, markDead);      /* Queue this for the next time when no readers are on the entry.*/
-        }
+        cds_lfht_count_nodes(ns->ht, &before, &identifier, &after); // current amount of nodes stored
         rcu_read_unlock();
+        identifier++;
+
+        ((UA_Node *)&entry->node)->nodeId.identifier.numeric = identifier;
+        while(UA_TRUE) {
+            hash_t nhash = hash(&entry->node.nodeId);
+            rcu_read_lock();
+            result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
+            rcu_read_unlock();
+            if(result == &entry->htn)
+                break;
+
+            ((UA_Node *)&entry->node)->nodeId.identifier.numeric += (identifier * 2654435761);
+        }
     }
 
     UA_free((UA_Node *)*node);     /* The old node is replaced by a managed node. */
-    if(flags & UA_NODESTORE_INSERT_GETMANAGED)
+    if(getManaged)
+        *node = &entry->node;
+    else
+        *node = UA_NULL;
+    return UA_STATUSCODE_GOOD;
+}
+
+UA_StatusCode UA_NodeStore_replace(UA_NodeStore *ns, const UA_Node **node, UA_Boolean getManaged) {
+    UA_UInt32 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_NodeStore_Entry *entry;
+    if(!(entry = UA_alloc(sizeof(UA_NodeStore_Entry) - sizeof(UA_Node) + nodesize)))
+        return UA_STATUSCODE_BADOUTOFMEMORY;
+    memcpy((void*)&entry->node, *node, nodesize);
+
+    cds_lfht_node_init(&entry->htn);
+    entry->refcount = ALIVE_BIT;
+    if(getManaged) // increase the counter before adding the node
+        entry->refcount++;
+
+    hash_t h = hash(&(*node)->nodeId);
+
+    struct cds_lfht_iter iter;
+    rcu_read_lock();
+    cds_lfht_lookup(ns->ht, h, compare, &(*node)->nodeId, &iter);
+    struct cds_lfht_node *result = cds_lfht_iter_get_node(&iter);
+
+    if(result && cds_lfht_replace(ns->ht, &iter, h, compare, &(*node)->nodeId, &entry->htn) == 0) {
+        UA_NodeStore_Entry *entry = caa_container_of(result, UA_NodeStore_Entry, htn);
+        /* If an entry got replaced, mark it as dead. */
+        call_rcu(&entry->rcu_head, markDead);
+        rcu_read_unlock();
+    } else {
+        rcu_read_unlock();
+        UA_free(entry);
+        return UA_STATUSCODE_BADNODEIDUNKNOWN;
+    }
+
+    /* The old node is replaced by a managed node. */
+    UA_free((UA_Node *)*node);
+    if(getManaged)
         *node = &entry->node;
     else
         *node = UA_NULL;
 
     return UA_STATUSCODE_GOOD;
+
 }
 
 UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
@@ -314,7 +308,7 @@ UA_StatusCode UA_NodeStore_remove(UA_NodeStore *ns, const UA_NodeId *nodeid) {
     return UA_STATUSCODE_GOOD;
 }
 
-UA_StatusCode UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid, const UA_Node **managedNode) {
+const UA_Node * UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid) {
     hash_t nhash = hash(nodeid);
     struct cds_lfht_iter iter;
 
@@ -324,15 +318,13 @@ UA_StatusCode UA_NodeStore_get(const UA_NodeStore *ns, const UA_NodeId *nodeid,
 
     if(!found_entry) {
         rcu_read_unlock();
-        return UA_STATUSCODE_BADNODEIDUNKNOWN;
+        return UA_NULL;
     }
 
     /* This is done within a read-lock. The node will not be marked dead within a read-lock. */
     uatomic_inc(&found_entry->refcount);
     rcu_read_unlock();
-
-    *managedNode = &found_entry->node;
-    return UA_STATUSCODE_GOOD;
+    return &found_entry->node;
 }
 
 void UA_NodeStore_iterate(const UA_NodeStore *ns, UA_NodeStore_nodeVisitor visitor) {

+ 69 - 0
src/server/ua_nodestore_hash.inc

@@ -0,0 +1,69 @@
+typedef UA_UInt32 hash_t;
+
+static INLINE hash_t mod(hash_t h, hash_t size) { return h % size; }
+static INLINE hash_t mod2(hash_t h, hash_t size) { return 1 + (h % (size - 2)); }
+
+/* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
+static hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
+    if(data == UA_NULL)
+        return 0;
+
+    const int32_t   nblocks = len / 4;
+    const uint32_t *blocks;
+    static const uint32_t c1 = 0xcc9e2d51;
+    static const uint32_t c2 = 0x1b873593;
+    static const uint32_t r1 = 15;
+    static const uint32_t r2 = 13;
+    static const uint32_t m  = 5;
+    static const uint32_t n  = 0xe6546b64;
+    hash_t hash = seed;
+    blocks = (const uint32_t *)data;
+    for(int32_t i = 0;i < nblocks;i++) {
+        uint32_t k = blocks[i];
+        k    *= c1;
+        k     = (k << r1) | (k >> (32 - r1));
+        k    *= c2;
+        hash ^= k;
+        hash  = ((hash << r2) | (hash >> (32 - r2))) * m + n;
+    }
+
+    const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
+    uint32_t       k1   = 0;
+    switch(len & 3) {
+    case 3:
+        k1 ^= tail[2] << 16;
+    case 2:
+        k1 ^= tail[1] << 8;
+    case 1:
+        k1   ^= tail[0];
+        k1   *= c1;
+        k1    = (k1 << r1) | (k1 >> (32 - r1));
+        k1   *= c2;
+        hash ^= k1;
+    }
+
+    hash ^= len;
+    hash ^= (hash >> 16);
+    hash *= 0x85ebca6b;
+    hash ^= (hash >> 13);
+    hash *= 0xc2b2ae35;
+    hash ^= (hash >> 16);
+    return hash;
+}
+
+static hash_t hash(const UA_NodeId *n) {
+    switch(n->identifierType) {
+    case UA_NODEIDTYPE_NUMERIC:
+        /*  Knuth's multiplicative hashing */
+        return (n->identifier.numeric + n->namespaceIndex) * 2654435761;   // mod(2^32) is implicit
+    case UA_NODEIDTYPE_STRING:
+        return hash_array(n->identifier.string.data, n->identifier.string.length, n->namespaceIndex);
+    case UA_NODEIDTYPE_GUID:
+        return hash_array((UA_Byte *)&(n->identifier.guid), sizeof(UA_Guid), n->namespaceIndex);
+    case UA_NODEIDTYPE_BYTESTRING:
+        return hash_array((UA_Byte *)n->identifier.byteString.data, n->identifier.byteString.length, n->namespaceIndex);
+    default:
+        UA_assert(UA_FALSE);
+        return 0;
+    }
+}

+ 1 - 1
src/ua_config.h.in

@@ -2,7 +2,7 @@
 #define UA_ENCODING_AMOUNT ${UA_ENCODING_AMOUNT}
 #define UA_LOGLEVEL ${UA_LOGLEVEL}
 
-#cmakedefine ENABLE_MULTITHREADING
+#cmakedefine MULTITHREADING
 #cmakedefine MSVC
 #cmakedefine WIN32
 

+ 2 - 2
tests/CMakeLists.txt

@@ -5,9 +5,9 @@ set(LIBS ${CHECK_LIBRARIES})
 if(NOT WIN32)
     list(APPEND LIBS rt pthread)
 endif(NOT WIN32)
-if(ENABLE_MULTITHREADING)
+if(MULTITHREADING)
     list(APPEND LIBS urcu-cds urcu)
-endif(ENABLE_MULTITHREADING)
+endif(MULTITHREADING)
 
 # the unit test are built directly on the open62541 object files. so they can
 # access symbols that are hidden/not exported to the shared library

+ 33 - 13
tests/check_nodestore.c

@@ -23,12 +23,6 @@ void printVisitor(const UA_Node* node) {
 	printf("%d\n", node->nodeId.identifier.numeric);
 }
 
-START_TEST(test_UA_NodeStore) {
-	UA_NodeStore *ns = UA_NodeStore_new();
-	UA_NodeStore_delete(ns);
-}
-END_TEST
-
 UA_StatusCode createNode(UA_Node** p, UA_Int16 nsid, UA_Int32 id) {
 	*p = (UA_Node *)UA_VariableNode_new();
 	(*p)->nodeId.identifierType = UA_NODEIDTYPE_NUMERIC;
@@ -38,6 +32,31 @@ UA_StatusCode createNode(UA_Node** p, UA_Int16 nsid, UA_Int32 id) {
 	return UA_STATUSCODE_GOOD;
 }
 
+START_TEST(replaceExistingNode) {
+	UA_NodeStore *ns = UA_NodeStore_new();
+	UA_Node* n1; createNode(&n1,0,2253);
+	UA_NodeStore_insert(ns, (const UA_Node **)&n1, UA_FALSE);
+	UA_Node* n2; createNode(&n2,0,2253);
+    UA_StatusCode retval = UA_NodeStore_replace(ns, (const UA_Node **)&n2, UA_FALSE);
+    
+	ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
+    
+	UA_NodeStore_delete(ns);
+}
+END_TEST
+
+START_TEST(replaceNonExistingNode) {
+	UA_NodeStore *ns = UA_NodeStore_new();
+	UA_Node* n2; createNode(&n2,0,2253);
+    UA_StatusCode retval = UA_NodeStore_replace(ns, (const UA_Node **)&n2, UA_FALSE);
+    
+	ck_assert_int_ne(retval, UA_STATUSCODE_GOOD);
+    
+    UA_Node_delete(n2);
+	UA_NodeStore_delete(ns);
+}
+END_TEST
+
 START_TEST(findNodeInUA_NodeStoreWithSingleEntry) {
 #ifdef MULTITHREADING
    	rcu_register_thread();
@@ -66,9 +85,8 @@ START_TEST(failToFindNodeInOtherUA_NodeStore) {
 	// given
 	UA_NodeStore *ns = UA_NodeStore_new();
 
-	UA_Node* n1; createNode(&n1,0,2253);
+	UA_Node* n1; createNode(&n1,0,2255);
     UA_NodeStore_insert(ns, (const UA_Node **)&n1, UA_FALSE);
-	UA_Node* n2; createNode(&n2,0,2253); UA_NodeStore_insert(ns, (const UA_Node **)&n2, UA_FALSE);
 
 	// when
 	UA_Node* n; createNode(&n,1,2255);
@@ -251,7 +269,8 @@ struct UA_NodeStoreProfileTest {
 void *profileGetThread(void *arg) {
    	rcu_register_thread();
 	struct UA_NodeStoreProfileTest *test = (struct UA_NodeStoreProfileTest*) arg;
-	UA_NodeId id = NS0NODEID(0);
+	UA_NodeId id;
+    UA_NodeId_init(&id);
 	const UA_Node *cn;
 	UA_Int32 max_val = test->max_val;
 	UA_NodeStore *ns = test->ns;
@@ -321,10 +340,6 @@ END_TEST
 Suite * namespace_suite (void) {
 	Suite *s = suite_create ("UA_NodeStore");
 
-	TCase *tc_cd = tcase_create ("Create/Delete");
-	tcase_add_test (tc_cd, test_UA_NodeStore);
-	suite_add_tcase (s, tc_cd);
-
 	TCase* tc_find = tcase_create ("Find");
 	tcase_add_test (tc_find, findNodeInUA_NodeStoreWithSingleEntry);
 	tcase_add_test (tc_find, findNodeInUA_NodeStoreWithSeveralEntries);
@@ -333,6 +348,11 @@ Suite * namespace_suite (void) {
 	tcase_add_test (tc_find, failToFindNodeInOtherUA_NodeStore);
 	suite_add_tcase (s, tc_find);
 
+	TCase *tc_replace = tcase_create("Replace");
+	tcase_add_test (tc_replace, replaceExistingNode);
+	tcase_add_test (tc_replace, replaceNonExistingNode);
+	suite_add_tcase (s, tc_replace);
+
 	TCase* tc_iterate = tcase_create ("Iterate");
 	tcase_add_test (tc_iterate, iterateOverUA_NodeStoreShallNotVisitEmptyNodes);
 	tcase_add_test (tc_iterate, iterateOverExpandedNamespaceShallNotVisitEmptyNodes);

+ 2 - 2
tools/generate_builtin.py

@@ -90,7 +90,7 @@ def createEnumerated(element):
             valuemap[name + "_" + child.get("Name")] = child.get("Value")
     valuemap = OrderedDict(sorted(valuemap.iteritems(), key=lambda (k,v): int(v)))
     # printh("typedef UA_Int32 " + name + ";")
-    printh("typedef enum " + name + " { \n\t" +
+    printh("typedef enum { \n\t" +
            ",\n\t".join(map(lambda (key, value) : key.upper() + " = " + value, valuemap.iteritems())) +
            "\n} " + name + ";")
     if args.export_prototypes:
@@ -162,7 +162,7 @@ def createStructured(element):
 
     # 3) Print structure
     if len(membermap) > 0:
-        printh("typedef struct %(name)s {")
+        printh("typedef struct {")
         for n,t in membermap.iteritems():
 	    if t.find("*") != -1:
 	        printh("\t" + "UA_Int32 " + n + "Size;")