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 break; 93 } 94 } 95 96 97 void 98 FileMonitor::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 114 void 115 FileMonitor::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 127 void 128 FileMonitor::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 141 void 142 FileMonitor::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 151 void 152 FileMonitor::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 161 WatchedFile* 162 FileMonitor::_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 175 int32 176 ReadThreadFunction(void *data) 177 { 178 ReadThread* that = (ReadThread*)data; 179 return that->Process(); 180 } 181 182 183 ReadThread::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 197 status_t 198 ReadThread::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 214 bool 215 ReadThread::Running() 216 { 217 return fRunning; 218 } 219 220 221 status_t 222 ReadThread::Wait() 223 { 224 status_t exitValue; 225 return wait_for_thread(fThreadId, &exitValue); 226 } 227 228 229 void 230 ReadThread::Stop() 231 { 232 fStopped = true; 233 } 234 235 236 bool 237 ReadThread::Stopped() 238 { 239 return fStopped; 240 } 241 242 243 RefList* 244 ReadThread::ReadRefList() 245 { 246 return fReadRefList; 247 } 248 249 250 void 251 ReadThread::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 264 int32 265 ReadThread::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 292 void 293 ReadThread::_SwapLists() 294 { 295 RefList* lastReadList = fReadRefList; 296 fReadRefList = fWriteRefList; 297 fWriteRefList = lastReadList; 298 } 299 300 301 void 302 ReadThread::_PublishEntrys(BMessenger& messenger) 303 { 304 if (fReading || Stopped()) 305 return; 306 _SwapLists(); 307 fReading = true; 308 fNReaded = 0; 309 messenger.SendMessage(kMsgAddRefs); 310 } 311