Bläddra i källkod

namespace with read/write locks (pthread)

Julius Pfrommer 11 år sedan
förälder
incheckning
2c5e644e4d
2 ändrade filer med 147 tillägg och 175 borttagningar
  1. 127 169
      src/opcua_namespace.c
  2. 20 6
      src/opcua_namespace.h

+ 127 - 169
src/opcua_namespace.c

@@ -2,8 +2,7 @@
 #include <string.h>
 #include <stdio.h>
 
-/* The basic data structure is a hash-map of UA_Node objects.
-
+/* The central data structure is a hash-map of UA_Node objects.
    Entry lookup via Algorithm D from Knuth's TAOCP (no linked lists here).
    Table of primes and mod-functions are from libiberty (licensed under LGPL) */
 
@@ -53,15 +52,17 @@ UA_Int32 create_ns(namespace **result, uint32_t size) {
 	uint32_t sizePrimeIndex = higher_prime_index(size);
 	size = prime_tab[sizePrimeIndex].prime;
 
-	if (UA_alloc((void **)&ns, sizeof(namespace)) != UA_SUCCESS) return UA_ERR_NO_MEMORY;
+	if (UA_alloc((void *)&ns, sizeof(namespace)) != UA_SUCCESS) return UA_ERR_NO_MEMORY;
   
-	if (UA_alloc((void **)&(ns->entries), sizeof(void *)*size) != UA_SUCCESS) {
+	if (UA_alloc((void *)&ns->entries, sizeof(ns_entry) * size) != UA_SUCCESS) {
 		UA_free(ns);
 		return UA_ERR_NO_MEMORY;
 	}
-	
-	for (uint32_t i=0; i<size;i++)
-		ns->entries[i] = UA_NULL;
+
+    /* set entries to zero	
+	ns->entries[i].lock = UA_NUll;
+	ns->entries[i].node = UA_NULL; */
+	memset(ns->entries, 0, size * sizeof(ns_entry));
 
 	ns->size = size;
 	ns->count = 0;
@@ -71,166 +72,82 @@ UA_Int32 create_ns(namespace **result, uint32_t size) {
 }
 
 void empty_ns(namespace *ns) {
-	uint32_t size = ns->size;
-	UA_Node **entries = ns->entries;
-
-	for (uint32_t i = 0; i < size; i++) {
-		if (entries[i] != UA_NULL) {
-			switch(entries[i]->nodeClass) {
-			case UA_NODECLASS_OBJECT:
-				// UA_ObjectNode_delete((UA_ObjectNode *) entries[i]);
-				break;
-			case UA_NODECLASS_VARIABLE:
-				// UA_VariableNode_delete((UA_VariableNode *) entries[i]);
-				break;
-			case UA_NODECLASS_METHOD:
-				// UA_MethodNode_delete((UA_MethodNode *) entries[i]);
-				break;
-			case UA_NODECLASS_OBJECTTYPE:
-				// UA_ObjectTypeNode_delete((UA_ObjectTypeNode *) entries[i]);
-				break;
-			case UA_NODECLASS_VARIABLETYPE:
-				// UA_VariableTypeNode_delete((UA_VariableTypeNode *) entries[i]);
-				break;
-			case UA_NODECLASS_REFERENCETYPE:
-				// UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *) entries[i]);
-				break;
-			case UA_NODECLASS_DATATYPE:
-				// UA_DataTypeNode_delete((UA_DataTypeNode *) entries[i]);
-				break;
-			case UA_NODECLASS_VIEW:
-				// UA_ViewNode_delete((UA_ViewNode *) entries[i]);
-				break;
-			default:
-				break; // Unspecified nodes are not permitted.
-			}
-		}
-	}	
-
+	clear_ns(ns);
+	
 	/* Downsize the table.  */
-	if (size > 1024*1024 / sizeof (void *)) {
-		int nindex = higher_prime_index (1024 / sizeof (void *));
+	if (ns->size > 1024*1024 / sizeof (ns_entry)) {
+		int nindex = higher_prime_index (1024 / sizeof (ns_entry));
 		int nsize = prime_tab[nindex].prime;
-
 		UA_free(ns->entries);
-		UA_alloc((void **)&(ns->entries), sizeof(void *)*nsize); //FIXME: Check if memory was available
+		UA_alloc((void *)&ns->entries, sizeof(ns_entry) * nsize); //FIXME: Check return value
 		ns->size = nsize;
 		ns->sizePrimeIndex = nindex;
-		ns->count = 0;
-	}
-	else {
-		memset(*(ns->entries), 0, size * sizeof(void *));
-		ns->count = 0;
 	}
 }
 
 /* This function frees all memory allocated for a namespace */
 void delete_ns (namespace *ns) {
-	uint32_t size = ns->size;
-	UA_Node **entries = ns->entries;
-
-	for (uint32_t i = 0; i < size; i++) {
-		if (entries[i] != UA_NULL) {
-			switch(entries[i]->nodeClass) {
-			case UA_NODECLASS_OBJECT:
-				// UA_ObjectNode_delete((UA_ObjectNode *) entries[i]);
-				break;
-			case UA_NODECLASS_VARIABLE:
-				// UA_VariableNode_delete((UA_VariableNode *) entries[i]);
-				break;
-			case UA_NODECLASS_METHOD:
-				// UA_MethodNode_delete((UA_MethodNode *) entries[i]);
-				break;
-			case UA_NODECLASS_OBJECTTYPE:
-				// UA_ObjectTypeNode_delete((UA_ObjectTypeNode *) entries[i]);
-				break;
-			case UA_NODECLASS_VARIABLETYPE:
-				// UA_VariableTypeNode_delete((UA_VariableTypeNode *) entries[i]);
-				break;
-			case UA_NODECLASS_REFERENCETYPE:
-				// UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *) entries[i]);
-				break;
-			case UA_NODECLASS_DATATYPE:
-				// UA_DataTypeNode_delete((UA_DataTypeNode *) entries[i]);
-				break;
-			case UA_NODECLASS_VIEW:
-				// UA_ViewNode_delete((UA_ViewNode *) entries[i]);
-				break;
-			default:
-				break; // Unspecified nodes are not permitted.
-			}
-		}
-	}	
-
-	UA_free(entries);
+	clear_ns(ns);
+	UA_free(ns->entries);
 	UA_free(ns);
 }
 
-UA_Int32 insert(namespace *ns, UA_Node *node) {
+UA_Int32 insert_node(namespace *ns, UA_Node *node) {
 	if(ns->size * 3 <= ns->count*4) {
 		if(expand(ns) != UA_SUCCESS)
 			return UA_ERROR;
 	}
 		
-	hash_t h = hash(&(node->nodeId));
-	UA_Node **slot = find_empty_slot(ns, h);
-	*slot = node;
+	hash_t h = hash(&node->nodeId);
+	ns_entry *slot = find_empty_slot(ns, h);
+	if (UA_alloc((void *)&slot->lock, sizeof(pthread_rwlock_t)) != UA_SUCCESS)
+		return UA_ERR_NO_MEMORY;
+	pthread_rwlock_init((pthread_rwlock_t *)slot->lock, NULL);
+	slot->node = node;
 	return UA_SUCCESS;
 }
 
-UA_Int32 find(namespace *ns, UA_Node **result, UA_NodeId *nodeid) {
-	UA_Node **slot = UA_NULL;
-	if(find_slot(ns, slot, nodeid) == UA_SUCCESS) {
-		*result = *slot;
+UA_Int32 get_node(namespace *ns, UA_Node  ** const result, ns_lock ** lock, UA_NodeId *nodeid) {
+	ns_entry *slot;
+	if(find_slot(ns, &slot, nodeid) == UA_SUCCESS) {
+		if(pthread_rwlock_rdlock((pthread_rwlock_t *)slot->lock) != 0)
+			return UA_ERROR;
+		*result = slot->node;
+		*lock = slot->lock;
 		return UA_SUCCESS;
 	}
 	return UA_ERROR;
 }
 
-void delete(namespace *ns, UA_NodeId *nodeid) {
-	UA_Node **slot = UA_NULL;
-	if (find_slot(ns, slot, nodeid) != UA_SUCCESS)
-		return;
+UA_Int32 get_writable_node(namespace *ns, UA_Node  **result, ns_lock ** lock, UA_NodeId *nodeid) {
+	ns_entry *slot;
+	if(find_slot(ns, &slot, nodeid) == UA_SUCCESS) {
+		if(pthread_rwlock_wrlock((pthread_rwlock_t *)slot->lock) != 0)
+			return UA_ERROR;
+		*result = slot->node;
+		*lock = slot->lock;
+		return UA_SUCCESS;
+	}
+	return UA_ERROR;
+}
 
-	UA_Node *node = *slot;
-	*slot = UA_NULL;
+inline void unlock_node(ns_lock *lock) {
+	pthread_rwlock_unlock((pthread_rwlock_t *)lock);
+}
 
-	switch(node->nodeClass) {
-	case UA_NODECLASS_OBJECT:
-		// UA_ObjectNode_delete((UA_ObjectNode *) node);
-		break;
-	case UA_NODECLASS_VARIABLE:
-		// UA_VariableNode_delete((UA_VariableNode *) node);
-		break;
-	case UA_NODECLASS_METHOD:
-		// UA_MethodNode_delete((UA_MethodNode *) node);
-		break;
-	case UA_NODECLASS_OBJECTTYPE:
-		// UA_ObjectTypeNode_delete((UA_ObjectTypeNode *) node);
-		break;
-	case UA_NODECLASS_VARIABLETYPE:
-		// UA_VariableTypeNode_delete((UA_VariableTypeNode *) node);
-		break;
-	case UA_NODECLASS_REFERENCETYPE:
-		// UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *) node);
-		break;
-	case UA_NODECLASS_DATATYPE:
-		// UA_DataTypeNode_delete((UA_DataTypeNode *) node);
-		break;
-	case UA_NODECLASS_VIEW:
-		// UA_ViewNode_delete((UA_ViewNode *) node);
-		break;
-	default:
-		break; // Unspecified nodes are not permitted.
-	}
+void delete_node(namespace *ns, UA_NodeId *nodeid) {
+	ns_entry *slot;
+	if (find_slot(ns, &slot, nodeid) != UA_SUCCESS)
+		return;
+	clear_slot(ns, slot);
 
 	/* Downsize the hashmap if it is very empty */
-	if(ns->count*8 < ns->size && ns->size > 32) {
+	if(ns->count*8 < ns->size && ns->size > 32)
 		expand(ns);
-	}
 }
 
-/* Hashing inspired by code from from http://www.azillionmonkeys.com/qed/hash.html, licensed under the LGPL 2.1 */
+/* Hashing inspired by code from from
+   http://www.azillionmonkeys.com/qed/hash.html, licensed under the LGPL 2.1 */
 #undef get16bits
 #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
 #define get16bits(d) (*((const uint16_t *) (d)))
@@ -300,7 +217,6 @@ static hash_t hash(const UA_NodeId *n) {
 	case UA_NODEIDTYPE_BYTESTRING:
 		return hash_string((UA_Byte*) n->identifier.byteString.data, n->identifier.byteString.length);
 	default:
-		// Not permitted
 		return 0;
 	}
 }
@@ -318,10 +234,8 @@ static unsigned int higher_prime_index (unsigned long n) {
 			high = mid;
 	}
 
-	// Fixme: If we've run out of primes, abort.
 	/* if (n > prime_tab[low].prime) */
 	/* 	abort ();  */
-  
 	return low;
 }
 
@@ -355,20 +269,67 @@ static inline hash_t mod_m2 (hash_t h, const namespace *ns) {
 	return 1 + mod_1 (h, p->prime - 2, p->inv_m2, p->shift);
 }
 
-static UA_Int32 find_slot (const namespace *ns, UA_Node **slot, UA_NodeId *nodeid) {
+static inline void clear_slot(namespace *ns, ns_entry *slot) {
+	if(slot->node == UA_NULL)
+		return;
+	
+	pthread_rwlock_wrlock((pthread_rwlock_t *) slot->lock); /* Get write lock. */
+	switch(slot->node->nodeClass) {
+	case UA_NODECLASS_OBJECT:
+		UA_ObjectNode_delete((UA_ObjectNode *) slot->node);
+		break;
+	case UA_NODECLASS_VARIABLE:
+		UA_VariableNode_delete((UA_VariableNode *) slot->node);
+		break;
+	case UA_NODECLASS_METHOD:
+		UA_MethodNode_delete((UA_MethodNode *) slot->node);
+		break;
+	case UA_NODECLASS_OBJECTTYPE:
+		UA_ObjectTypeNode_delete((UA_ObjectTypeNode *) slot->node);
+		break;
+	case UA_NODECLASS_VARIABLETYPE:
+		UA_VariableTypeNode_delete((UA_VariableTypeNode *) slot->node);
+		break;
+	case UA_NODECLASS_REFERENCETYPE:
+		UA_ReferenceTypeNode_delete((UA_ReferenceTypeNode *) slot->node);
+		break;
+	case UA_NODECLASS_DATATYPE:
+		UA_DataTypeNode_delete((UA_DataTypeNode *) slot->node);
+		break;
+	case UA_NODECLASS_VIEW:
+		UA_ViewNode_delete((UA_ViewNode *) slot->node);
+		break;
+	default:
+		break;
+	}
+	slot->node = UA_NULL;
+	pthread_rwlock_destroy((pthread_rwlock_t *)slot->lock);
+	UA_free(slot->lock);
+}
+
+/* Delete all entries */
+static void clear_ns(namespace *ns) {
+	uint32_t size = ns->size;
+	ns_entry *entries = ns->entries;
+	for (uint32_t i = 0; i < size; i++)
+		clear_slot(ns, &entries[i]);
+	ns->count = 0;
+}
+
+static UA_Int32 find_slot (const namespace *ns, ns_entry **slot, UA_NodeId *nodeid) {
 	hash_t h = hash(nodeid);
 	hash_t index, hash2;
 	uint32_t size;
-	UA_Node *entry;
+	ns_entry *entry;
 
 	size = ns->size;
 	index = mod(h, ns);
 
-	entry = ns->entries[index];
+	entry = &ns->entries[index];
 	if (entry == UA_NULL)
 		return UA_ERROR;
-	if (UA_NodeId_compare(&(entry->nodeId), nodeid)) {
-		*slot = ns->entries[index];
+	if (UA_NodeId_compare(&entry->node->nodeId, nodeid)) {
+		*slot = entry;
 		return UA_SUCCESS;
 	}
 
@@ -378,11 +339,11 @@ static UA_Int32 find_slot (const namespace *ns, UA_Node **slot, UA_NodeId *nodei
 		if (index >= size)
 			index -= size;
 
-		entry = ns->entries[index];
+		entry = &ns->entries[index];
 		if (entry == UA_NULL)
 			return UA_ERROR;
-		if (UA_NodeId_compare(&(entry->nodeId), nodeid)) {
-			slot = &(ns->entries[index]);
+		if (UA_NodeId_compare(&entry->node->nodeId, nodeid)) {
+			*slot = entry;
 			return UA_SUCCESS;
 		}
     }
@@ -391,43 +352,42 @@ static UA_Int32 find_slot (const namespace *ns, UA_Node **slot, UA_NodeId *nodei
 
 /* Always returns an empty slot. This is inevitable if the entries are not
    completely full. */
-static UA_Node ** find_empty_slot(const namespace *ns, hash_t h) {
+static ns_entry * find_empty_slot(const namespace *ns, hash_t h) {
 	hash_t index = mod(h, ns);
 	uint32_t size = ns->size;
-	UA_Node **slot = ns->entries + index;
+	ns_entry *slot = &ns->entries[index];
 
-	if (*slot == UA_NULL)
+	if (slot->node == UA_NULL)
 		return slot;
 
-	hash_t hash2 = mod_m2 (h, ns);
+	hash_t hash2 = mod_m2(h, ns);
 	for (;;) {
 		index += hash2;
 		if (index >= size)
 			index -= size;
 
-		slot = ns->entries + index;
-		if (*slot == UA_NULL)
+		slot = &ns->entries[index];
+		if (slot->node == UA_NULL)
 			return slot;
 	}
 	return UA_NULL;
 }
 
-/* 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%.  Naturally the hash
-   table must already exist.  Remember also that the place of the
-   table entries is changed.  If memory allocation failures are allowed,
-   this function will return zero, indicating that the table could not be
-   expanded.  If all goes well, it will return a non-zero value.  */
+/* 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%. Naturally the hash table must already exist. Remember
+   also that the place of the table entries is changed. If memory allocation
+   failures are allowed, this function will return zero, indicating that the
+   table could not be expanded. If all goes well, it will return a non-zero
+   value. */
 static UA_Int32 expand (namespace *ns) {
-	UA_Node **p;
-	UA_Node **nentries;
+	ns_entry *nentries;
 	int32_t nsize;
 	uint32_t nindex;
 
-	UA_Node **oentries = ns->entries;
+	ns_entry *oentries = ns->entries;
 	int32_t osize = ns->size;
-	UA_Node **olimit = oentries + osize;
+	ns_entry *olimit = &oentries[osize];
 	int32_t count = ns->count;
 
 	/* Resize only when table after removal of unused elements is either too full or too empty.  */
@@ -437,19 +397,17 @@ static UA_Int32 expand (namespace *ns) {
 	}
 	return UA_SUCCESS;
 
-	if (UA_alloc((void **)nentries, sizeof(void *)*nsize) != UA_SUCCESS)
+	if (UA_alloc((void *)nentries, sizeof(ns_entry)*nsize) != UA_SUCCESS)
 		return UA_ERR_NO_MEMORY;
 	ns->entries = nentries;
 	ns->size = nsize;
 	ns->sizePrimeIndex = nindex;
 
-	p = oentries;
+	ns_entry *p = oentries;
 	do {
-		UA_Node *x = *p;
-
-		if (x != UA_NULL) {
-			UA_Node **q = find_empty_slot(ns, hash(&(x->nodeId)));
-			*q = x;
+		if (p->node != UA_NULL) {
+			ns_entry *q = find_empty_slot(ns, hash(&p->node->nodeId));
+			*q = *p;
 		}
 		p++;
 	}

+ 20 - 6
src/opcua_namespace.h

@@ -1,15 +1,25 @@
 #ifndef __NAMESPACE_H__
 #define __NAMESPACE_H__
 
+/* Defines needed for pthread_rwlock_t */
+#define _XOPEN_SOURCE 500
+
 #include "opcua_basictypes.h"
 #include "opcua.h"
+#include <pthread.h>
 
 typedef uint32_t hash_t;
+typedef struct pthread_rwlock_t ns_lock;
+
+typedef struct ns_entry_t {
+	ns_lock *lock; /* locks are heap-allocated, so we can resize the entry-array online */
+	UA_Node *node;
+} ns_entry;
 
 typedef struct namespace_t {
 	UA_Int32 namespaceId;
 	UA_String namespaceUri;
-	UA_Node **entries;
+	ns_entry *entries;
 	uint32_t size;
 	uint32_t count;
 	uint32_t sizePrimeIndex; /* Current size, as an index into the table of primes.  */
@@ -18,9 +28,11 @@ typedef struct namespace_t {
 UA_Int32 create_ns(namespace **result, uint32_t size);
 void empty_ns(namespace *ns);
 void delete_ns(namespace *ns);
-UA_Int32 insert(namespace *ns, UA_Node *node);
-UA_Int32 find(namespace *ns, UA_Node **result, UA_NodeId *nodeid);
-void delete(namespace *ns, UA_NodeId *nodeid);
+UA_Int32 insert_node(namespace *ns, UA_Node *node);
+UA_Int32 get_node(namespace *ns, UA_Node  ** const result, ns_lock ** lock, UA_NodeId *nodeid);
+UA_Int32 get_writable_node(namespace *ns, UA_Node  **result, ns_lock ** lock, UA_NodeId *nodeid);
+inline void unlock_node(ns_lock *lock);
+void delete_node(namespace *ns, UA_NodeId *nodeid);
 
 /* Internal */
 static hash_t hash_string(const UA_Byte * data, UA_Int32 len);
@@ -30,8 +42,10 @@ static unsigned int higher_prime_index (unsigned long n);
 static inline hash_t mod_1(hash_t x, hash_t y, hash_t inv, int shift);
 static inline hash_t mod(hash_t hash, const namespace *ns);
 static inline hash_t htab_mod_m2(hash_t hash, const namespace *ns);
-static UA_Int32 find_slot(const namespace *ns, UA_Node **slot, UA_NodeId *nodeid);
-static UA_Node ** find_empty_slot(const namespace *ns, hash_t hash);
+static inline void clear_slot(namespace *ns, ns_entry *slot);
+static void clear_ns(namespace *ns);
+static UA_Int32 find_slot(const namespace *ns, ns_entry **slot, UA_NodeId *nodeid);
+static ns_entry * find_empty_slot(const namespace *ns, hash_t hash);
 static UA_Int32 expand(namespace *ns);
 
 #endif /* __NAMESPACE_H */