diff runtime/transaction.c @ 139:a68e6828d896

Global stores and transactions are working. Definately leaks memory on retries. Probably a fair number of bugs to work out. However, a basic test program works.
author Mike Pavone <pavone@retrodev.com>
date Fri, 19 Nov 2010 04:04:14 -0500
parents
children ba35ab624ec2
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/runtime/transaction.c	Fri Nov 19 04:04:14 2010 -0500
@@ -0,0 +1,255 @@
+#include "transaction.h"
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+
+rh_mutex(trans_lock)
+
+trans_cell * find_obj_cell(transaction * trans, mutable_object * to_find)
+{
+	int32_t idx;
+	while(trans)
+	{
+		for (idx = 0; idx < trans->num_cells; ++idx)
+			if (trans->cells[idx].obj = to_find)
+				return &(trans->cells[idx]);
+		trans = trans->chain;
+	}
+	return NULL;
+}
+
+void begin_transaction(context * ct, int numobjs,...)
+{
+	mutable_object *obj;
+	transaction *parent;
+	va_list args;
+	int32_t idx,got_global_lock=0;
+	
+	parent = ct->transaction ? ct->transaction : NULL;
+	
+	ct->transaction = malloc(sizeof(transaction)+((numobjs-1)*sizeof(trans_cell)));
+	ct->transaction->parent = parent;
+	ct->transaction->chain = NULL;
+	rh_mutex_init(ct->lock);
+	ct->transaction->num_cells = numobjs;
+	va_start(args, numobjs);
+	if (parent)
+	{
+		rh_lock(parent->lock);
+			for (idx = 0; idx < numobjs; ++idx)
+			{
+				obj = va_arg(args, mutable_object *);
+				ct->transaction->cells[idx].obj = obj;
+				ct->transaction->cells[idx].parent = find_obj_cell(parent, obj);
+				if (ct->transaction->cells[idx].parent)
+				{
+					ct->transaction->cells[idx].local_data = ct->transaction->cells[idx].parent->local_data;
+					ct->transaction->cells[idx].orig_version = ct->transaction->cells[idx].parent->local_version;
+				} else {
+					if (!got_global_lock)
+					{
+						rh_lock(trans_lock);
+						got_global_lock = 1;
+					}
+					ct->transaction->cells[idx].local_data = obj->data;
+					ct->transaction->cells[idx].orig_version = obj->version;
+				}
+				ct->transaction->cells[idx].local_version = 0;
+			}
+			if (got_global_lock)
+			{
+				rh_unlock(trans_lock);
+			}
+		rh_unlock(parent->lock);
+	} else {
+		rh_lock(trans_lock);
+			for (idx = 0; idx < numobjs; ++idx)
+			{
+				obj = va_arg(args, mutable_object *);
+				ct->transaction->cells[idx].obj = obj;
+				ct->transaction->cells[idx].parent = NULL;
+				ct->transaction->cells[idx].local_data = add_ref(obj->data);
+				ct->transaction->cells[idx].orig_version = obj->version;
+				ct->transaction->cells[idx].local_version = 0;
+			}
+		rh_unlock(trans_lock);
+	}
+}
+
+void free_trans(transaction * trans)
+{
+	if (trans)
+	{
+		free_trans(trans->chain);
+		free(trans);
+	}
+}
+
+int32_t commit_transaction(context * ct, int32_t readonly)
+{
+	transaction *tmp_trans, *current;
+	object * tmp_obj;
+	int32_t idx,numaddparent;
+
+	if (ct->transaction->parent)
+	{
+		rh_lock(ct->transaction->parent->lock);
+			current = ct->transaction;
+			while(current)
+			{
+				for (idx = 0; idx < current->num_cells; ++idx)
+				{
+					if (current->cells[idx].parent)
+					{
+						if (current->cells[idx].parent->local_version != current->cells[idx].orig_version)
+						{
+							rh_unlock(ct->transaction->parent->lock);
+							return 0;
+						}
+					} else {
+						if(find_obj_cell(ct->transaction->parent->chain, current->cells[idx].obj))
+						{
+							rh_unlock(ct->transaction->parent->lock);
+							return 0;
+						} else
+							numaddparent++;
+					}
+				}
+				current = current->chain;
+			}
+			if (numaddparent)
+			{
+				tmp_trans = malloc(sizeof(transaction)+(numaddparent - 1)*sizeof(trans_cell));
+				tmp_trans->chain = ct->transaction->parent->chain;
+				tmp_trans->num_cells = 0;
+				ct->transaction->parent->chain = tmp_trans;
+			}
+			current = ct->transaction;
+			while(current)
+			{
+				for (idx = 0; idx < ct->transaction->num_cells; ++idx)
+				{
+					if (ct->transaction->cells[idx].parent)
+					{
+						//Only commit a particular object if a change has been made
+						if (ct->transaction->cells[idx].local_version)
+						{
+							tmp_obj = ct->transaction->cells[idx].parent->local_data;
+							ct->transaction->cells[idx].parent->local_data = ct->transaction->cells[idx].local_data;
+							release_ref(tmp_obj);
+							ct->transaction->cells[idx].parent->local_version++;
+						} else {
+							release_ref(ct->transaction->cells[idx].local_data);
+						}
+					} else {
+						memcpy(&(tmp_trans->cells[tmp_trans->num_cells++]), &(ct->transaction->cells[idx]), sizeof(trans_cell));
+					}
+				}
+				current = current->chain;
+			}
+		rh_unlock(ct->transaction->parent->lock);
+	} else {
+		if(readonly)
+		{
+			for (idx = 0; idx < ct->transaction->num_cells; ++idx)
+			{
+				release_ref(ct->transaction->cells[idx].local_data);
+			}
+		} else {
+			rh_lock(trans_lock);
+				current = ct->transaction;
+				while(current)
+				{
+					for (idx = 0; idx < current->num_cells; ++idx)
+					{
+						if (current->cells[idx].obj->version != current->cells[idx].orig_version)
+						{
+							rh_unlock(trans_lock);
+							return 0;
+						}
+					}
+					current = current->chain;
+				}
+				current = ct->transaction;
+				while(current)
+				{
+					for (idx = 0; idx < current->num_cells; ++idx)
+					{
+						//Only commit a particular object if a change has been made
+						if (current->cells[idx].local_version)
+						{
+							tmp_obj = current->cells[idx].obj->data;
+							current->cells[idx].obj->data = current->cells[idx].local_data;
+							release_ref(tmp_obj);
+							current->cells[idx].obj->version++;
+						} else {
+							release_ref(current->cells[idx].local_data);
+						}
+					}
+					current = current->chain;
+				}
+			rh_unlock(trans_lock);
+		}
+	}
+	rh_mutex_del(ct->transaction->lock);
+	tmp_trans = ct->transaction->parent;
+	free_trans(ct->transaction);
+	ct->transaction = tmp_trans;
+	return 1;
+}
+
+void prep_retry(context * ct)
+{
+	transaction * current;
+	int32_t idx,got_global_lock=0;
+	if (ct->transaction->parent)
+	{
+		rh_lock(ct->transaction->parent->lock);
+			current = ct->transaction;
+			while(current)
+			{
+				for (idx = 0; idx < current->num_cells; ++idx)
+				{
+					release_ref(current->cells[idx].local_data);
+					current->cells[idx].local_version = 0;
+					if (!current->cells[idx].parent)
+						current->cells[idx].parent = find_obj_cell(ct->transaction->parent, current->cells[idx].obj);
+					if (current->cells[idx].parent)
+					{
+						current->cells[idx].local_data = current->cells[idx].parent->local_data;
+						current->cells[idx].orig_version = current->cells[idx].parent->local_version;
+					} else {
+						if (!got_global_lock)
+						{
+							rh_lock(trans_lock);
+							got_global_lock = 1;
+						}
+						current->cells[idx].local_data = current->cells[idx].obj->data;
+						current->cells[idx].orig_version = current->cells[idx].obj->version;
+					}
+				}
+				current = current->chain;
+			}
+			if (got_global_lock)
+			{
+				rh_unlock(trans_lock);
+			}
+		rh_unlock(ct->transaction->parent->lock);
+	} else {
+		rh_lock(trans_lock);
+			current = ct->transaction;
+			while(current)
+			{
+				for (idx = 0; idx < current->num_cells; ++idx)
+				{
+					release_ref(current->cells[idx].local_data);
+					current->cells[idx].local_version = 0;
+					current->cells[idx].local_data = current->cells[idx].obj->data;
+					current->cells[idx].orig_version = current->cells[idx].obj->version;
+				}
+				current = current->chain;
+			}
+		rh_unlock(trans_lock);
+	}
+}
+