⛏️ index : haiku.git

/*
 * Copyright 2010, Haiku Inc. All rights reserved.
 * Copyright 2001-2010, Axel DΓΆrfler, axeld@pinc-software.de.
 * This file may be used under the terms of the MIT License.
 *
 * Authors:
 *		Janito V. Ferreira Filho
 */


#include "Transaction.h"

#include <string.h>

#include <fs_cache.h>

#include "Journal.h"


//#define TRACE_EXT2
#ifdef TRACE_EXT2
#	define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
#else
#	define TRACE(x...) ;
#endif


TransactionListener::TransactionListener()
{
}


TransactionListener::~TransactionListener()
{
}


Transaction::Transaction()
	:
	fJournal(NULL),
	fParent(NULL)
{
}


Transaction::Transaction(Journal* journal)
	:
	fJournal(NULL),
	fParent(NULL)
{
	Start(journal);
}


Transaction::~Transaction()
{
	if (IsStarted())
		fJournal->Unlock(this, false);
}

status_t
Transaction::Start(Journal* journal)
{
	if (IsStarted())
		return B_OK;

	fJournal = journal;
	if (fJournal == NULL)
		return B_ERROR;
	
	status_t status = fJournal->Lock(this, false);
	if (status != B_OK)
		fJournal = NULL;

	return status;
}


status_t
Transaction::Done(bool success)
{
	if (!IsStarted())
		return B_OK;

	status_t status = fJournal->Unlock(this, success);
	
	if (status == B_OK)
		fJournal = NULL;

	return status;
}


int32
Transaction::ID() const
{
	if (!IsStarted())
		return -1;

	return fJournal->TransactionID();
}


bool
Transaction::IsStarted() const
{
	return fJournal != NULL;
}


bool
Transaction::HasParent() const
{
	return fParent != NULL;
}


status_t
Transaction::WriteBlocks(off_t blockNumber, const uint8* buffer,
	size_t numBlocks)
{
	if (!IsStarted())
		return B_NO_INIT;

	void* cache = GetVolume()->BlockCache();
	size_t blockSize = GetVolume()->BlockSize();

	for (size_t i = 0; i < numBlocks; ++i) {
		void* block = block_cache_get_empty(cache, blockNumber + i, ID());
		if (block == NULL)
			return B_ERROR;
		
		memcpy(block, buffer, blockSize);
		buffer += blockSize;

		block_cache_put(cache, blockNumber + i);
	}

	return B_OK;
}


void
Transaction::Split()
{
	cache_start_sub_transaction(fJournal->GetFilesystemVolume()->BlockCache(),
		ID());
}


Volume*
Transaction::GetVolume() const
{
	if (!IsStarted())
		return NULL;

	return fJournal->GetFilesystemVolume();
}


void
Transaction::AddListener(TransactionListener* listener)
{
	TRACE("Transaction::AddListener()\n");
	if (!IsStarted())
		panic("Transaction is not running!");

	fListeners.Add(listener);
}


void
Transaction::RemoveListener(TransactionListener* listener)
{
	TRACE("Transaction::RemoveListener()\n");
	if (!IsStarted())
		panic("Transaction is not running!");

	fListeners.Remove(listener);
	listener->RemovedFromTransaction();
}


void
Transaction::NotifyListeners(bool success)
{
	TRACE("Transaction::NotifyListeners(): fListeners.First(): %p\n",
		fListeners.First());
	if (success) {
		TRACE("Transaction::NotifyListeners(true): Number of listeners: %"
			B_PRId32 "\n", fListeners.Count());
	} else {
		TRACE("Transaction::NotifyListeners(false): Number of listeners: %"
			B_PRId32 "\n", fListeners.Count());
	}
	TRACE("Transaction::NotifyListeners(): Finished counting\n");

	while (TransactionListener* listener = fListeners.RemoveHead()) {
		listener->TransactionDone(success);
		listener->RemovedFromTransaction();
	}
}


void
Transaction::MoveListenersTo(Transaction* transaction)
{
	TRACE("Transaction::MoveListenersTo()\n");
	while (TransactionListener* listener = fListeners.RemoveHead())
		transaction->fListeners.Add(listener);
}


void
Transaction::SetParent(Transaction* transaction)
{
	fParent = transaction;
}


Transaction*
Transaction::Parent() const
{
	return fParent;
}