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 22 initQueryLooper() 23 { 24 sQueryLooper = new BLooper("query looper"); 25 sQueryLooper->Run(); 26 } 27 28 29 // #pragma mark - QueryListener 30 31 32 QueryListener::~QueryListener() 33 { 34 } 35 36 37 // #pragma mark - QueryList 38 39 40 QueryList::QueryList() 41 : 42 fQuit(false), 43 fListeners(5, true) 44 { 45 } 46 47 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 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 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 108 QueryList::RemoveListener(QueryListener* listener) 109 { 110 BAutolock locker(this); 111 fListeners.RemoveItem(listener, false); 112 } 113 114 115 void 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 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 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 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 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 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 219 QueryList::_FetchQuery(void* self) 220 { 221 return static_cast<QueryList*>(self)->_FetchQuery(); 222 } 223 224 225 status_t 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