ua_nodestore_concurrent.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. #include "open62541NodeStore.h"
  2. #include "ua_util.h"
  3. #include <urcu.h>
  4. #include <urcu/compiler.h> // for caa_container_of
  5. #include <urcu/uatomic.h>
  6. #include <urcu/rculfhash.h>
  7. #define ALIVE_BIT (1 << 15) /* Alive bit in the readcount */
  8. typedef struct open62541NodeStore_Entry {
  9. struct cds_lfht_node htn; /* contains next-ptr for urcu-hashmap */
  10. struct rcu_head rcu_head; /* For call-rcu */
  11. UA_UInt16 readcount; /* Counts the amount of readers on it [alive-bit, 15 counter-bits] */
  12. UA_Node node; /* Might be cast from any _bigger_ UA_Node* type. Allocate enough memory! */
  13. } open62541NodeStore_Entry;
  14. struct open62541NodeStore {
  15. struct cds_lfht *ht; /* Hash table */
  16. };
  17. /********/
  18. /* Hash */
  19. /********/
  20. typedef UA_UInt32 hash_t;
  21. /* Based on Murmur-Hash 3 by Austin Appleby (public domain, freely usable) */
  22. static INLINE hash_t hash_array(const UA_Byte *data, UA_UInt32 len, UA_UInt32 seed) {
  23. static const uint32_t c1 = 0xcc9e2d51;
  24. static const uint32_t c2 = 0x1b873593;
  25. static const uint32_t r1 = 15;
  26. static const uint32_t r2 = 13;
  27. static const uint32_t m = 5;
  28. static const uint32_t n = 0xe6546b64;
  29. hash_t hash = seed;
  30. if(data == UA_NULL) return 0;
  31. const int32_t nblocks = len / 4;
  32. const uint32_t *blocks = (const uint32_t *)data;
  33. for(int32_t i = 0;i < nblocks;i++) {
  34. uint32_t k = blocks[i];
  35. k *= c1;
  36. k = (k << r1) | (k >> (32 - r1));
  37. k *= c2;
  38. hash ^= k;
  39. hash = ((hash << r2) | (hash >> (32 - r2))) * m + n;
  40. }
  41. const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
  42. uint32_t k1 = 0;
  43. switch(len & 3) {
  44. case 3:
  45. k1 ^= tail[2] << 16;
  46. case 2:
  47. k1 ^= tail[1] << 8;
  48. case 1:
  49. k1 ^= tail[0];
  50. k1 *= c1;
  51. k1 = (k1 << r1) | (k1 >> (32 - r1));
  52. k1 *= c2;
  53. hash ^= k1;
  54. }
  55. hash ^= len;
  56. hash ^= (hash >> 16);
  57. hash *= 0x85ebca6b;
  58. hash ^= (hash >> 13);
  59. hash *= 0xc2b2ae35;
  60. hash ^= (hash >> 16);
  61. return hash;
  62. }
  63. static INLINE hash_t hash(const UA_NodeId *n) {
  64. switch(n->identifierType) {
  65. case UA_NODEIDTYPE_NUMERIC:
  66. /* Knuth's multiplicative hashing */
  67. return (n->identifier.numeric + n->namespaceIndex) * 2654435761; // mod(2^32) is implicit
  68. case UA_NODEIDTYPE_STRING:
  69. return hash_array(n->identifier.string.data, n->identifier.string.length, n->namespaceIndex);
  70. case UA_NODEIDTYPE_GUID:
  71. return hash_array((UA_Byte *)&(n->identifier.guid), sizeof(UA_Guid), n->namespaceIndex);
  72. case UA_NODEIDTYPE_BYTESTRING:
  73. return hash_array((UA_Byte *)n->identifier.byteString.data, n->identifier.byteString.length, n->namespaceIndex);
  74. default:
  75. UA_assert(UA_FALSE);
  76. return 0;
  77. }
  78. }
  79. /****************/
  80. /* open62541NodeStore */
  81. /****************/
  82. static inline void node_deleteMembers(const UA_Node *node) {
  83. switch(node->nodeClass) {
  84. case UA_NODECLASS_OBJECT:
  85. UA_ObjectNode_deleteMembers((UA_ObjectNode *)node);
  86. break;
  87. case UA_NODECLASS_VARIABLE:
  88. UA_VariableNode_deleteMembers((UA_VariableNode *)node);
  89. break;
  90. case UA_NODECLASS_METHOD:
  91. UA_MethodNode_deleteMembers((UA_MethodNode *)node);
  92. break;
  93. case UA_NODECLASS_OBJECTTYPE:
  94. UA_ObjectTypeNode_deleteMembers((UA_ObjectTypeNode *)node);
  95. break;
  96. case UA_NODECLASS_VARIABLETYPE:
  97. UA_VariableTypeNode_deleteMembers((UA_VariableTypeNode *)node);
  98. break;
  99. case UA_NODECLASS_REFERENCETYPE:
  100. UA_ReferenceTypeNode_deleteMembers((UA_ReferenceTypeNode *)node);
  101. break;
  102. case UA_NODECLASS_DATATYPE:
  103. UA_DataTypeNode_deleteMembers((UA_DataTypeNode *)node);
  104. break;
  105. case UA_NODECLASS_VIEW:
  106. UA_ViewNode_deleteMembers((UA_ViewNode *)node);
  107. break;
  108. default:
  109. UA_assert(UA_FALSE);
  110. break;
  111. }
  112. }
  113. /* We are in a rcu_read lock. So the node will not be freed under our feet. */
  114. static int compare(struct cds_lfht_node *htn, const void *orig) {
  115. UA_NodeId *origid = (UA_NodeId *)orig;
  116. UA_NodeId *newid = &((open62541NodeStore_Entry *)htn)->node.nodeId; /* The htn is first in the entry structure. */
  117. return UA_NodeId_equal(newid, origid);
  118. }
  119. /* The entry was removed from the hashtable. No more readers can get it. Since
  120. all readers using the node for a longer time (outside the rcu critical
  121. section) increased the readcount, we only need to wait for the readcount
  122. to reach zero. */
  123. static void markDead(struct rcu_head *head) {
  124. open62541NodeStore_Entry *entry = caa_container_of(head, open62541NodeStore_Entry, rcu_head);
  125. if(uatomic_sub_return(&entry->readcount, ALIVE_BIT) > 0)
  126. return;
  127. node_deleteMembers(&entry->node);
  128. UA_free(entry);
  129. return;
  130. }
  131. /* Free the entry if it is dead and nobody uses it anymore */
  132. void open62541NodeStore_release(const UA_Node *managed) {
  133. open62541NodeStore_Entry *entry = caa_container_of(managed, open62541NodeStore_Entry, node); // pointer to the first entry
  134. if(uatomic_sub_return(&entry->readcount, 1) > 0)
  135. return;
  136. node_deleteMembers(managed);
  137. UA_free(entry);
  138. return;
  139. }
  140. UA_StatusCode open62541NodeStore_new(open62541NodeStore **result) {
  141. open62541NodeStore *ns;
  142. if(!(ns = UA_alloc(sizeof(open62541NodeStore))))
  143. return UA_STATUSCODE_BADOUTOFMEMORY;
  144. /* 32 is the minimum size for the hashtable. */
  145. ns->ht = cds_lfht_new(32, 32, 0, CDS_LFHT_AUTO_RESIZE, NULL);
  146. if(!ns->ht) {
  147. UA_free(ns);
  148. return UA_STATUSCODE_BADOUTOFMEMORY;
  149. }
  150. *result = ns;
  151. return UA_STATUSCODE_GOOD;
  152. }
  153. void open62541NodeStore_delete(open62541NodeStore *ns) {
  154. struct cds_lfht *ht = ns->ht;
  155. struct cds_lfht_iter iter;
  156. struct cds_lfht_node *found_htn;
  157. rcu_read_lock();
  158. cds_lfht_first(ht, &iter);
  159. while(iter.node != UA_NULL) {
  160. found_htn = cds_lfht_iter_get_node(&iter);
  161. if(!cds_lfht_del(ht, found_htn)) {
  162. open62541NodeStore_Entry *entry = caa_container_of(found_htn, open62541NodeStore_Entry, htn);
  163. call_rcu(&entry->rcu_head, markDead);
  164. }
  165. cds_lfht_next(ht, &iter);
  166. }
  167. rcu_read_unlock();
  168. cds_lfht_destroy(ht, UA_NULL);
  169. UA_free(ns);
  170. }
  171. UA_StatusCode open62541NodeStore_insert(open62541NodeStore *ns, UA_Node **node, UA_Byte flags) {
  172. UA_UInt32 nodesize;
  173. /* Copy the node into the entry. Then reset the original node. It shall no longer be used. */
  174. switch((*node)->nodeClass) {
  175. case UA_NODECLASS_OBJECT:
  176. nodesize = sizeof(UA_ObjectNode);
  177. break;
  178. case UA_NODECLASS_VARIABLE:
  179. nodesize = sizeof(UA_VariableNode);
  180. break;
  181. case UA_NODECLASS_METHOD:
  182. nodesize = sizeof(UA_MethodNode);
  183. break;
  184. case UA_NODECLASS_OBJECTTYPE:
  185. nodesize = sizeof(UA_ObjectTypeNode);
  186. break;
  187. case UA_NODECLASS_VARIABLETYPE:
  188. nodesize = sizeof(UA_VariableTypeNode);
  189. break;
  190. case UA_NODECLASS_REFERENCETYPE:
  191. nodesize = sizeof(UA_ReferenceTypeNode);
  192. break;
  193. case UA_NODECLASS_DATATYPE:
  194. nodesize = sizeof(UA_DataTypeNode);
  195. break;
  196. case UA_NODECLASS_VIEW:
  197. nodesize = sizeof(UA_ViewNode);
  198. break;
  199. default:
  200. return UA_STATUSCODE_BADINTERNALERROR;
  201. }
  202. open62541NodeStore_Entry *entry;
  203. if(!(entry = UA_alloc(sizeof(open62541NodeStore_Entry) - sizeof(UA_Node) + nodesize)))
  204. return UA_STATUSCODE_BADOUTOFMEMORY;
  205. memcpy(&entry->node, *node, nodesize);
  206. cds_lfht_node_init(&entry->htn);
  207. entry->readcount = ALIVE_BIT;
  208. if(flags & open62541NodeStore_INSERT_GETMANAGED)
  209. entry->readcount++;
  210. hash_t nhash = hash(&(*node)->nodeId);
  211. struct cds_lfht_node *result;
  212. if(flags & open62541NodeStore_INSERT_UNIQUE) {
  213. rcu_read_lock();
  214. result = cds_lfht_add_unique(ns->ht, nhash, compare, &entry->node.nodeId, &entry->htn);
  215. rcu_read_unlock();
  216. /* If the nodeid exists already */
  217. if(result != &entry->htn) {
  218. UA_free(entry);
  219. return UA_STATUSCODE_BADNODEIDEXISTS;
  220. }
  221. } else {
  222. rcu_read_lock();
  223. result = cds_lfht_add_replace(ns->ht, nhash, compare, &(*node)->nodeId, &entry->htn);
  224. /* If an entry got replaced, mark it as dead. */
  225. if(result) {
  226. open62541NodeStore_Entry *entry = caa_container_of(result, open62541NodeStore_Entry, htn);
  227. call_rcu(&entry->rcu_head, markDead); /* Queue this for the next time when no readers are on the entry.*/
  228. }
  229. rcu_read_unlock();
  230. }
  231. UA_free((UA_Node *)*node); /* The old node is replaced by a managed node. */
  232. if(flags & open62541NodeStore_INSERT_GETMANAGED)
  233. *node = &entry->node;
  234. else
  235. *node = UA_NULL;
  236. return UA_STATUSCODE_GOOD;
  237. }
  238. UA_StatusCode open62541NodeStore_remove(open62541NodeStore *ns, const UA_NodeId *nodeid) {
  239. hash_t nhash = hash(nodeid);
  240. struct cds_lfht_iter iter;
  241. rcu_read_lock();
  242. cds_lfht_lookup(ns->ht, nhash, compare, &nodeid, &iter);
  243. struct cds_lfht_node *found_htn = cds_lfht_iter_get_node(&iter);
  244. /* If this fails, then the node has already been removed. */
  245. if(!found_htn || cds_lfht_del(ns->ht, found_htn) != 0) {
  246. rcu_read_unlock();
  247. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  248. }
  249. open62541NodeStore_Entry *entry = caa_container_of(found_htn, open62541NodeStore_Entry, htn);
  250. call_rcu(&entry->rcu_head, markDead);
  251. rcu_read_unlock();
  252. return UA_STATUSCODE_GOOD;
  253. }
  254. UA_StatusCode open62541NodeStore_get(const open62541NodeStore *ns, const UA_NodeId *nodeid, const UA_Node **managedNode) {
  255. hash_t nhash = hash(nodeid);
  256. struct cds_lfht_iter iter;
  257. rcu_read_lock();
  258. cds_lfht_lookup(ns->ht, nhash, compare, nodeid, &iter);
  259. open62541NodeStore_Entry *found_entry = (open62541NodeStore_Entry *)cds_lfht_iter_get_node(&iter);
  260. if(!found_entry) {
  261. rcu_read_unlock();
  262. return UA_STATUSCODE_BADNODEIDUNKNOWN;
  263. }
  264. /* This is done within a read-lock. The node will not be marked dead within a read-lock. */
  265. uatomic_inc(&found_entry->readcount);
  266. rcu_read_unlock();
  267. *managedNode = &found_entry->node;
  268. return UA_STATUSCODE_GOOD;
  269. }
  270. void open62541NodeStore_iterate(const open62541NodeStore *ns, open62541NodeStore_nodeVisitor visitor) {
  271. struct cds_lfht *ht = ns->ht;
  272. struct cds_lfht_iter iter;
  273. rcu_read_lock();
  274. cds_lfht_first(ht, &iter);
  275. while(iter.node != UA_NULL) {
  276. open62541NodeStore_Entry *found_entry = (open62541NodeStore_Entry *)cds_lfht_iter_get_node(&iter);
  277. uatomic_inc(&found_entry->readcount);
  278. const UA_Node *node = &found_entry->node;
  279. rcu_read_unlock();
  280. visitor(node);
  281. open62541NodeStore_release((UA_Node *)node);
  282. rcu_read_lock();
  283. cds_lfht_next(ht, &iter);
  284. }
  285. rcu_read_unlock();
  286. }