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 17FileMonitor::FileMonitor(EntryViewInterface* listener) 18 : 19 fListener(listener), 20 fCurrentReadList(NULL), 21 fCurrentReadIndex(0) 22{ 23 24} 25 26 27FileMonitor::~FileMonitor() 28{ 29 Reset(); 30} 31 32 33void 34FileMonitor::SetReadThread(ReadThread* readThread) 35{ 36 fReadThread = readThread; 37} 38 39 40void 41FileMonitor::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 54void 55FileMonitor::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 96void 97FileMonitor::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 113void 114FileMonitor::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 126void 127FileMonitor::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 140void 141FileMonitor::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 150void 151FileMonitor::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 160WatchedFile* 161FileMonitor::_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 174int32 175ReadThreadFunction(void *data) 176{ 177 ReadThread* that = (ReadThread*)data; 178 return that->Process(); 179} 180 181 182ReadThread::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 196status_t 197ReadThread::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 213bool 214ReadThread::Running() 215{ 216 return fRunning; 217} 218 219 220status_t 221ReadThread::Wait() 222{ 223 status_t exitValue; 224 return wait_for_thread(fThreadId, &exitValue); 225} 226 227 228void 229ReadThread::Stop() 230{ 231 fStopped = true; 232} 233 234 235bool 236ReadThread::Stopped() 237{ 238 return fStopped; 239} 240 241 242RefList* 243ReadThread::ReadRefList() 244{ 245 return fReadRefList; 246} 247 248 249void 250ReadThread::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 263int32 264ReadThread::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 291void 292ReadThread::_SwapLists() 293{ 294 RefList* lastReadList = fReadRefList; 295 fReadRefList = fWriteRefList; 296 fWriteRefList = lastReadList; 297} 298 299 300void 301ReadThread::_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