Parcourir la source

Trivial transactions implementation for namespace

Julius Pfrommer il y a 11 ans
Parent
commit
3275f4930b
2 fichiers modifiés avec 71 ajouts et 21 suppressions
  1. 47 17
      src/opcua_namespace.c
  2. 24 4
      src/opcua_namespace.h

+ 47 - 17
src/opcua_namespace.c

@@ -2,6 +2,10 @@
 #include <string.h>
 #include <stdio.h>
 
+UA_Int32 init_tc(transaction_context * tc) {
+	return UA_list_init((UA_list_List*) tc);
+}
+
 /* 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) */
@@ -107,31 +111,57 @@ UA_Int32 insert_node(namespace *ns, UA_Node *node) {
 	return UA_SUCCESS;
 }
 
-UA_Int32 get_node(namespace *ns, UA_NodeId *nodeid, UA_Node ** const result, ns_lock ** lock);
+UA_Int32 get_node(namespace *ns, UA_NodeId *nodeid, UA_Node ** const result, ns_lock ** lock) {
 	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;
+	if(find_slot(ns, &slot, nodeid) == UA_SUCCESS) return UA_ERROR;
+	if(pthread_rwlock_rdlock((pthread_rwlock_t *)slot->lock) != 0) return UA_ERROR;
+	*result = slot->node;
+	*lock = slot->lock;
+	return UA_SUCCESS;
+}
+
+UA_Int32 get_writable_node(namespace *ns, UA_NodeId *nodeid, UA_Node **result, ns_lock ** lock) {
+	ns_entry *slot;
+	if(find_slot(ns, &slot, nodeid) != UA_SUCCESS) return UA_ERROR;
+	if(pthread_rwlock_wrlock((pthread_rwlock_t *)slot->lock) != 0) return UA_ERROR;
+	*result = slot->node;
+	*lock = slot->lock;
+	return UA_SUCCESS;
+}
+
+static inline void release_context_walker(void * lock) { pthread_rwlock_unlock(lock); }
+
+UA_Int32 get_tc_node(namespace *ns, transaction_context *tc, UA_NodeId *nodeid, UA_Node ** const result, ns_lock ** lock) {
+	ns_entry *slot;
+	if(find_slot(ns, &slot, nodeid) != UA_SUCCESS) return UA_ERROR;
+	if(pthread_rwlock_tryrdlock((pthread_rwlock_t *)slot->lock) != 0) {
+		/* Transaction failed. Release all acquired locks and bail out. */
+		UA_list_destroy((UA_list_List*) tc, release_context_walker);
+		return UA_ERROR;
 	}
-	return UA_ERROR;
+
+	UA_list_addPayloadToBack((UA_list_List*) tc, slot->lock);
+	*result = slot->node;
+	*lock = slot->lock;
+	return UA_SUCCESS;
 }
 
-UA_Int32 get_writable_node(namespace *ns, UA_NodeId *nodeid, UA_Node **result, ns_lock ** lock);
+UA_Int32 get_tc_writable_node(namespace *ns, transaction_context *tc, UA_NodeId *nodeid, UA_Node **result, ns_lock ** lock) {
 	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;
+	if(find_slot(ns, &slot, nodeid) != UA_SUCCESS) return UA_ERROR;
+	if(pthread_rwlock_trywrlock((pthread_rwlock_t *)slot->lock) != 0) {
+		/* Transaction failed. Release all acquired locks and bail out. */
+		UA_list_destroy((UA_list_List*) tc, release_context_walker);
+		return UA_ERROR;
 	}
-	return UA_ERROR;
+
+	UA_list_addPayloadToBack((UA_list_List*) tc, slot->lock);
+	*result = slot->node;
+	*lock = slot->lock;
+	return UA_SUCCESS;
 }
 
-inline void unlock_node(ns_lock *lock) {
+inline void release_node(ns_lock *lock) {
 	pthread_rwlock_unlock((pthread_rwlock_t *)lock);
 }
 

+ 24 - 4
src/opcua_namespace.h

@@ -3,14 +3,31 @@
 
 /* Defines needed for pthread_rwlock_t */
 #define _XOPEN_SOURCE 500
+#include <pthread.h>
 
 #include "opcua_basictypes.h"
 #include "opcua.h"
-#include <pthread.h>
+#include "UA_list.h"
 
-typedef uint32_t hash_t;
 typedef struct pthread_rwlock_t ns_lock;
 
+/* Poor-man's transactions: If we need multiple locks and at least one of them
+   is a writelock ("transaction"), a deadlock can be introduced in conjunction
+   with a second thread.
+
+   Convention: All nodes in a transaction (read and write) must be locked before
+   the first write. If one write-lock cannot be acquired immediately, bail out
+   and restart the transaction.
+
+   A transaction_context is currently only a linked list of the acquired locks.
+
+   More advanced transaction mechanisms will be established once the runtime
+   behavior can be observed. */
+typedef UA_list_List transaction_context;
+UA_Int32 init_tc(transaction_context * tc);
+
+/* Each namespace is a hash-map of NodeIds to Nodes. Every entry in the hashmap
+   consists of a pointer to a read-write lock and a pointer to the Node. */
 typedef struct ns_entry_t {
 	ns_lock *lock; /* locks are heap-allocated, so we can resize the entry-array online */
 	UA_Node *node;
@@ -30,11 +47,14 @@ void empty_ns(namespace *ns);
 void delete_ns(namespace *ns);
 UA_Int32 insert_node(namespace *ns, UA_Node *node);
 UA_Int32 get_node(namespace *ns, UA_NodeId *nodeid, UA_Node ** const result, ns_lock ** lock);
-UA_Int32 get_writable_node(namespace *ns, UA_NodeId *nodeid, UA_Node **result, ns_lock ** lock);
-inline void unlock_node(ns_lock *lock);
+UA_Int32 get_writable_node(namespace *ns, UA_NodeId *nodeid, UA_Node **result, ns_lock ** lock); // use only for _single_ writes.
+UA_Int32 get_tc_node(namespace *ns, transaction_context *tc, UA_NodeId *nodeid, UA_Node ** const result, ns_lock ** lock);
+UA_Int32 get_tc_writable_node(namespace *ns, transaction_context *tc, UA_NodeId *nodeid, UA_Node **result, ns_lock ** lock); // use only for _single_ writes.
+inline void release_node(ns_lock *lock);
 void delete_node(namespace *ns, UA_NodeId *nodeid);
 
 /* Internal */
+typedef uint32_t hash_t;
 static hash_t hash_string(const UA_Byte * data, UA_Int32 len);
 static hash_t hash(const UA_NodeId *n);