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