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