Add a global node listener mechanism
This commit is contained in:
parent
44de97031e
commit
67f11c47a7
@ -18,6 +18,7 @@ HAIKU_PACKAGE_FS_SOURCES =
|
||||
GlobalFactory.cpp
|
||||
kernel_interface.cpp
|
||||
Node.cpp
|
||||
NodeListener.cpp
|
||||
Package.cpp
|
||||
PackageDirectory.cpp
|
||||
PackageDomain.cpp
|
||||
|
39
src/add-ons/kernel/file_systems/packagefs/NodeListener.cpp
Normal file
39
src/add-ons/kernel/file_systems/packagefs/NodeListener.cpp
Normal 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)
|
||||
{
|
||||
}
|
115
src/add-ons/kernel/file_systems/packagefs/NodeListener.h
Normal file
115
src/add-ons/kernel/file_systems/packagefs/NodeListener.h
Normal 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
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user