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 break; 93 } 94} 95 96 97void 98FileMonitor::EntryCreated(const char *name, ino_t directory, dev_t device, 99 ino_t node) 100{ 101 WatchedFile file; 102 NodeMonitorHandler::make_node_ref(device, node, &file.node); 103 if (fWatchedFileList.find(file.node) != fWatchedFileList.end()) 104 return; 105 106 NodeMonitorHandler::make_entry_ref(device, directory, name, &file.entry); 107 fWatchedFileList[file.node] = file; 108 109 watch_node(&file.node, B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, this); 110 fListener->EntryCreated(&fWatchedFileList[file.node]); 111} 112 113 114void 115FileMonitor::EntryRemoved(const char *name, ino_t directory, dev_t device, 116 ino_t node) 117{ 118 WatchedFile* file = _FindFile(device, node); 119 if (file == NULL) 120 return; 121 122 fListener->EntryRemoved(file); 123 fWatchedFileList.erase(file->node); 124} 125 126 127void 128FileMonitor::EntryMoved(const char *name, const char *fromName, 129 ino_t fromDirectory, ino_t toDirectory, dev_t device, ino_t node, 130 dev_t nodeDevice) 131{ 132 WatchedFile* file = _FindFile(device, node); 133 if (file == NULL) 134 return; 135 NodeMonitorHandler::make_entry_ref(device, toDirectory, name, &file->entry); 136 NodeMonitorHandler::make_node_ref(device, node, &file->node); 137 fListener->EntryMoved(file); 138} 139 140 141void 142FileMonitor::StatChanged(ino_t node, dev_t device, int32 statFields) 143{ 144 WatchedFile* file = _FindFile(device, node); 145 if (file == NULL) 146 return; 147 fListener->StatChanged(file); 148} 149 150 151void 152FileMonitor::AttrChanged(ino_t node, dev_t device) 153{ 154 WatchedFile* file = _FindFile(device, node); 155 if (file == NULL) 156 return; 157 fListener->AttrChanged(file); 158} 159 160 161WatchedFile* 162FileMonitor::_FindFile(dev_t device, ino_t node) 163{ 164 node_ref nodeRef; 165 NodeMonitorHandler::make_node_ref(device, node, &nodeRef); 166 167 WatchedFileList::iterator it = fWatchedFileList.find(nodeRef); 168 if (it == fWatchedFileList.end()) 169 return NULL; 170 171 return &it->second; 172} 173 174 175int32 176ReadThreadFunction(void *data) 177{ 178 ReadThread* that = (ReadThread*)data; 179 return that->Process(); 180} 181 182 183ReadThread::ReadThread(FileMonitor* target) 184 : 185 fTarget(target), 186 fReading(false), 187 fStopped(false), 188 fThreadId(-1), 189 fNReaded(0), 190 fRunning(false) 191{ 192 fWriteRefList = &fRefList1; 193 fReadRefList = &fRefList2; 194} 195 196 197status_t 198ReadThread::Run() 199{ 200 if (fThreadId >= 0) 201 return B_ERROR; 202 203 fStopped = false; 204 fThreadId = spawn_thread(ReadThreadFunction, "file reader", B_LOW_PRIORITY, 205 this); 206 fRunning = true; 207 status_t status = resume_thread(fThreadId); 208 if (status != B_OK) 209 fRunning = false; 210 return status; 211} 212 213 214bool 215ReadThread::Running() 216{ 217 return fRunning; 218} 219 220 221status_t 222ReadThread::Wait() 223{ 224 status_t exitValue; 225 return wait_for_thread(fThreadId, &exitValue); 226} 227 228 229void 230ReadThread::Stop() 231{ 232 fStopped = true; 233} 234 235 236bool 237ReadThread::Stopped() 238{ 239 return fStopped; 240} 241 242 243RefList* 244ReadThread::ReadRefList() 245{ 246 return fReadRefList; 247} 248 249 250void 251ReadThread::ReadDone() 252{ 253 fReadRefList->clear(); 254 // and release the list 255 fReading = false; 256 257 if (!fRunning && fWriteRefList->size() != 0) { 258 BMessenger messenger(fTarget); 259 _PublishEntrys(messenger); 260 } 261} 262 263 264int32 265ReadThread::Process() 266{ 267 BMessenger messenger(fTarget); 268 269 entry_ref entry; 270 while (ReadNextEntry(entry)) { 271 if (Stopped()) { 272 fWriteRefList->clear(); 273 break; 274 } 275 276 fWriteRefList->push_back(entry); 277 278 fNReaded++; 279 if (fNReaded >= 50) 280 _PublishEntrys(messenger); 281 } 282 283 fRunning = false; 284 285 _PublishEntrys(messenger); 286 287 fThreadId = -1; 288 return B_OK; 289} 290 291 292void 293ReadThread::_SwapLists() 294{ 295 RefList* lastReadList = fReadRefList; 296 fReadRefList = fWriteRefList; 297 fWriteRefList = lastReadList; 298} 299 300 301void 302ReadThread::_PublishEntrys(BMessenger& messenger) 303{ 304 if (fReading || Stopped()) 305 return; 306 _SwapLists(); 307 fReading = true; 308 fNReaded = 0; 309 messenger.SendMessage(kMsgAddRefs); 310} 311