xref: /haiku/src/add-ons/kernel/file_systems/ramfs/LastModifiedIndex.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
1 /*
2  * Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * All rights reserved. Distributed under the terms of the MIT license.
4  */
5 
6 #include <TypeConstants.h>
7 
8 #include "DebugSupport.h"
9 #include "Entry.h"
10 #include "EntryListener.h"
11 #include "IndexImpl.h"
12 #include "LastModifiedIndex.h"
13 #include "Node.h"
14 #include "NodeListener.h"
15 #include "Volume.h"
16 
17 // LastModifiedIndexPrimaryKey
18 class LastModifiedIndexPrimaryKey {
19 public:
20 	LastModifiedIndexPrimaryKey(Node *node, time_t modified)
21 		: node(node), modified(modified) {}
22 	LastModifiedIndexPrimaryKey(Node *node)
23 		: node(node), modified(node->GetMTime()) {}
24 	LastModifiedIndexPrimaryKey(time_t modified)
25 		: node(NULL), modified(modified) {}
26 
27 	Node	*node;
28 	time_t	modified;
29 };
30 
31 // LastModifiedIndexGetPrimaryKey
32 class LastModifiedIndexGetPrimaryKey {
33 public:
34 	inline LastModifiedIndexPrimaryKey operator()(Node *a)
35 	{
36 		return LastModifiedIndexPrimaryKey(a);
37 	}
38 
39 	inline LastModifiedIndexPrimaryKey operator()(Node *a) const
40 	{
41 		return LastModifiedIndexPrimaryKey(a);
42 	}
43 };
44 
45 // LastModifiedIndexPrimaryKeyCompare
46 class LastModifiedIndexPrimaryKeyCompare
47 {
48 public:
49 	inline int operator()(const LastModifiedIndexPrimaryKey &a,
50 						  const LastModifiedIndexPrimaryKey &b) const
51 	{
52 		if (a.node != NULL && a.node == b.node)
53 			return 0;
54 		if (a.modified < b.modified)
55 			return -1;
56 		if (a.modified > b.modified)
57 			return 1;
58 		return 0;
59 	}
60 };
61 
62 
63 // NodeTree
64 typedef TwoKeyAVLTree<Node*, LastModifiedIndexPrimaryKey,
65 					  LastModifiedIndexPrimaryKeyCompare,
66 					  LastModifiedIndexGetPrimaryKey>
67 		_NodeTree;
68 class LastModifiedIndex::NodeTree : public _NodeTree {};
69 
70 
71 // IteratorList
72 class LastModifiedIndex::IteratorList : public DoublyLinkedList<Iterator> {};
73 
74 
75 // Iterator
76 class LastModifiedIndex::Iterator
77 	: public NodeEntryIterator<LastModifiedIndex::NodeTree::Iterator>,
78 	  public DoublyLinkedListLinkImpl<Iterator>, public EntryListener,
79 	  public NodeListener {
80 public:
81 	Iterator();
82 	virtual ~Iterator();
83 
84 	virtual Entry *GetCurrent();
85 	virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
86 
87 	virtual status_t Suspend();
88 	virtual status_t Resume();
89 
90 	bool SetTo(LastModifiedIndex *index, time_t modified,
91 			   bool ignoreValue = false);
92 	void Unset();
93 
94 	virtual void EntryRemoved(Entry *entry);
95 	virtual void NodeRemoved(Node *node);
96 
97 private:
98 	typedef NodeEntryIterator<LastModifiedIndex::NodeTree::Iterator> BaseClass;
99 
100 private:
101 	LastModifiedIndex						*fIndex;
102 };
103 
104 
105 // LastModifiedIndex
106 
107 // constructor
108 LastModifiedIndex::LastModifiedIndex(Volume *volume)
109 	: Index(volume, "last_modified", B_INT32_TYPE, true, sizeof(time_t)),
110 	  fNodes(new(nothrow) NodeTree),
111 	  fIterators(new(nothrow) IteratorList)
112 {
113 	if (fInitStatus == B_OK && (!fNodes || !fIterators))
114 		fInitStatus = B_NO_MEMORY;
115 	if (fInitStatus == B_OK) {
116 		fInitStatus = fVolume->AddNodeListener(this,
117 			NULL, NODE_LISTEN_ANY_NODE | NODE_LISTEN_ALL);
118 	}
119 }
120 
121 // destructor
122 LastModifiedIndex::~LastModifiedIndex()
123 {
124 	if (fVolume)
125 		fVolume->RemoveNodeListener(this, NULL);
126 	if (fIterators) {
127 		// unset the iterators
128 		for (Iterator *iterator = fIterators->First();
129 			 iterator;
130 			 iterator = fIterators->GetNext(iterator)) {
131 			iterator->SetTo(NULL, 0);
132 		}
133 		delete fIterators;
134 	}
135 	if (fNodes)
136 		delete fNodes;
137 }
138 
139 // CountEntries
140 int32
141 LastModifiedIndex::CountEntries() const
142 {
143 	return fNodes->CountItems();
144 }
145 
146 // Changed
147 status_t
148 LastModifiedIndex::Changed(Node *node, time_t oldModified)
149 {
150 	status_t error = B_BAD_VALUE;
151 	if (node) {
152 		NodeTree::Iterator it;
153 		Node **foundNode = fNodes->Find(LastModifiedIndexPrimaryKey(node,
154 			oldModified), node, &it);
155 		if (foundNode && *foundNode == node) {
156 			// update the iterators
157 			for (Iterator *iterator = fIterators->First();
158 				 iterator;
159 				 iterator = fIterators->GetNext(iterator)) {
160 				if (iterator->GetCurrentNode() == node)
161 					iterator->NodeRemoved(node);
162 			}
163 			// remove and re-insert the node
164 			it.Remove();
165 			error = fNodes->Insert(node);
166 
167 			// udpate live queries
168 			time_t newModified = node->GetMTime();
169 			fVolume->UpdateLiveQueries(NULL, node, GetName(), GetType(),
170 				(const uint8*)&oldModified, sizeof(oldModified),
171 				(const uint8*)&newModified, sizeof(newModified));
172 		}
173 	}
174 	return error;
175 }
176 
177 // NodeAdded
178 void
179 LastModifiedIndex::NodeAdded(Node *node)
180 {
181 	if (node)
182 		fNodes->Insert(node);
183 }
184 
185 // NodeRemoved
186 void
187 LastModifiedIndex::NodeRemoved(Node *node)
188 {
189 	if (node)
190 		fNodes->Remove(node, node);
191 }
192 
193 // InternalGetIterator
194 AbstractIndexEntryIterator *
195 LastModifiedIndex::InternalGetIterator()
196 {
197 	Iterator *iterator = new(nothrow) Iterator;
198 	if (iterator) {
199 		if (!iterator->SetTo(this, 0, true)) {
200 			delete iterator;
201 			iterator = NULL;
202 		}
203 	}
204 	return iterator;
205 }
206 
207 // InternalFind
208 AbstractIndexEntryIterator *
209 LastModifiedIndex::InternalFind(const uint8 *key, size_t length)
210 {
211 	if (!key || length != sizeof(time_t))
212 		return NULL;
213 	Iterator *iterator = new(nothrow) Iterator;
214 	if (iterator) {
215 		if (!iterator->SetTo(this, *(const time_t*)key)) {
216 			delete iterator;
217 			iterator = NULL;
218 		}
219 	}
220 	return iterator;
221 }
222 
223 // _AddIterator
224 void
225 LastModifiedIndex::_AddIterator(Iterator *iterator)
226 {
227 	fIterators->Insert(iterator);
228 }
229 
230 // _RemoveIterator
231 void
232 LastModifiedIndex::_RemoveIterator(Iterator *iterator)
233 {
234 	fIterators->Remove(iterator);
235 }
236 
237 
238 // Iterator
239 
240 // constructor
241 LastModifiedIndex::Iterator::Iterator()
242 	: BaseClass(),
243 	  fIndex(NULL)
244 {
245 }
246 
247 // destructor
248 LastModifiedIndex::Iterator::~Iterator()
249 {
250 	SetTo(NULL, 0);
251 }
252 
253 // GetCurrent
254 Entry *
255 LastModifiedIndex::Iterator::GetCurrent()
256 {
257 	return BaseClass::GetCurrent();
258 }
259 
260 // GetCurrent
261 Entry *
262 LastModifiedIndex::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength)
263 {
264 	Entry *entry = GetCurrent();
265 	if (entry) {
266 		*(time_t*)buffer = entry->GetNode()->GetMTime();
267 		*keyLength = sizeof(time_t);
268 	}
269 	return entry;
270 }
271 
272 // Suspend
273 status_t
274 LastModifiedIndex::Iterator::Suspend()
275 {
276 	status_t error = BaseClass::Suspend();
277 	if (error == B_OK) {
278 		if (fNode) {
279 			error = fIndex->GetVolume()->AddNodeListener(this, fNode,
280 														 NODE_LISTEN_REMOVED);
281 			if (error == B_OK && fEntry) {
282 				error = fIndex->GetVolume()->AddEntryListener(this, fEntry,
283 					ENTRY_LISTEN_REMOVED);
284 				if (error != B_OK)
285 					fIndex->GetVolume()->RemoveNodeListener(this, fNode);
286 			}
287 			if (error != B_OK)
288 				BaseClass::Resume();
289 		}
290 	}
291 	return error;
292 }
293 
294 // Resume
295 status_t
296 LastModifiedIndex::Iterator::Resume()
297 {
298 	status_t error = BaseClass::Resume();
299 	if (error == B_OK) {
300 		if (fEntry)
301 			error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry);
302 		if (fNode) {
303 			if (error == B_OK)
304 				error = fIndex->GetVolume()->RemoveNodeListener(this, fNode);
305 			else
306 				fIndex->GetVolume()->RemoveNodeListener(this, fNode);
307 		}
308 	}
309 	return error;
310 }
311 
312 // SetTo
313 bool
314 LastModifiedIndex::Iterator::SetTo(LastModifiedIndex *index, time_t modified,
315 								   bool ignoreValue)
316 {
317 	Resume();
318 	Unset();
319 	// set the new values
320 	fIndex = index;
321 	if (fIndex)
322 		fIndex->_AddIterator(this);
323 	fInitialized = fIndex;
324 	// get the node's first entry
325 	if (fIndex) {
326 		// get the first node
327 		bool found = true;
328 		if (ignoreValue)
329 			fIndex->fNodes->GetIterator(&fIterator);
330 		else
331 			found = fIndex->fNodes->FindFirst(modified, &fIterator);
332 		// get the first entry
333 		if (found) {
334 			if (Node **nodeP = fIterator.GetCurrent()) {
335 				fNode = *nodeP;
336 				fEntry = fNode->GetFirstReferrer();
337 				if (!fEntry)
338 					BaseClass::GetNext();
339 				if (!ignoreValue && fNode && fNode->GetMTime() != modified)
340 					Unset();
341 			}
342 		}
343 	}
344 	return fEntry;
345 }
346 
347 // Unset
348 void
349 LastModifiedIndex::Iterator::Unset()
350 {
351 	if (fIndex) {
352 		fIndex->_RemoveIterator(this);
353 		fIndex = NULL;
354 	}
355 	BaseClass::Unset();
356 }
357 
358 // EntryRemoved
359 void
360 LastModifiedIndex::Iterator::EntryRemoved(Entry */*entry*/)
361 {
362 	Resume();
363 	fIsNext = BaseClass::GetNext();
364 	Suspend();
365 }
366 
367 // NodeRemoved
368 void
369 LastModifiedIndex::Iterator::NodeRemoved(Node */*node*/)
370 {
371 	Resume();
372 	fEntry = NULL;
373 	fIsNext = BaseClass::GetNext();
374 	Suspend();
375 }
376 
377