# HG changeset patch # User Paul Boddie # Date 1626388764 -7200 # Node ID 83c79809b8530bb654e3a11284bbb73176bfe99f # Parent b0f34fc8d4d8192d634a803db7bec8567ce4e661 Added support for notifications when accessing files. diff -r b0f34fc8d4d8 -r 83c79809b853 conf/dstest_file.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/dstest_file.cfg Fri Jul 16 00:39:24 2021 +0200 @@ -0,0 +1,23 @@ +-- vim:set ft=lua: + +local L4 = require("L4"); + +local l = L4.default_loader; + +local server = l:new_channel(); + +l:startv({ + caps = { + server = server:svr(), + }, + log = { "server", "r" }, + }, + "rom/dstest_block_server", "10"); + +l:startv({ + caps = { + server = server, + }, + log = { "client", "g" }, + }, + "rom/dstest_file_client", "rom/dstest_file.cfg"); diff -r b0f34fc8d4d8 -r 83c79809b853 conf/dstest_file.list --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/conf/dstest_file.list Fri Jul 16 00:39:24 2021 +0200 @@ -0,0 +1,25 @@ +entry dstest_file +roottask moe rom/dstest_file.cfg +module dstest_file.cfg +module l4re +module ned +module dstest_file_client +module dstest_block_server +module lib4re-c.so +module lib4re-c-util.so +module lib4re.so +module lib4re-util.so +module libc_be_l4refile.so +module libc_be_l4re.so +module libc_be_socket_noop.so +module libc_support_misc.so +module libdl.so +module libipc.so +module libl4sys-direct.so +module libl4sys.so +module libl4util.so +module libld-l4.so +module libpthread.so +module libstdc++.so +module libsupc++.so +module libuc_c.so diff -r b0f34fc8d4d8 -r 83c79809b853 libfsserver/include/fsserver/file_pager.h --- a/libfsserver/include/fsserver/file_pager.h Fri Jul 16 00:38:55 2021 +0200 +++ b/libfsserver/include/fsserver/file_pager.h Fri Jul 16 00:39:24 2021 +0200 @@ -34,6 +34,14 @@ protected: FilePaging *_paging; + /* Notification endpoint for event subscription. */ + + unsigned int _endpoint; + + /* Resize flag for notification. */ + + bool _resized = false; + public: fileid_t fileid; @@ -64,6 +72,12 @@ virtual long mmap(offset_t position, offset_t length, offset_t *start_pos, offset_t *end_pos, offset_t *size); + + /* Notification methods. */ + + virtual long subscribe(l4_cap_idx_t notifier, notify_flags_t flags); + + virtual long unsubscribe(l4_cap_idx_t notifier); }; // vim: tabstop=4 expandtab shiftwidth=4 diff -r b0f34fc8d4d8 -r 83c79809b853 libfsserver/include/fsserver/file_paging.h --- a/libfsserver/include/fsserver/file_paging.h Fri Jul 16 00:38:55 2021 +0200 +++ b/libfsserver/include/fsserver/file_paging.h Fri Jul 16 00:39:24 2021 +0200 @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -39,7 +40,7 @@ /* A registry of mappers for accessors. */ -class FilePaging +class FilePaging : public NotificationSupport { protected: Pages *_pages; @@ -55,9 +56,11 @@ /* Configurable methods. */ - virtual fileid_t get_fileid(const char *path) = 0; + virtual map_flags_t get_flags(flags_t flags); - virtual map_flags_t get_flags(flags_t flags); + /* Configurable methods requiring implementation. */ + + virtual fileid_t get_fileid(const char *path) = 0; virtual Accessor *make_accessor(const char *path, fileid_t fileid) = 0; @@ -72,8 +75,6 @@ public: explicit FilePaging(Pages *pages); - virtual ~FilePaging(); - /* Methods for the pager. */ void detach_pager(fileid_t fileid, PageMapper *mapper); diff -r b0f34fc8d4d8 -r 83c79809b853 libfsserver/lib/Makefile --- a/libfsserver/lib/Makefile Fri Jul 16 00:38:55 2021 +0200 +++ b/libfsserver/lib/Makefile Fri Jul 16 00:39:24 2021 +0200 @@ -16,7 +16,7 @@ # Compound interfaces. mapped_file_object_NAME = MappedFileObject -mapped_file_object_INTERFACES = dataspace file flush mapped_file +mapped_file_object_INTERFACES = dataspace file flush mapped_file notification opener_context_object_NAME = OpenerContextObject opener_context_object_INTERFACES = dataspace opener_context diff -r b0f34fc8d4d8 -r 83c79809b853 libfsserver/lib/files/file_pager.cc --- a/libfsserver/lib/files/file_pager.cc Fri Jul 16 00:38:55 2021 +0200 +++ b/libfsserver/lib/files/file_pager.cc Fri Jul 16 00:39:24 2021 +0200 @@ -51,6 +51,10 @@ void FilePager::close() { _paging->detach_pager(fileid, _mapper); + + /* Notify other users of the file. */ + + _paging->notify_others(_endpoint, NOTIFY_PEER_CLOSED); } @@ -59,12 +63,29 @@ long FilePager::flush(offset_t populated_size, offset_t *size) { - return Pager::flush(populated_size, size); + long err = Pager::flush(populated_size, size); + + if (_resized) + { + _paging->notify_others(_endpoint, NOTIFY_CONTENT_AVAILABLE); + _resized = false; + } + + return err; } long FilePager::resize(offset_t *size) { - return Pager::resize(size); + offset_t old_size = get_data_size(); + long err = Pager::resize(size); + + /* If the size has changed, notify other users, but only after data has been + flushed. */ + + if (old_size < get_data_size()) + _resized = true; + + return err; } long FilePager::mmap(offset_t position, offset_t length, offset_t *start_pos, @@ -84,4 +105,23 @@ return Pager::map(offset, hot_spot, flags, region); } + + +/* Subscribe to notifications. */ + +long FilePager::subscribe(l4_cap_idx_t notifier, notify_flags_t flags) +{ + /* Readers can subscribe to new data (at end), and pipe closed events. + Writers can subscribe to new space and pipe closed events. */ + + _endpoint = _paging->subscribe(notifier, flags); + return L4_EOK; +} + +long FilePager::unsubscribe(l4_cap_idx_t notifier) +{ + _paging->unsubscribe(_endpoint, notifier); + return L4_EOK; +} + // vim: tabstop=4 expandtab shiftwidth=4 diff -r b0f34fc8d4d8 -r 83c79809b853 libfsserver/lib/files/file_paging.cc --- a/libfsserver/lib/files/file_paging.cc Fri Jul 16 00:38:55 2021 +0200 +++ b/libfsserver/lib/files/file_paging.cc Fri Jul 16 00:39:24 2021 +0200 @@ -27,11 +27,7 @@ FilePaging::FilePaging(Pages *pages) -: _pages(pages) -{ -} - -FilePaging::~FilePaging() +: NotificationSupport(), _pages(pages) { } diff -r b0f34fc8d4d8 -r 83c79809b853 tests/Makefile --- a/tests/Makefile Fri Jul 16 00:38:55 2021 +0200 +++ b/tests/Makefile Fri Jul 16 00:39:24 2021 +0200 @@ -4,6 +4,7 @@ TARGET = \ dstest_block_client dstest_block_client_simple \ dstest_ext2fs_client \ + dstest_file_client \ dstest_host_client \ dstest_pipe_client \ dstest_test_client \ @@ -16,6 +17,8 @@ SRC_CC_dstest_ext2fs_client = dstest_ext2fs_client.cc +SRC_CC_dstest_file_client = dstest_file_client.cc + SRC_CC_dstest_host_client = dstest_host_client.cc SRC_CC_dstest_pipe_client = dstest_pipe_client.cc diff -r b0f34fc8d4d8 -r 83c79809b853 tests/dstest_file_client.cc --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/dstest_file_client.cc Fri Jul 16 00:39:24 2021 +0200 @@ -0,0 +1,168 @@ +/* + * Test file operations. + * + * Copyright (C) 2020, 2021 Paul Boddie + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA + */ + +#include +#include + +#include + +#include +#include +#include + +#include +#include + + + +/* Write data to the file. */ + +static void write_data(file_t *file, int region) +{ + offset_t size = 600; + char buffer[size]; + + memset(buffer, (int) 'a' + region, size); + + offset_t nwritten = client_write(file, buffer, size); + + printf("Written %ld/%ld in #%d to file with first %c, last %c...\n", nwritten, size, region, *buffer, *(buffer + nwritten - 1)); + + /* Flush to make the output available. */ + + client_flush(file); +} + +/* Read and write data to the file. */ + +static void read_and_write(file_t *file, bool creator) +{ + offset_t size = 600, total = 0; + long err; + int region = 0; + + /* Register for notifications. */ + + if ((err = client_subscribe(file, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED))) + { + printf("Could not subscribe to notifications: %s\n", l4sys_errtostr(err)); + return; + } + + /* Seek to the end of the file, if this is not actually a new file. */ + + client_seek(file, 0, SEEK_END); + + printf("File %s at position %ld...\n", creator ? "creator" : "follower", client_tell(file)); + + /* Write initial data as the file's creator. */ + + if (creator) + write_data(file, region++); + + while (region < 26) + { + char buffer[size]; + offset_t nread; + + /* Wait for notification of content. */ + + err = client_wait_file(file); + + if (err) + { + printf("Error waiting for notifications: %s\n", l4sys_errtostr(err)); + return; + } + + printf("File %s notified with conditions:%s%s\n", creator ? "creator" : "follower", + file->notifications & NOTIFY_PEER_CLOSED ? " closed" : "", + file->notifications & NOTIFY_CONTENT_AVAILABLE ? " content" : ""); + + /* Attempt to read. */ + + nread = client_read(file, buffer, size); + + while (nread) + { + total += nread; + + printf("Read as %s %ld/%ld, total %ld, first %c, last %c, from file...\n", creator ? "creator" : "follower", + nread, size, total, *buffer, *(buffer + nread - 1)); + + if (!(*(buffer + nread - 1))) + printf("Warning: length before zero region is %ld\n", strlen(buffer)); + +#if 0 + for (offset_t i = 0; i < nread; i += 60) + { + fwrite(buffer + i, sizeof(char), nread - i > 60 ? 60 : nread - i, stdout); + fputs("\n", stdout); + } +#endif + nread = client_read(file, buffer, size); + } + + /* Without any more content, a peer closed event should terminate reading + from the pipe. */ + + if (file->notifications & NOTIFY_PEER_CLOSED) + break; + + write_data(file, region++); + } + + printf("Data shown.\n"); +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) + { + printf("Need a filename.\n"); + return 1; + } + + char *filename = argv[1]; + + /* Invoke the open function to receive each file reference. */ + + file_t *file1 = client_open(filename, O_RDWR); // | O_CREAT + file_t *file2 = client_open(filename, O_RDWR); + + if ((file1 == NULL) || (file2 == NULL)) + { + printf("Could not obtain files.\n"); + return 1; + } + + /* Schedule threads. */ + + std::thread *activities[2]; + + activities[0] = new std::thread(read_and_write, file1, true); + activities[1] = new std::thread(read_and_write, file2, false); + + for (int i = 0; i < 2; i++) + activities[i]->join(); +} + +// vim: tabstop=2 expandtab shiftwidth=2