/* * Copyright 2011, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Clemens Zeidler */ #include "FileMonitor.h" #include #include #include FileMonitor::FileMonitor(EntryViewInterface* listener) : fListener(listener), fCurrentReadList(NULL), fCurrentReadIndex(0) { } FileMonitor::~FileMonitor() { Reset(); } void FileMonitor::SetReadThread(ReadThread* readThread) { fReadThread = readThread; } void FileMonitor::Reset() { fWatchedFileList.clear(); stop_watching(this); BMessenger messenger(this); messenger.SendMessage(kMsgCleared); if (fCurrentReadList != NULL) fCurrentReadIndex = fCurrentReadList->size(); } void FileMonitor::MessageReceived(BMessage* msg) { switch (msg->what) { case kMsgAddRefs: { if (fCurrentReadList == NULL) fCurrentReadList = fReadThread->ReadRefList(); uint32 terminate = fCurrentReadIndex + 50; for (; fCurrentReadIndex < terminate; fCurrentReadIndex++) { if (fCurrentReadIndex >= fCurrentReadList->size()) { fCurrentReadList = NULL; fCurrentReadIndex = 0; fReadThread->ReadDone(); break; } entry_ref& entry = (*fCurrentReadList)[fCurrentReadIndex]; node_ref nodeRef; BNode node(&entry); if (node.GetNodeRef(&nodeRef) != B_OK) continue; EntryCreated(entry.name, entry.directory, entry.device, nodeRef.node); } if (fCurrentReadList) Looper()->PostMessage(kMsgAddRefs, this); break; } case kMsgCleared: fListener->EntriesCleared(); break; default: NodeMonitorHandler::MessageReceived(msg); break; } } void FileMonitor::EntryCreated(const char *name, ino_t directory, dev_t device, ino_t node) { WatchedFile file; NodeMonitorHandler::make_node_ref(device, node, &file.node); if (fWatchedFileList.find(file.node) != fWatchedFileList.end()) return; NodeMonitorHandler::make_entry_ref(device, directory, name, &file.entry); fWatchedFileList[file.node] = file; watch_node(&file.node, B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, this); fListener->EntryCreated(&fWatchedFileList[file.node]); } void FileMonitor::EntryRemoved(const char *name, ino_t directory, dev_t device, ino_t node) { WatchedFile* file = _FindFile(device, node); if (file == NULL) return; fListener->EntryRemoved(file); fWatchedFileList.erase(file->node); } void FileMonitor::EntryMoved(const char *name, const char *fromName, ino_t fromDirectory, ino_t toDirectory, dev_t device, ino_t node, dev_t nodeDevice) { WatchedFile* file = _FindFile(device, node); if (file == NULL) return; NodeMonitorHandler::make_entry_ref(device, toDirectory, name, &file->entry); NodeMonitorHandler::make_node_ref(device, node, &file->node); fListener->EntryMoved(file); } void FileMonitor::StatChanged(ino_t node, dev_t device, int32 statFields) { WatchedFile* file = _FindFile(device, node); if (file == NULL) return; fListener->StatChanged(file); } void FileMonitor::AttrChanged(ino_t node, dev_t device) { WatchedFile* file = _FindFile(device, node); if (file == NULL) return; fListener->AttrChanged(file); } WatchedFile* FileMonitor::_FindFile(dev_t device, ino_t node) { node_ref nodeRef; NodeMonitorHandler::make_node_ref(device, node, &nodeRef); WatchedFileList::iterator it = fWatchedFileList.find(nodeRef); if (it == fWatchedFileList.end()) return NULL; return &it->second; } int32 ReadThreadFunction(void *data) { ReadThread* that = (ReadThread*)data; return that->Process(); } ReadThread::ReadThread(FileMonitor* target) : fTarget(target), fReading(false), fStopped(false), fThreadId(-1), fNReaded(0), fRunning(false) { fWriteRefList = &fRefList1; fReadRefList = &fRefList2; } status_t ReadThread::Run() { if (fThreadId >= 0) return B_ERROR; fStopped = false; fThreadId = spawn_thread(ReadThreadFunction, "file reader", B_LOW_PRIORITY, this); fRunning = true; status_t status = resume_thread(fThreadId); if (status != B_OK) fRunning = false; return status; } bool ReadThread::Running() { return fRunning; } status_t ReadThread::Wait() { status_t exitValue; return wait_for_thread(fThreadId, &exitValue); } void ReadThread::Stop() { fStopped = true; } bool ReadThread::Stopped() { return fStopped; } RefList* ReadThread::ReadRefList() { return fReadRefList; } void ReadThread::ReadDone() { fReadRefList->clear(); // and release the list fReading = false; if (!fRunning && fWriteRefList->size() != 0) { BMessenger messenger(fTarget); _PublishEntrys(messenger); } } int32 ReadThread::Process() { BMessenger messenger(fTarget); entry_ref entry; while (ReadNextEntry(entry)) { if (Stopped()) { fWriteRefList->clear(); break; } fWriteRefList->push_back(entry); fNReaded++; if (fNReaded >= 50) _PublishEntrys(messenger); } fRunning = false; _PublishEntrys(messenger); fThreadId = -1; return B_OK; } void ReadThread::_SwapLists() { RefList* lastReadList = fReadRefList; fReadRefList = fWriteRefList; fWriteRefList = lastReadList; } void ReadThread::_PublishEntrys(BMessenger& messenger) { if (fReading || Stopped()) return; _SwapLists(); fReading = true; fNReaded = 0; messenger.SendMessage(kMsgAddRefs); }