* Copyright 2007, Ingo Weinhold, ingo_weinhold@gmx.de.
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include <TypeConstants.h>
#include "DebugSupport.h"
#include "Entry.h"
#include "EntryListener.h"
#include "IndexImpl.h"
#include "LastModifiedIndex.h"
#include "Node.h"
#include "NodeListener.h"
#include "Volume.h"
#include <util/AutoLock.h>
class LastModifiedIndexPrimaryKey {
public:
LastModifiedIndexPrimaryKey(Node *node, time_t modified)
: node(node), modified(modified) {}
LastModifiedIndexPrimaryKey(Node *node)
: node(node), modified(node->GetMTime()) {}
LastModifiedIndexPrimaryKey(time_t modified)
: node(NULL), modified(modified) {}
Node *node;
time_t modified;
};
class LastModifiedIndexGetPrimaryKey {
public:
inline LastModifiedIndexPrimaryKey operator()(Node *a)
{
return LastModifiedIndexPrimaryKey(a);
}
inline LastModifiedIndexPrimaryKey operator()(Node *a) const
{
return LastModifiedIndexPrimaryKey(a);
}
};
class LastModifiedIndexPrimaryKeyCompare
{
public:
inline int operator()(const LastModifiedIndexPrimaryKey &a,
const LastModifiedIndexPrimaryKey &b) const
{
if (a.node != NULL && a.node == b.node)
return 0;
if (a.modified < b.modified)
return -1;
if (a.modified > b.modified)
return 1;
return 0;
}
};
typedef TwoKeyAVLTree<Node*, LastModifiedIndexPrimaryKey,
LastModifiedIndexPrimaryKeyCompare,
LastModifiedIndexGetPrimaryKey>
_NodeTree;
class LastModifiedIndex::NodeTree : public _NodeTree {};
class LastModifiedIndex::IteratorList : public DoublyLinkedList<Iterator> {};
class LastModifiedIndex::Iterator
: public NodeEntryIterator<LastModifiedIndex::NodeTree::Iterator>,
public DoublyLinkedListLinkImpl<Iterator>, public EntryListener,
public NodeListener {
public:
Iterator();
virtual ~Iterator();
virtual Entry *GetCurrent();
virtual Entry *GetCurrent(uint8 *buffer, size_t *keyLength);
virtual status_t Suspend();
virtual status_t Resume();
bool SetTo(LastModifiedIndex *index, time_t modified,
bool ignoreValue = false);
void Unset();
virtual void EntryRemoved(Entry *entry);
virtual void NodeRemoved(Node *node);
private:
typedef NodeEntryIterator<LastModifiedIndex::NodeTree::Iterator> BaseClass;
private:
LastModifiedIndex *fIndex;
};
LastModifiedIndex::LastModifiedIndex(Volume *volume)
: Index(volume, "last_modified", B_INT32_TYPE, true, sizeof(time_t)),
fNodes(new(nothrow) NodeTree),
fIterators(new(nothrow) IteratorList)
{
if (fInitStatus == B_OK && (!fNodes || !fIterators))
fInitStatus = B_NO_MEMORY;
if (fInitStatus == B_OK) {
fInitStatus = fVolume->AddNodeListener(this,
NULL, NODE_LISTEN_ANY_NODE | NODE_LISTEN_ALL);
}
}
LastModifiedIndex::~LastModifiedIndex()
{
if (fVolume)
fVolume->RemoveNodeListener(this, NULL);
if (fIterators) {
for (Iterator *iterator = fIterators->First();
iterator;
iterator = fIterators->GetNext(iterator)) {
iterator->SetTo(NULL, 0);
}
delete fIterators;
}
if (fNodes)
delete fNodes;
}
int32
LastModifiedIndex::CountEntries() const
{
return fNodes->CountItems();
}
status_t
LastModifiedIndex::Changed(Node *node, time_t oldModified)
{
fVolume->AssertWriteLocked();
status_t error = B_BAD_VALUE;
if (node) {
NodeTree::Iterator it;
Node **foundNode = fNodes->Find(LastModifiedIndexPrimaryKey(node,
oldModified), node, &it);
if (foundNode && *foundNode == node) {
for (Iterator *iterator = fIterators->First();
iterator;
iterator = fIterators->GetNext(iterator)) {
if (iterator->GetCurrentNode() == node)
iterator->NodeRemoved(node);
}
it.Remove();
error = fNodes->Insert(node);
time_t newModified = node->GetMTime();
fVolume->UpdateLiveQueries(NULL, node, GetName(), GetType(),
(const uint8*)&oldModified, sizeof(oldModified),
(const uint8*)&newModified, sizeof(newModified));
}
}
return error;
}
void
LastModifiedIndex::NodeAdded(Node *node)
{
if (node)
fNodes->Insert(node);
}
void
LastModifiedIndex::NodeRemoved(Node *node)
{
if (node)
fNodes->Remove(node, node);
}
AbstractIndexEntryIterator *
LastModifiedIndex::InternalGetIterator()
{
Iterator *iterator = new(nothrow) Iterator;
if (iterator) {
if (!iterator->SetTo(this, 0, true)) {
delete iterator;
iterator = NULL;
}
}
return iterator;
}
AbstractIndexEntryIterator *
LastModifiedIndex::InternalFind(const uint8 *key, size_t length)
{
if (!key || length != sizeof(time_t))
return NULL;
Iterator *iterator = new(nothrow) Iterator;
if (iterator) {
if (!iterator->SetTo(this, *(const time_t*)key)) {
delete iterator;
iterator = NULL;
}
}
return iterator;
}
void
LastModifiedIndex::_AddIterator(Iterator *iterator)
{
RecursiveLocker locker(fVolume->GetAttributeIteratorLock());
fIterators->Insert(iterator);
}
void
LastModifiedIndex::_RemoveIterator(Iterator *iterator)
{
RecursiveLocker locker(fVolume->GetAttributeIteratorLock());
fIterators->Remove(iterator);
}
LastModifiedIndex::Iterator::Iterator()
: BaseClass(),
fIndex(NULL)
{
}
LastModifiedIndex::Iterator::~Iterator()
{
SetTo(NULL, 0);
}
Entry *
LastModifiedIndex::Iterator::GetCurrent()
{
return BaseClass::GetCurrent();
}
Entry *
LastModifiedIndex::Iterator::GetCurrent(uint8 *buffer, size_t *keyLength)
{
Entry *entry = GetCurrent();
if (entry) {
*(time_t*)buffer = entry->GetNode()->GetMTime();
*keyLength = sizeof(time_t);
}
return entry;
}
status_t
LastModifiedIndex::Iterator::Suspend()
{
status_t error = BaseClass::Suspend();
if (error == B_OK) {
if (fNode) {
error = fIndex->GetVolume()->AddNodeListener(this, fNode,
NODE_LISTEN_REMOVED);
if (error == B_OK && fEntry) {
error = fIndex->GetVolume()->AddEntryListener(this, fEntry,
ENTRY_LISTEN_REMOVED);
if (error != B_OK)
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
}
if (error != B_OK)
BaseClass::Resume();
}
}
return error;
}
status_t
LastModifiedIndex::Iterator::Resume()
{
status_t error = BaseClass::Resume();
if (error == B_OK) {
if (fEntry)
error = fIndex->GetVolume()->RemoveEntryListener(this, fEntry);
if (fNode) {
if (error == B_OK)
error = fIndex->GetVolume()->RemoveNodeListener(this, fNode);
else
fIndex->GetVolume()->RemoveNodeListener(this, fNode);
}
}
return error;
}
bool
LastModifiedIndex::Iterator::SetTo(LastModifiedIndex *index, time_t modified,
bool ignoreValue)
{
Resume();
Unset();
fIndex = index;
if (fIndex)
fIndex->_AddIterator(this);
fInitialized = fIndex;
if (fIndex) {
bool found = true;
if (ignoreValue)
fIndex->fNodes->GetIterator(&fIterator);
else
found = fIndex->fNodes->FindFirst(modified, &fIterator);
if (found) {
if (Node **nodeP = fIterator.GetCurrent()) {
fNode = *nodeP;
fEntry = fNode->GetFirstReferrer();
if (!fEntry)
BaseClass::GetNext();
if (!ignoreValue && fNode && fNode->GetMTime() != modified)
Unset();
}
}
}
return fEntry;
}
void
LastModifiedIndex::Iterator::Unset()
{
if (fIndex) {
fIndex->_RemoveIterator(this);
fIndex = NULL;
}
BaseClass::Unset();
}
void
LastModifiedIndex::Iterator::EntryRemoved(Entry *)
{
Resume();
fIsNext = BaseClass::GetNext();
Suspend();
}
void
LastModifiedIndex::Iterator::NodeRemoved(Node *)
{
Resume();
fEntry = NULL;
fIsNext = BaseClass::GetNext();
Suspend();
}