1/*
2 * Copyright 2001-2012, Axel D��rfler, axeld@pinc-software.de.
3 * This file may be used under the terms of the MIT License.
4 */
5#ifndef JOURNAL_H
6#define JOURNAL_H
7
8
9#include "system_dependencies.h"
10
11#include "Volume.h"
12#include "Utility.h"
13
14
15struct run_array;
16class Inode;
17class LogEntry;
18typedef DoublyLinkedList<LogEntry> LogEntryList;
19
20
21class Journal {
22public:
23							Journal(Volume* volume);
24							~Journal();
25
26			status_t		InitCheck();
27
28			status_t		Lock(Transaction* owner,
29								bool separateSubTransactions);
30			status_t		Unlock(Transaction* owner, bool success);
31
32			status_t		ReplayLog();
33
34			Transaction*	CurrentTransaction() const { return fOwner; }
35			size_t			CurrentTransactionSize() const;
36			bool			CurrentTransactionTooLarge() const;
37
38			status_t		FlushLogAndBlocks();
39			Volume*			GetVolume() const { return fVolume; }
40			int32			TransactionID() const { return fTransactionID; }
41
42	inline	uint32			FreeLogBlocks() const;
43
44#ifdef BFS_DEBUGGER_COMMANDS
45			void			Dump();
46#endif
47
48private:
49			bool			_HasSubTransaction() const
50								{ return fHasSubtransaction; }
51
52			status_t		_FlushLog(bool canWait, bool flushBlocks);
53			uint32			_TransactionSize() const;
54			status_t		_WriteTransactionToLog();
55			status_t		_CheckRunArray(const run_array* array);
56			status_t		_ReplayRunArray(int32* start);
57			status_t		_TransactionDone(bool success);
58
59	static	void			_TransactionWritten(int32 transactionID,
60								int32 event, void* _logEntry);
61	static	void			_TransactionIdle(int32 transactionID, int32 event,
62								void* _journal);
63	static	status_t		_LogFlusher(void* _journal);
64
65private:
66			Volume*			fVolume;
67			recursive_lock	fLock;
68			Transaction*	fOwner;
69			uint32			fLogSize;
70			uint32			fMaxTransactionSize;
71			uint32			fUsed;
72			int32			fUnwrittenTransactions;
73			mutex			fEntriesLock;
74			LogEntryList	fEntries;
75			bigtime_t		fTimestamp;
76			int32			fTransactionID;
77			bool			fHasSubtransaction;
78			bool			fSeparateSubTransactions;
79
80			thread_id		fLogFlusher;
81			sem_id			fLogFlusherSem;
82};
83
84
85inline uint32
86Journal::FreeLogBlocks() const
87{
88	return fVolume->LogStart() <= fVolume->LogEnd()
89		? fLogSize - fVolume->LogEnd() + fVolume->LogStart()
90		: fVolume->LogStart() - fVolume->LogEnd();
91}
92
93
94class TransactionListener
95	: public DoublyLinkedListLinkImpl<TransactionListener> {
96public:
97								TransactionListener();
98	virtual						~TransactionListener();
99
100	virtual void				TransactionDone(bool success) = 0;
101	virtual void				RemovedFromTransaction() = 0;
102};
103
104typedef DoublyLinkedList<TransactionListener> TransactionListeners;
105
106
107class Transaction {
108public:
109	Transaction(Volume* volume, off_t refBlock)
110		:
111		fJournal(NULL),
112		fParent(NULL)
113	{
114		Start(volume, refBlock);
115	}
116
117	Transaction(Volume* volume, block_run refRun)
118		:
119		fJournal(NULL),
120		fParent(NULL)
121	{
122		Start(volume, volume->ToBlock(refRun));
123	}
124
125	Transaction()
126		:
127		fJournal(NULL),
128		fParent(NULL)
129	{
130	}
131
132	~Transaction()
133	{
134		if (fJournal != NULL)
135			fJournal->Unlock(this, false);
136	}
137
138	status_t Start(Volume* volume, off_t refBlock);
139	bool IsStarted() const { return fJournal != NULL; }
140
141	status_t Done()
142	{
143		status_t status = B_OK;
144		if (fJournal != NULL) {
145			status = fJournal->Unlock(this, true);
146			if (status == B_OK)
147				fJournal = NULL;
148		}
149		return status;
150	}
151
152	bool HasParent() const
153	{
154		return fParent != NULL;
155	}
156
157	bool IsTooLarge() const
158	{
159		return fJournal->CurrentTransactionTooLarge();
160	}
161
162	status_t WriteBlocks(off_t blockNumber, const uint8* buffer,
163		size_t numBlocks = 1)
164	{
165		if (fJournal == NULL)
166			return B_NO_INIT;
167
168		void* cache = GetVolume()->BlockCache();
169		size_t blockSize = GetVolume()->BlockSize();
170
171		for (size_t i = 0; i < numBlocks; i++) {
172			void* block = block_cache_get_empty(cache, blockNumber + i,
173				ID());
174			if (block == NULL)
175				return B_ERROR;
176
177			memcpy(block, buffer, blockSize);
178			buffer += blockSize;
179
180			block_cache_put(cache, blockNumber + i);
181		}
182
183		return B_OK;
184	}
185
186	Volume* GetVolume() const
187		{ return fJournal != NULL ? fJournal->GetVolume() : NULL; }
188	int32 ID() const
189		{ return fJournal->TransactionID(); }
190
191	void AddListener(TransactionListener* listener);
192	void RemoveListener(TransactionListener* listener);
193
194	void NotifyListeners(bool success);
195	void MoveListenersTo(Transaction* transaction);
196
197	void SetParent(Transaction* parent)
198		{ fParent = parent; }
199	Transaction* Parent() const
200		{ return fParent; }
201
202private:
203	Transaction(const Transaction& other);
204	Transaction& operator=(const Transaction& other);
205		// no implementation
206
207	Journal*				fJournal;
208	TransactionListeners	fListeners;
209	Transaction*			fParent;
210};
211
212
213#ifdef BFS_DEBUGGER_COMMANDS
214int dump_journal(int argc, char** argv);
215#endif
216
217
218#endif	// JOURNAL_H
219