view runtime/transaction.c @ 186:ba35ab624ec2

Add support for raw C function output from C backend as well as an option to use Boehm-GC instead of reference counting
author Mike Pavone <pavone@retrodev.com>
date Fri, 07 Oct 2011 00:10:02 -0700
parents a68e6828d896
children
line wrap: on
line source

#include "transaction.h"
#include <stdarg.h>
#include <string.h>
#ifdef USE_GC
#include <gc/gc.h>
#else
#include <stdlib.h>
#endif

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; 
}

#ifdef RAW_FUNC
transaction * cur_transaction;
void real_begin_transaction(int numobjs,...)
#else
void begin_transaction(context * ct, int numobjs,...)
#endif
{
	mutable_object *obj;
	transaction *parent;
	va_list args;
	int32_t idx,got_global_lock=0;
	
	parent = cur_transaction ? cur_transaction : NULL;
	
#ifdef USE_GC
	cur_transaction = GC_malloc(sizeof(transaction)+((numobjs-1)*sizeof(trans_cell)));
#else
	cur_transaction = malloc(sizeof(transaction)+((numobjs-1)*sizeof(trans_cell)));
#endif
	cur_transaction->parent = parent;
	cur_transaction->chain = NULL;
	rh_mutex_init(ct->lock);
	cur_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 *);
				cur_transaction->cells[idx].obj = obj;
				cur_transaction->cells[idx].parent = find_obj_cell(parent, obj);
				if (cur_transaction->cells[idx].parent)
				{
					cur_transaction->cells[idx].local_data = cur_transaction->cells[idx].parent->local_data;
					cur_transaction->cells[idx].orig_version = cur_transaction->cells[idx].parent->local_version;
				} else {
					if (!got_global_lock)
					{
						rh_lock(trans_lock);
						got_global_lock = 1;
					}
					cur_transaction->cells[idx].local_data = obj->data;
					cur_transaction->cells[idx].orig_version = obj->version;
				}
				cur_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 *);
				cur_transaction->cells[idx].obj = obj;
				cur_transaction->cells[idx].parent = NULL;
				cur_transaction->cells[idx].local_data = add_ref(obj->data);
				cur_transaction->cells[idx].orig_version = obj->version;
				cur_transaction->cells[idx].local_version = 0;
			}
		rh_unlock(trans_lock);
	}
}

#ifdef USE_GC
#define free_trans(trans)
#else
void free_trans(transaction * trans)
{
	if (trans)
	{
		free_trans(trans->chain);
		free(trans);
	}
}
#endif

#ifdef RAW_FUNC
int32_t real_commit_transaction(int32_t readonly)
#else
int32_t commit_transaction(context * ct, int32_t readonly)
#endif
{
	transaction *tmp_trans, *current;
	object * tmp_obj;
	int32_t idx,numaddparent;

	if (cur_transaction->parent)
	{
		rh_lock(cur_transaction->parent->lock);
			current = cur_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(cur_transaction->parent->lock);
							return 0;
						}
					} else {
						if(find_obj_cell(cur_transaction->parent->chain, current->cells[idx].obj))
						{
							rh_unlock(cur_transaction->parent->lock);
							return 0;
						} else
							numaddparent++;
					}
				}
				current = current->chain;
			}
			if (numaddparent)
			{
#ifdef USE_GC
				tmp_trans = GC_malloc(sizeof(transaction)+(numaddparent - 1)*sizeof(trans_cell));
#else
				tmp_trans = malloc(sizeof(transaction)+(numaddparent - 1)*sizeof(trans_cell));
#endif
				tmp_trans->chain = cur_transaction->parent->chain;
				tmp_trans->num_cells = 0;
				cur_transaction->parent->chain = tmp_trans;
			}
			current = cur_transaction;
			while(current)
			{
				for (idx = 0; idx < cur_transaction->num_cells; ++idx)
				{
					if (cur_transaction->cells[idx].parent)
					{
						//Only commit a particular object if a change has been made
						if (cur_transaction->cells[idx].local_version)
						{
							tmp_obj = cur_transaction->cells[idx].parent->local_data;
							cur_transaction->cells[idx].parent->local_data = cur_transaction->cells[idx].local_data;
							release_ref(tmp_obj);
							cur_transaction->cells[idx].parent->local_version++;
						} else {
							release_ref(cur_transaction->cells[idx].local_data);
						}
					} else {
						memcpy(&(tmp_trans->cells[tmp_trans->num_cells++]), &(cur_transaction->cells[idx]), sizeof(trans_cell));
					}
				}
				current = current->chain;
			}
		rh_unlock(cur_transaction->parent->lock);
	} else {
		if(readonly)
		{
			for (idx = 0; idx < cur_transaction->num_cells; ++idx)
			{
				release_ref(cur_transaction->cells[idx].local_data);
			}
		} else {
			rh_lock(trans_lock);
				current = cur_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 = cur_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(cur_transaction->lock);
	tmp_trans = cur_transaction->parent;
	free_trans(cur_transaction);
	cur_transaction = tmp_trans;
	return 1;
}


#ifdef RAW_FUNC
void prep_retry()
#else
void prep_retry(context * ct)
#endif
{
	transaction * current;
	int32_t idx,got_global_lock=0;
	if (cur_transaction->parent)
	{
		rh_lock(cur_transaction->parent->lock);
			current = cur_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(cur_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(cur_transaction->parent->lock);
	} else {
		rh_lock(trans_lock);
			current = cur_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);
	}
}