xref: /haiku/src/apps/mail/QueryList.cpp (revision f1fafe317f3b018f7d4d5e209aef1dbb35f77ad7)
1 /*
2  * Copyright 2015, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "QueryList.h"
8 
9 #include <pthread.h>
10 
11 #include <Autolock.h>
12 #include <Debug.h>
13 #include <NodeMonitor.h>
14 #include <VolumeRoster.h>
15 
16 
17 static BLooper* sQueryLooper = NULL;
18 static pthread_once_t sInitOnce = PTHREAD_ONCE_INIT;
19 
20 
21 static void
initQueryLooper()22 initQueryLooper()
23 {
24 	sQueryLooper = new BLooper("query looper");
25 	sQueryLooper->Run();
26 }
27 
28 
29 // #pragma mark - QueryListener
30 
31 
~QueryListener()32 QueryListener::~QueryListener()
33 {
34 }
35 
36 
37 // #pragma mark - QueryList
38 
39 
QueryList()40 QueryList::QueryList()
41 	:
42 	fQuit(false),
43 	fListeners(5, true)
44 {
45 }
46 
47 
~QueryList()48 QueryList::~QueryList()
49 {
50 	fQuit = true;
51 
52 	ThreadVector::const_iterator threadIterator = fFetchThreads.begin();
53 	for (; threadIterator != fFetchThreads.end(); threadIterator++) {
54 		wait_for_thread(*threadIterator, NULL);
55 	}
56 
57 	QueryVector::iterator queryIterator = fQueries.begin();
58 	for (; queryIterator != fQueries.end(); queryIterator++) {
59 		delete *queryIterator;
60 	}
61 }
62 
63 
64 status_t
Init(const char * predicate,BVolume * specificVolume)65 QueryList::Init(const char* predicate, BVolume* specificVolume)
66 {
67 	if (sQueryLooper == NULL)
68 		pthread_once(&sInitOnce, &initQueryLooper);
69 
70 	if (Looper() != NULL)
71 		debugger("Init() called twice!");
72 
73 	sQueryLooper->Lock();
74 	sQueryLooper->AddHandler(this);
75 	sQueryLooper->Unlock();
76 
77 	if (specificVolume == NULL) {
78 		BVolumeRoster roster;
79 		BVolume volume;
80 
81 		while (roster.GetNextVolume(&volume) == B_OK) {
82 			if (volume.KnowsQuery() && volume.KnowsAttr() && volume.KnowsMime())
83 				_AddVolume(volume, predicate);
84 		}
85 	} else
86 		_AddVolume(*specificVolume, predicate);
87 
88 	return B_OK;
89 }
90 
91 
92 void
AddListener(QueryListener * listener)93 QueryList::AddListener(QueryListener* listener)
94 {
95 	BAutolock locker(this);
96 
97 	// Add all entries that were already retrieved
98 	RefMap::const_iterator iterator = fRefs.begin();
99 	for (; iterator != fRefs.end(); iterator++) {
100 		listener->EntryCreated(*this, iterator->second, iterator->first.node);
101 	}
102 
103 	fListeners.AddItem(listener);
104 }
105 
106 
107 void
RemoveListener(QueryListener * listener)108 QueryList::RemoveListener(QueryListener* listener)
109 {
110 	BAutolock locker(this);
111 	fListeners.RemoveItem(listener, false);
112 }
113 
114 
115 void
MessageReceived(BMessage * message)116 QueryList::MessageReceived(BMessage* message)
117 {
118 	switch (message->what) {
119 		case B_QUERY_UPDATE:
120 		{
121 			int32 opcode = message->GetInt32("opcode", -1);
122 			int64 directory = message->GetInt64("directory", -1);
123 			int32 device = message->GetInt32("device", -1);
124 			int64 node = message->GetInt64("node", -1);
125 
126 			if (opcode == B_ENTRY_CREATED) {
127 				const char* name = message->GetString("name");
128 				if (name != NULL) {
129 					entry_ref ref(device, directory, name);
130 					_AddEntry(ref, node);
131 				}
132 			} else if (opcode == B_ENTRY_REMOVED) {
133 				node_ref nodeRef(device, node);
134 				_RemoveEntry(nodeRef);
135 			}
136 			break;
137 		}
138 
139 		default:
140 			BHandler::MessageReceived(message);
141 			break;
142 	}
143 }
144 
145 
146 void
_AddEntry(const entry_ref & ref,ino_t node)147 QueryList::_AddEntry(const entry_ref& ref, ino_t node)
148 {
149 	BAutolock locker(this);
150 
151 	// TODO: catch bad_alloc
152 	fRefs.insert(std::make_pair(node_ref(ref.device, node), ref));
153 
154 	_NotifyEntryCreated(ref, node);
155 }
156 
157 
158 void
_RemoveEntry(const node_ref & nodeRef)159 QueryList::_RemoveEntry(const node_ref& nodeRef)
160 {
161 	BAutolock locker(this);
162 	RefMap::iterator found = fRefs.find(nodeRef);
163 	if (found != fRefs.end())
164 		_NotifyEntryRemoved(nodeRef);
165 }
166 
167 
168 void
_NotifyEntryCreated(const entry_ref & ref,ino_t node)169 QueryList::_NotifyEntryCreated(const entry_ref& ref, ino_t node)
170 {
171 	ASSERT(IsLocked());
172 
173 	int32 count = fListeners.CountItems();
174 	for (int32 index = 0; index < count; index++) {
175 		fListeners.ItemAt(index)->EntryCreated(*this, ref, node);
176 	}
177 }
178 
179 
180 void
_NotifyEntryRemoved(const node_ref & nodeRef)181 QueryList::_NotifyEntryRemoved(const node_ref& nodeRef)
182 {
183 	ASSERT(IsLocked());
184 
185 	int32 count = fListeners.CountItems();
186 	for (int32 index = 0; index < count; index++) {
187 		fListeners.ItemAt(index)->EntryRemoved(*this, nodeRef);
188 	}
189 }
190 
191 
192 void
_AddVolume(BVolume & volume,const char * predicate)193 QueryList::_AddVolume(BVolume& volume, const char* predicate)
194 {
195 	BQuery* query = new BQuery();
196 	if (query->SetVolume(&volume) != B_OK
197 		|| query->SetPredicate(predicate) != B_OK
198 		|| query->SetTarget(this) != B_OK) {
199 		delete query;
200 	}
201 
202 	// TODO: catch bad_alloc
203 	fQueries.push_back(query);
204 	Lock();
205 	fQueryQueue.push_back(query);
206 	Unlock();
207 
208 	thread_id thread = spawn_thread(_FetchQuery, "query fetcher",
209 		B_NORMAL_PRIORITY, this);
210 	if (thread >= B_OK) {
211 		resume_thread(thread);
212 
213 		fFetchThreads.push_back(thread);
214 	}
215 }
216 
217 
218 /*static*/ status_t
_FetchQuery(void * self)219 QueryList::_FetchQuery(void* self)
220 {
221 	return static_cast<QueryList*>(self)->_FetchQuery();
222 }
223 
224 
225 status_t
_FetchQuery()226 QueryList::_FetchQuery()
227 {
228 	RefMap map;
229 
230 	BAutolock locker(this);
231 	BQuery* query = fQueryQueue.back();
232 	fQueryQueue.pop_back();
233 	locker.Unlock();
234 
235 	query->Fetch();
236 
237 	entry_ref ref;
238 	while (!fQuit && query->GetNextRef(&ref) == B_OK) {
239 		BEntry entry(&ref);
240 		node_ref nodeRef;
241 		if (entry.GetNodeRef(&nodeRef) == B_OK)
242 			map.insert(std::make_pair(nodeRef, ref));
243 	}
244 	if (fQuit)
245 		return B_INTERRUPTED;
246 
247 	locker.Lock();
248 
249 	RefMap::const_iterator iterator = map.begin();
250 	for (; iterator != map.end(); iterator++) {
251 		_AddEntry(iterator->second, iterator->first.node);
252 	}
253 
254 	return B_OK;
255 }
256