1// QueryManager.cpp
2
3#include <new>
4
5#include <fsproto.h>
6
7#include <AutoLocker.h>
8#include <HashMap.h>
9
10#include "DebugSupport.h"
11#include "Locker.h"
12#include "QueryManager.h"
13#include "Volume.h"
14#include "VolumeManager.h"
15
16typedef DoublyLinkedList<QueryIterator, QueryIterator::GetVolumeLink>
17	IteratorList;
18
19// IteratorMap
20struct QueryManager::IteratorMap : HashMap<HashKey64<vnode_id>, IteratorList*> {
21};
22
23
24// constructor
25QueryManager::QueryManager(VolumeManager* volumeManager)
26	: fLock("query manager"),
27	  fVolumeManager(volumeManager),
28	  fIterators(NULL)
29{
30}
31
32// destructor
33QueryManager::~QueryManager()
34{
35	// delete all iterator lists (there shouldn't be any, though)
36	for (IteratorMap::Iterator it = fIterators->GetIterator(); it.HasNext();) {
37		IteratorList* iteratorList = it.Next().value;
38		delete iteratorList;
39	}
40	delete fIterators;
41}
42
43// Init
44status_t
45QueryManager::Init()
46{
47	// check lock
48	if (fLock.Sem() < 0)
49		return fLock.Sem();
50
51	// allocate iterator map
52	fIterators = new(std::nothrow) IteratorMap;
53	if (!fIterators)
54		return B_NO_MEMORY;
55	status_t error = fIterators->InitCheck();
56	if (error != B_OK)
57		return error;
58
59	return B_OK;
60}
61
62// AddIterator
63status_t
64QueryManager::AddIterator(QueryIterator* iterator)
65{
66	if (!iterator || !iterator->GetVolume())
67		return B_BAD_VALUE;
68
69	AutoLocker<Locker> _(fLock);
70
71	// get the iterator list for the volume
72	vnode_id nodeID = iterator->GetVolume()->GetRootID();
73	IteratorList* iteratorList = fIterators->Get(nodeID);
74	if (!iteratorList) {
75		// no list yet: create one
76		iteratorList = new(std::nothrow) IteratorList;
77		if (!iteratorList)
78			return B_NO_MEMORY;
79
80		// add it to the map
81		status_t error = fIterators->Put(nodeID, iteratorList);
82		if (error != B_OK) {
83			delete iteratorList;
84			return error;
85		}
86	}
87
88	// add the iterator
89	iteratorList->Insert(iterator);
90
91	// get a volume reference for the iterator
92	iterator->GetVolume()->AcquireReference();
93
94	return B_OK;
95}
96
97// AddSubIterator
98status_t
99QueryManager::AddSubIterator(HierarchicalQueryIterator* iterator,
100	QueryIterator* subIterator)
101{
102	if (!iterator || !subIterator)
103		return B_BAD_VALUE;
104
105	AutoLocker<Locker> _(fLock);
106	if (subIterator->GetVolume()->IsUnmounting())
107		return B_BAD_VALUE;
108
109	iterator->AddSubIterator(subIterator);
110
111	return B_OK;
112}
113
114// RemoveSubIterator
115status_t
116QueryManager::RemoveSubIterator(HierarchicalQueryIterator* iterator,
117	QueryIterator* subIterator)
118{
119	if (!iterator || !subIterator)
120		return B_BAD_VALUE;
121
122	AutoLocker<Locker> _(fLock);
123	if (subIterator->GetParentIterator() != iterator)
124		return B_BAD_VALUE;
125
126	iterator->RemoveSubIterator(subIterator);
127
128	return B_OK;
129}
130
131// GetCurrentSubIterator
132QueryIterator*
133QueryManager::GetCurrentSubIterator(HierarchicalQueryIterator* iterator)
134{
135	if (!iterator)
136		return NULL;
137
138	AutoLocker<Locker> _(fLock);
139	QueryIterator* subIterator = iterator->GetCurrentSubIterator();
140	if (subIterator)
141		subIterator->AcquireReference();
142	return subIterator;
143}
144
145// NextSubIterator
146void
147QueryManager::NextSubIterator(HierarchicalQueryIterator* iterator,
148	QueryIterator* subIterator)
149{
150	if (iterator) {
151		AutoLocker<Locker> _(fLock);
152		if (iterator->GetCurrentSubIterator() == subIterator)
153			iterator->NextSubIterator();
154	}
155}
156
157// RewindSubIterator
158void
159QueryManager::RewindSubIterator(HierarchicalQueryIterator* iterator)
160{
161	if (iterator) {
162		AutoLocker<Locker> _(fLock);
163		iterator->RewindSubIterator();
164	}
165}
166
167// PutIterator
168void
169QueryManager::PutIterator(QueryIterator* iterator)
170{
171	if (!iterator)
172		return;
173
174	AutoLocker<Locker> locker(fLock);
175	if (iterator->ReleaseReference()) {
176		// last reference removed: remove the iterator
177
178		// remove its subiterators (if any)
179		DoublyLinkedList<QueryIterator> subIterators;
180		if (HierarchicalQueryIterator* hIterator
181				= dynamic_cast<HierarchicalQueryIterator*>(iterator)) {
182			hIterator->RemoveAllSubIterators(subIterators);
183		}
184
185		// remove from the parent iterator
186		HierarchicalQueryIterator* parentIterator
187			= iterator->GetParentIterator();
188		if (parentIterator)
189			parentIterator->RemoveSubIterator(iterator);
190
191		// remove from the list
192		vnode_id nodeID = iterator->GetVolume()->GetRootID();
193		IteratorList* iteratorList = fIterators->Get(nodeID);
194		if (iteratorList) {
195			iteratorList->Remove(iterator);
196
197			// if the list is empty, remove it completely
198			if (!iteratorList->First()) {
199				fIterators->Remove(nodeID);
200				delete iteratorList;
201			}
202		} else {
203			ERROR("QueryManager::PutIterator(): ERROR: No iterator list "
204				"for volume %p!\n", iterator->GetVolume());
205		}
206
207		// free the iterator and surrender its volume reference
208		Volume* volume = iterator->GetVolume();
209		locker.Unlock();
210		volume->FreeQueryIterator(iterator);
211		volume->PutVolume();
212
213		// put the subiterators
214		while (QueryIterator* subIterator = subIterators.First()) {
215			subIterators.Remove(subIterator);
216			PutIterator(subIterator);
217		}
218	}
219}
220
221// VolumeUnmounting
222//
223// Removes all subiterators belonging to the volume from their parent iterators
224// and puts the respective reference.
225void
226QueryManager::VolumeUnmounting(Volume* volume)
227{
228	if (!volume || !volume->IsUnmounting())
229		return;
230
231	vnode_id nodeID = volume->GetRootID();
232	IteratorList iterators;
233	DoublyLinkedList<QueryIterator> subIterators;
234	AutoLocker<Locker> locker(fLock);
235	if (IteratorList* iteratorList = fIterators->Get(nodeID)) {
236		// Unset the parent of all iterators and remove one reference.
237		// If the iterators are unreference, remove them.
238		QueryIterator* iterator = iteratorList->First();
239		while (iterator) {
240			QueryIterator* nextIterator = iteratorList->GetNext(iterator);
241
242			if (iterator->GetParentIterator()) {
243				// remove its subiterators (if any)
244				if (HierarchicalQueryIterator* hIterator
245						= dynamic_cast<HierarchicalQueryIterator*>(iterator)) {
246					hIterator->RemoveAllSubIterators(subIterators);
247				}
248
249				// remove from parent
250				iterator->GetParentIterator()->RemoveSubIterator(iterator);
251
252				// remove reference
253				if (iterator->ReleaseReference()) {
254					// no more reference: move to our local list
255					iteratorList->Remove(iterator);
256					iterators.Insert(iterator);
257				}
258			}
259
260			iterator = nextIterator;
261		}
262
263		// if the list is empty now, remove it completely
264		if (!iteratorList->First()) {
265			fIterators->Remove(nodeID);
266			delete iteratorList;
267		}
268
269		// free the iterators we have removed and surrender their volume
270		// references
271		locker.Unlock();
272		while (QueryIterator* iterator = iterators.First()) {
273			iterators.Remove(iterator);
274			volume->FreeQueryIterator(iterator);
275			volume->PutVolume();
276		}
277
278		// put the subiterators
279		while (QueryIterator* subIterator = subIterators.First()) {
280			subIterators.Remove(subIterator);
281			PutIterator(subIterator);
282		}
283	}
284}
285
286