Add a global node listener mechanism

This commit is contained in:
Ingo Weinhold 2011-07-05 22:49:07 +02:00
parent 44de97031e
commit 67f11c47a7
5 changed files with 306 additions and 10 deletions

View File

@ -18,6 +18,7 @@ HAIKU_PACKAGE_FS_SOURCES =
GlobalFactory.cpp
kernel_interface.cpp
Node.cpp
NodeListener.cpp
Package.cpp
PackageDirectory.cpp
PackageDomain.cpp

View File

@ -0,0 +1,39 @@
/*
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "NodeListener.h"
NodeListener::NodeListener()
:
fPrevious(this),
fNext(this),
fNode(NOT_LISTENING_NODE)
{
}
NodeListener::~NodeListener()
{
}
void
NodeListener::NodeAdded(Node* node)
{
}
void
NodeListener::NodeRemoved(Node* node)
{
}
void
NodeListener::NodeChanged(Node* node, uint32 statFields)
{
}

View File

@ -0,0 +1,115 @@
/*
* Copyright 2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef NODE_LISTENER_H
#define NODE_LISTENER_H
#include <util/DoublyLinkedList.h>
#include <util/OpenHashTable.h>
class Node;
#define NOT_LISTENING_NODE ((Node*)~(addr_t)0)
class NodeListener {
public:
NodeListener();
virtual ~NodeListener();
virtual void NodeAdded(Node* node);
virtual void NodeRemoved(Node* node);
virtual void NodeChanged(Node* node, uint32 statFields);
void StartedListening(Node* node)
{ fNode = node; }
void StoppedListening()
{ fNode = NOT_LISTENING_NODE; }
bool IsListening() const
{ return fNode != NOT_LISTENING_NODE; }
Node* ListenedNode() const
{ return fNode; }
inline void AddNodeListener(NodeListener* listener);
inline NodeListener* RemoveNodeListener();
NodeListener* PreviousNodeListener() const
{ return fPrevious; }
NodeListener* NextNodeListener() const
{ return fNext; }
NodeListener*& NodeListenerHashLink()
{ return fHashLink; }
private:
NodeListener* fHashLink;
NodeListener* fPrevious;
NodeListener* fNext;
Node* fNode;
};
inline void
NodeListener::AddNodeListener(NodeListener* listener)
{
listener->fPrevious = this;
listener->fNext = fNext;
fNext->fPrevious = listener;
fNext = listener;
}
inline NodeListener*
NodeListener::RemoveNodeListener()
{
if (fNext == this)
return NULL;
NodeListener* next = fNext;
fPrevious->fNext = next;
next->fPrevious = fPrevious;
fPrevious = fNext = this;
return next;
}
struct NodeListenerHashDefinition {
typedef Node* KeyType;
typedef NodeListener ValueType;
size_t HashKey(Node* key) const
{
return (size_t)key;
}
size_t Hash(const NodeListener* value) const
{
return HashKey(value->ListenedNode());
}
bool Compare(Node* key, const NodeListener* value) const
{
return key == value->ListenedNode();
}
NodeListener*& GetLink(NodeListener* value) const
{
return value->NodeListenerHashLink();
}
};
typedef DoublyLinkedList<NodeListener> NodeListenerList;
typedef BOpenHashTable<NodeListenerHashDefinition> NodeListenerHashTable;
#endif // NODE_LISTENER_H

View File

@ -52,6 +52,10 @@ using BPackageKit::BHPKG::BPrivate::PackageReaderImpl;
// node ID of the root directory
static const ino_t kRootDirectoryID = 1;
static const uint32 kAllStatFields = B_STAT_MODE | B_STAT_UID | B_STAT_GID
| B_STAT_SIZE | B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME
| B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME;
// shine-through directories
const char* const kSystemShineThroughDirectories[] = {
"packages", NULL
@ -482,11 +486,16 @@ Volume::~Volume()
status_t
Volume::Mount(const char* parameterString)
{
// init the node table
// init the hash tables
status_t error = fNodes.Init();
if (error != B_OK)
RETURN_ERROR(error);
error = fNodeListeners.Init();
if (error != B_OK)
RETURN_ERROR(error);
// get the mount parameters
const char* packages = NULL;
const char* volumeName = NULL;
const char* mountType = NULL;
@ -605,6 +614,42 @@ Volume::Unmount()
}
void
Volume::AddNodeListener(NodeListener* listener, Node* node)
{
ASSERT(!listener->IsListening());
listener->StartedListening(node);
if (NodeListener* list = fNodeListeners.Lookup(node))
list->AddNodeListener(listener);
else
fNodeListeners.Insert(listener);
}
void
Volume::RemoveNodeListener(NodeListener* listener)
{
ASSERT(listener->IsListening());
Node* node = listener->ListenedNode();
if (NodeListener* next = listener->RemoveNodeListener()) {
// list not empty yet -- if we removed the head, add a new head to the
// hash table
NodeListener* list = fNodeListeners.Lookup(node);
if (list == listener) {
fNodeListeners.Remove(listener);
fNodeListeners.Insert(next);
}
} else
fNodeListeners.Remove(listener);
listener->StoppedListening();
}
status_t
Volume::GetVNode(ino_t nodeID, Node*& _node)
{
@ -662,6 +707,7 @@ Volume::PackageLinkNodeAdded(Node* node)
_AddPackageLinksNode(node);
notify_entry_created(ID(), node->Parent()->ID(), node->Name(), node->ID());
_NotifyNodeAdded(node);
}
@ -671,6 +717,7 @@ Volume::PackageLinkNodeRemoved(Node* node)
_RemovePackageLinksNode(node);
notify_entry_removed(ID(), node->Parent()->ID(), node->Name(), node->ID());
_NotifyNodeRemoved(node);
}
@ -678,6 +725,7 @@ void
Volume::PackageLinkNodeChanged(Node* node, uint32 statFields)
{
notify_stat_changed(ID(), node->ID(), statFields);
_NotifyNodeChanged(node, statFields);
}
@ -1069,6 +1117,11 @@ Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode,
RETURN_ERROR(error);
}
if (newNode)
_NotifyNodeAdded(node);
else
_NotifyNodeChanged(node, kAllStatFields);
if (notify) {
if (newNode) {
notify_entry_created(ID(), directory->ID(), node->Name(),
@ -1078,10 +1131,7 @@ Volume::_AddPackageNode(Directory* directory, PackageNode* packageNode,
// Send stat changed notification for directories and entry
// removed + created notifications for files and symlinks.
if (S_ISDIR(packageNode->Mode())) {
notify_stat_changed(ID(), node->ID(),
B_STAT_MODE | B_STAT_UID | B_STAT_GID | B_STAT_SIZE
| B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME
| B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME);
notify_stat_changed(ID(), node->ID(), kAllStatFields);
// TODO: Actually the attributes might change, too!
} else {
notify_entry_removed(ID(), directory->ID(), node->Name(),
@ -1133,6 +1183,11 @@ Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode,
}
}
if (nodeRemoved)
_NotifyNodeRemoved(node);
else
_NotifyNodeChanged(node, kAllStatFields);
if (!notify)
return;
@ -1144,10 +1199,7 @@ Volume::_RemovePackageNode(Directory* directory, PackageNode* packageNode,
// Send stat changed notification for directories and entry
// removed + created notifications for files and symlinks.
if (S_ISDIR(packageNode->Mode())) {
notify_stat_changed(ID(), node->ID(),
B_STAT_MODE | B_STAT_UID | B_STAT_GID | B_STAT_SIZE
| B_STAT_ACCESS_TIME | B_STAT_MODIFICATION_TIME
| B_STAT_CREATION_TIME | B_STAT_CHANGE_TIME);
notify_stat_changed(ID(), node->ID(), kAllStatFields);
// TODO: Actually the attributes might change, too!
} else {
notify_entry_removed(ID(), directory->ID(), node->Name(),
@ -1556,3 +1608,81 @@ Volume::_SystemVolumeIfNotSelf() const
return systemVolume == this ? NULL : systemVolume;
return NULL;
}
void
Volume::_NotifyNodeAdded(Node* node)
{
Node* key = node;
for (int i = 0; i < 2; i++) {
if (NodeListener* listener = fNodeListeners.Lookup(key)) {
NodeListener* last = listener->PreviousNodeListener();
while (true) {
NodeListener* next = listener->NextNodeListener();
listener->NodeAdded(node);
if (listener == last)
break;
listener = next;
}
}
key = NULL;
}
}
void
Volume::_NotifyNodeRemoved(Node* node)
{
Node* key = node;
for (int i = 0; i < 2; i++) {
if (NodeListener* listener = fNodeListeners.Lookup(key)) {
NodeListener* last = listener->PreviousNodeListener();
while (true) {
NodeListener* next = listener->NextNodeListener();
listener->NodeRemoved(node);
if (listener == last)
break;
listener = next;
}
}
key = NULL;
}
}
void
Volume::_NotifyNodeChanged(Node* node, uint32 statFields)
{
Node* key = node;
for (int i = 0; i < 2; i++) {
if (NodeListener* listener = fNodeListeners.Lookup(key)) {
NodeListener* last = listener->PreviousNodeListener();
while (true) {
NodeListener* next = listener->NextNodeListener();
listener->NodeChanged(node, statFields);
if (listener == last)
break;
listener = next;
}
}
key = NULL;
}
}

View File

@ -15,12 +15,12 @@
#include <util/KMessage.h>
#include "Node.h"
#include "NodeListener.h"
#include "PackageDomain.h"
#include "PackageLinksListener.h"
class Directory;
class Node;
class PackageFSRoot;
class UnpackingNode;
@ -66,6 +66,11 @@ public:
Node* FindNode(ino_t nodeID) const
{ return fNodes.Lookup(nodeID); }
// node listeners -- volume must be write-locked
void AddNodeListener(NodeListener* listener,
Node* node);
void RemoveNodeListener(NodeListener* listener);
// VFS wrappers
status_t GetVNode(ino_t nodeID, Node*& _node);
status_t PutVNode(ino_t nodeID);
@ -162,6 +167,11 @@ private:
inline Volume* _SystemVolumeIfNotSelf() const;
void _NotifyNodeAdded(Node* node);
void _NotifyNodeRemoved(Node* node);
void _NotifyNodeChanged(Node* node,
uint32 statFields);
private:
mutable rw_lock fLock;
fs_volume* fFSVolume;
@ -177,6 +187,7 @@ private:
} fMountPoint;
NodeIDHashTable fNodes;
NodeListenerHashTable fNodeListeners;
JobList fJobQueue;
mutex fJobQueueLock;