⛏️ index : haiku.git

/*
 * Copyright 2001-2012, Axel DΓΆrfler, axeld@pinc-software.de.
 * This file may be used under the terms of the MIT License.
 */
#ifndef JOURNAL_H
#define JOURNAL_H


#include "system_dependencies.h"

#include "Volume.h"
#include "Utility.h"


struct run_array;
class Inode;
class LogEntry;
typedef DoublyLinkedList<LogEntry> LogEntryList;


class Journal {
public:
							Journal(Volume* volume);
							~Journal();

			status_t		InitCheck();

			status_t		Lock(Transaction* owner,
								bool separateSubTransactions);
			status_t		Unlock(Transaction* owner, bool success);

			status_t		ReplayLog();

			Transaction*	CurrentTransaction() const { return fOwner; }
			size_t			CurrentTransactionSize() const;
			bool			CurrentTransactionTooLarge() const;

			status_t		FlushLogAndBlocks();
			Volume*			GetVolume() const { return fVolume; }
			int32			TransactionID() const { return fTransactionID; }

	inline	uint32			FreeLogBlocks() const;

#ifdef BFS_DEBUGGER_COMMANDS
			void			Dump();
#endif

private:
			bool			_HasSubTransaction() const
								{ return fHasSubtransaction; }

			status_t		_FlushLog(bool canWait, bool flushBlocks);
			uint32			_TransactionSize() const;
			status_t		_WriteTransactionToLog();
			status_t		_CheckRunArray(const run_array* array);
			status_t		_ReplayRunArray(int32* start);
			status_t		_TransactionDone(bool success);

	static	void			_TransactionWritten(int32 transactionID,
								int32 event, void* _logEntry);
	static	void			_TransactionIdle(int32 transactionID, int32 event,
								void* _journal);
	static	status_t		_FlushLog(void* _journal);

private:
			Volume*			fVolume;
			recursive_lock	fLock;
			Transaction*	fOwner;
			uint32			fLogSize;
			uint32			fMaxTransactionSize;
			uint32			fUsed;
			int32			fUnwrittenTransactions;
			mutex			fEntriesLock;
			LogEntryList	fEntries;
			bigtime_t		fTimestamp;
			int32			fTransactionID;
			bool			fHasSubtransaction;
			bool			fSeparateSubTransactions;
};


inline uint32
Journal::FreeLogBlocks() const
{
	return fVolume->LogStart() <= fVolume->LogEnd()
		? fLogSize - fVolume->LogEnd() + fVolume->LogStart()
		: fVolume->LogStart() - fVolume->LogEnd();
}


class TransactionListener
	: public DoublyLinkedListLinkImpl<TransactionListener> {
public:
								TransactionListener();
	virtual						~TransactionListener();

	virtual void				TransactionDone(bool success) = 0;
	virtual void				RemovedFromTransaction() = 0;
};

typedef DoublyLinkedList<TransactionListener> TransactionListeners;


class Transaction {
public:
	Transaction(Volume* volume, off_t refBlock)
		:
		fJournal(NULL),
		fParent(NULL)
	{
		Start(volume, refBlock);
	}

	Transaction(Volume* volume, block_run refRun)
		:
		fJournal(NULL),
		fParent(NULL)
	{
		Start(volume, volume->ToBlock(refRun));
	}

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

	~Transaction()
	{
		if (fJournal != NULL)
			fJournal->Unlock(this, false);
	}

	status_t Start(Volume* volume, off_t refBlock);
	bool IsStarted() const { return fJournal != NULL; }

	status_t Done()
	{
		status_t status = B_OK;
		if (fJournal != NULL) {
			status = fJournal->Unlock(this, true);
			if (status == B_OK)
				fJournal = NULL;
		}
		return status;
	}

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

	bool IsTooLarge() const
	{
		return fJournal->CurrentTransactionTooLarge();
	}

	status_t WriteBlocks(off_t blockNumber, const uint8* buffer,
		size_t numBlocks = 1)
	{
		if (fJournal == NULL)
			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;
	}

	Volume* GetVolume() const
		{ return fJournal != NULL ? fJournal->GetVolume() : NULL; }
	int32 ID() const
		{ return fJournal->TransactionID(); }

	void AddListener(TransactionListener* listener);
	void RemoveListener(TransactionListener* listener);

	void NotifyListeners(bool success);
	void MoveListenersTo(Transaction* transaction);

	void SetParent(Transaction* parent)
		{ fParent = parent; }
	Transaction* Parent() const
		{ return fParent; }

private:
	Transaction(const Transaction& other);
	Transaction& operator=(const Transaction& other);
		// no implementation

	Journal*				fJournal;
	TransactionListeners	fListeners;
	Transaction*			fParent;
};


#ifdef BFS_DEBUGGER_COMMANDS
int dump_journal(int argc, char** argv);
#endif


#endif	// JOURNAL_H