⛏️ index : haiku.git

/*
 * Copyright 2011, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Clemens Zeidler <haiku@clemens-zeidler.de>
 */

#include "FileMonitor.h"

#include <Looper.h>

#include <Messenger.h>
#include <NodeMonitor.h>


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);
}