file system: track content via version counter

This makes the delivery of CONTENT_CHANGED responses more robust.
This commit is contained in:
Norman Feske 2017-06-28 14:42:45 +02:00 committed by Christian Helmuth
parent 6a43f3c11a
commit 0b580628cf
4 changed files with 80 additions and 39 deletions

View File

@ -26,35 +26,61 @@ namespace File_system {
class Listener : public Genode::List<Listener>::Element
{
public:
struct Version { unsigned value; };
private:
Genode::Lock _lock;
Sink &_sink;
Node_handle _handle;
bool _marked_as_updated;
Genode::Lock _lock;
Sink &_sink;
Node_handle const _handle;
/*
* Version at the time when the file was opened
*/
Version _handed_out_version;
/*
* Version at the time when we issued the most recent notification
*/
Version _notified_version = _handed_out_version;
public:
Listener(Sink &sink, Node_handle handle)
: _sink(sink), _handle(handle), _marked_as_updated(false) { }
Listener(Sink &sink, Node_handle handle, Version handed_out_version)
: _sink(sink), _handle(handle), _handed_out_version(handed_out_version)
{ }
void notify()
/*
* Called on close of written files, on sync, or on arrival
* of a client's CONTENT_CHANGED packet.
*/
void notify(Version curr_version)
{
Genode::Lock::Guard guard(_lock);
if (_marked_as_updated && _sink.ready_to_ack()) {
if (curr_version.value == _handed_out_version.value)
return;
if (curr_version.value == _notified_version.value)
return;
if (_sink.ready_to_ack())
_sink.acknowledge_packet(Packet_descriptor(
_handle, Packet_descriptor::CONTENT_CHANGED));
_marked_as_updated = false;
}
_notified_version = curr_version;
}
void mark_as_updated()
/*
* Called during read
*/
void handed_out_version(Version version)
{
Genode::Lock::Guard guard(_lock);
_marked_as_updated = true;
_handed_out_version = version;
}
};

View File

@ -28,6 +28,10 @@ namespace File_system {
Genode::List<Listener> _listeners;
typedef Listener::Version Version;
Version _curr_version { 0 };
public:
virtual ~Node_base()
@ -53,14 +57,15 @@ namespace File_system {
void notify_listeners()
{
for (Listener *curr = _listeners.first(); curr; curr = curr->next())
curr->notify();
curr->notify(_curr_version);
}
void mark_as_updated()
{
for (Listener *curr = _listeners.first(); curr; curr = curr->next())
curr->mark_as_updated();
_curr_version = Version { _curr_version.value + 1 };
}
Version curr_version() const { return _curr_version; }
};
}

View File

@ -35,11 +35,19 @@ class File_system::Open_node : public File_system::Node
NODE &_node;
Genode::Constructible<File_system::Listener> _listener;
Listener::Version const _version_when_opened = _node.curr_version();
/*
* Flag to track whether the underlying file-system node was
* modified via this 'Open_node'. That is, if closing the 'Open_node'
* should notify listeners of the file.
*/
bool _was_written = false;
public:
Open_node(NODE &node, Genode::Id_space<File_system::Node> &id_space)
: _element(*this, id_space),
_node(node) { }
: _element(*this, id_space), _node(node) { }
~Open_node()
{
@ -47,6 +55,12 @@ class File_system::Open_node : public File_system::Node
_node.remove_listener(&*_listener);
_listener.destruct();
}
/*
* Notify remaining listeners about the changed file
*/
if (_was_written)
_node.notify_listeners();
}
NODE &node() { return _node; }
@ -71,9 +85,11 @@ class File_system::Open_node : public File_system::Node
/*
* Register new handler
*/
_listener.construct(sink, id());
_listener.construct(sink, id(), _version_when_opened);
_node.add_listener(&*_listener);
}
void mark_as_written() { _was_written = true; }
};
#endif /* _OPEN_NODE_H_ */

View File

@ -83,6 +83,8 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object
case Packet_descriptor::WRITE:
if (content && (packet.length() <= packet.size()))
res_length = open_node.node().write((char const *)content, length, packet.position());
open_node.mark_as_written();
break;
case Packet_descriptor::CONTENT_CHANGED:
@ -217,9 +219,10 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object
try {
File * const file = new (_alloc)
File(_alloc, name.string());
File(_alloc, name.string());
dir.adopt_unsynchronized(file);
open_node.mark_as_written();
}
catch (Allocator::Out_of_memory) { throw No_space(); }
}
@ -338,19 +341,7 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object
void close(Node_handle handle)
{
auto close_fn = [&] (Open_node &open_node) {
Node &node = open_node.node();
/*
* Notify listeners about the changed file.
*/
node.notify_listeners();
/*
* De-allocate handle
*/
destroy(_alloc, &open_node);
};
destroy(_alloc, &open_node); };
try {
_open_node_registry.apply<Open_node>(handle, close_fn);
@ -362,8 +353,7 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object
Status status(Node_handle node_handle)
{
auto status_fn = [&] (Open_node &open_node) {
return open_node.node().status();
};
return open_node.node().status(); };
try {
return _open_node_registry.apply<Open_node>(node_handle, status_fn);
@ -394,6 +384,7 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object
// is still referenced by a node handle
destroy(_alloc, node);
open_node.mark_as_written();
};
try {
@ -410,6 +401,7 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object
auto truncate_fn = [&] (Open_node &open_node) {
open_node.node().truncate(size);
open_node.mark_as_written();
};
try {
@ -448,15 +440,17 @@ class Ram_fs::Session_component : public File_system::Session_rpc_object
to_dir.adopt_unsynchronized(node);
/*
* If the file was moved from one directory to another we
* need to inform the new directory 'to_dir'. The original
* directory 'from_dir' will always get notified (i.e.,
* when just the file name was changed) below.
*/
* If the file was moved from one directory to another we
* need to inform the new directory 'to_dir'. The original
* directory 'from_dir' will always get notified (i.e.,
* when just the file name was changed) below.
*/
to_dir.mark_as_updated();
open_to_dir_node.mark_as_written();
to_dir.notify_listeners();
from_dir.mark_as_updated();
open_from_dir_node.mark_as_written();
from_dir.notify_listeners();
node->mark_as_updated();