1 /* 2 * Copyright 2011, Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Clemens Zeidler <haiku@clemens-zeidler.de> 7 */ 8 9 #include "FileMonitor.h" 10 11 #include <Looper.h> 12 13 #include <Messenger.h> 14 #include <NodeMonitor.h> 15 16 17 FileMonitor::FileMonitor(EntryViewInterface* listener) 18 : 19 fListener(listener), 20 fCurrentReadList(NULL), 21 fCurrentReadIndex(0) 22 { 23 24 } 25 26 27 FileMonitor::~FileMonitor() 28 { 29 Reset(); 30 } 31 32 33 void 34 FileMonitor::SetReadThread(ReadThread* readThread) 35 { 36 fReadThread = readThread; 37 } 38 39 40 void 41 FileMonitor::Reset() 42 { 43 fWatchedFileList.clear(); 44 stop_watching(this); 45 46 BMessenger messenger(this); 47 messenger.SendMessage(kMsgCleared); 48 49 if (fCurrentReadList != NULL) 50 fCurrentReadIndex = fCurrentReadList->size(); 51 } 52 53 54 void 55 FileMonitor::MessageReceived(BMessage* msg) 56 { 57 switch (msg->what) { 58 case kMsgAddRefs: 59 { 60 if (fCurrentReadList == NULL) 61 fCurrentReadList = fReadThread->ReadRefList(); 62 uint32 terminate = fCurrentReadIndex + 50; 63 for (; fCurrentReadIndex < terminate; fCurrentReadIndex++) { 64 if (fCurrentReadIndex >= fCurrentReadList->size()) { 65 fCurrentReadList = NULL; 66 fCurrentReadIndex = 0; 67 fReadThread->ReadDone(); 68 break; 69 } 70 71 entry_ref& entry = (*fCurrentReadList)[fCurrentReadIndex]; 72 node_ref nodeRef; 73 BNode node(&entry); 74 if (node.GetNodeRef(&nodeRef) != B_OK) 75 continue; 76 77 EntryCreated(entry.name, entry.directory, entry.device, 78 nodeRef.node); 79 } 80 if (fCurrentReadList) 81 Looper()->PostMessage(kMsgAddRefs, this); 82 83 break; 84 } 85 86 case kMsgCleared: 87 fListener->EntriesCleared(); 88 break; 89 90 default: 91 NodeMonitorHandler::MessageReceived(msg); 92 } 93 } 94 95 96 void 97 FileMonitor::EntryCreated(const char *name, ino_t directory, dev_t device, 98 ino_t node) 99 { 100 WatchedFile file; 101 NodeMonitorHandler::make_node_ref(device, node, &file.node); 102 if (fWatchedFileList.find(file.node) != fWatchedFileList.end()) 103 return; 104 105 NodeMonitorHandler::make_entry_ref(device, directory, name, &file.entry); 106 fWatchedFileList[file.node] = file; 107 108 watch_node(&file.node, B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, this); 109 fListener->EntryCreated(&fWatchedFileList[file.node]); 110 } 111 112 113 void 114 FileMonitor::EntryRemoved(const char *name, ino_t directory, dev_t device, 115 ino_t node) 116 { 117 WatchedFile* file = _FindFile(device, node); 118 if (file == NULL) 119 return; 120 121 fListener->EntryRemoved(file); 122 fWatchedFileList.erase(file->node); 123 } 124 125 126 void 127 FileMonitor::EntryMoved(const char *name, const char *fromName, 128 ino_t fromDirectory, ino_t toDirectory, dev_t device, ino_t node, 129 dev_t nodeDevice) 130 { 131 WatchedFile* file = _FindFile(device, node); 132 if (file == NULL) 133 return; 134 NodeMonitorHandler::make_entry_ref(device, toDirectory, name, &file->entry); 135 NodeMonitorHandler::make_node_ref(device, node, &file->node); 136 fListener->EntryMoved(file); 137 } 138 139 140 void 141 FileMonitor::StatChanged(ino_t node, dev_t device, int32 statFields) 142 { 143 WatchedFile* file = _FindFile(device, node); 144 if (file == NULL) 145 return; 146 fListener->StatChanged(file); 147 } 148 149 150 void 151 FileMonitor::AttrChanged(ino_t node, dev_t device) 152 { 153 WatchedFile* file = _FindFile(device, node); 154 if (file == NULL) 155 return; 156 fListener->AttrChanged(file); 157 } 158 159 160 WatchedFile* 161 FileMonitor::_FindFile(dev_t device, ino_t node) 162 { 163 node_ref nodeRef; 164 NodeMonitorHandler::make_node_ref(device, node, &nodeRef); 165 166 WatchedFileList::iterator it = fWatchedFileList.find(nodeRef); 167 if (it == fWatchedFileList.end()) 168 return NULL; 169 170 return &it->second; 171 } 172 173 174 int32 175 ReadThreadFunction(void *data) 176 { 177 ReadThread* that = (ReadThread*)data; 178 return that->Process(); 179 } 180 181 182 ReadThread::ReadThread(FileMonitor* target) 183 : 184 fTarget(target), 185 fReading(false), 186 fStopped(false), 187 fThreadId(-1), 188 fNReaded(0), 189 fRunning(false) 190 { 191 fWriteRefList = &fRefList1; 192 fReadRefList = &fRefList2; 193 } 194 195 196 status_t 197 ReadThread::Run() 198 { 199 if (fThreadId >= 0) 200 return B_ERROR; 201 202 fStopped = false; 203 fThreadId = spawn_thread(ReadThreadFunction, "file reader", B_LOW_PRIORITY, 204 this); 205 fRunning = true; 206 status_t status = resume_thread(fThreadId); 207 if (status != B_OK) 208 fRunning = false; 209 return status; 210 } 211 212 213 bool 214 ReadThread::Running() 215 { 216 return fRunning; 217 } 218 219 220 status_t 221 ReadThread::Wait() 222 { 223 status_t exitValue; 224 return wait_for_thread(fThreadId, &exitValue); 225 } 226 227 228 void 229 ReadThread::Stop() 230 { 231 fStopped = true; 232 } 233 234 235 bool 236 ReadThread::Stopped() 237 { 238 return fStopped; 239 } 240 241 242 RefList* 243 ReadThread::ReadRefList() 244 { 245 return fReadRefList; 246 } 247 248 249 void 250 ReadThread::ReadDone() 251 { 252 fReadRefList->clear(); 253 // and release the list 254 fReading = false; 255 256 if (!fRunning && fWriteRefList->size() != 0) { 257 BMessenger messenger(fTarget); 258 _PublishEntrys(messenger); 259 } 260 } 261 262 263 int32 264 ReadThread::Process() 265 { 266 BMessenger messenger(fTarget); 267 268 entry_ref entry; 269 while (ReadNextEntry(entry)) { 270 if (Stopped()) { 271 fWriteRefList->clear(); 272 break; 273 } 274 275 fWriteRefList->push_back(entry); 276 277 fNReaded++; 278 if (fNReaded >= 50) 279 _PublishEntrys(messenger); 280 } 281 282 fRunning = false; 283 284 _PublishEntrys(messenger); 285 286 fThreadId = -1; 287 return B_OK; 288 } 289 290 291 void 292 ReadThread::_SwapLists() 293 { 294 RefList* lastReadList = fReadRefList; 295 fReadRefList = fWriteRefList; 296 fWriteRefList = lastReadList; 297 } 298 299 300 void 301 ReadThread::_PublishEntrys(BMessenger& messenger) 302 { 303 if (fReading || Stopped()) 304 return; 305 _SwapLists(); 306 fReading = true; 307 fNReaded = 0; 308 messenger.SendMessage(kMsgAddRefs); 309 } 310 311