# HG changeset patch # User Paul Boddie # Date 1656190202 -7200 # Node ID 1c4421a0046f464c322217dd397d670dc7c2792e # Parent 6997274d527a83e5e0686a7f27470e8318f74a93 Introduced a dedicated page for server library details, expanding the filesystem access details, focusing on the essential abstractions. diff -r 6997274d527a -r 1c4421a0046f docs/wiki/FilesystemAccess --- a/docs/wiki/FilesystemAccess Thu Jun 23 23:05:52 2022 +0200 +++ b/docs/wiki/FilesystemAccess Sat Jun 25 22:50:02 2022 +0200 @@ -1,13 +1,11 @@ = Filesystem Access = -Within the filesystem server library, a number of different abstractions and -mechanisms are employed to provide access to filesystem objects. +Within the [[ServerLibrary|filesystem server library]], a number of different +abstractions and mechanisms are employed to provide access to filesystem +objects. An overview of these abstractions is presented below. -<> - -The abstractions or components used in the library are organised as follows. - -######## A graph showing the relationships between library components +######## A graph showing the relationships between filesystem access +######## components {{{#!graphviz #format svg @@ -20,281 +18,237 @@ subgraph { rank=same; - Resource -> DirectoryResource -> FilePager - [dir=none,style=dotted]; + Opener_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfile open\noperation"]; + Opener; + + Opener_note -> Opener [dir=none,style=dotted]; } subgraph { rank=same; - Provider -> DirectoryProvider -> FileProvider - [dir=none,style=dotted]; + ResourceRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen file\nsessions"]; + ResourceRegistry; + + ResourceRegistry_note -> ResourceRegistry [dir=none,style=dotted]; } subgraph { rank=same; - Accessor -> Ext2FileAccessor - [dir=none,style=dotted]; + Resource_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"]; + Resource [label="Resource\n(Pager)"]; + + Resource_note -> Resource [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Provider_note [shape=note,style=filled,fillcolor=gold,label="Represents\nopen file"]; + Provider; + + Provider_note -> Provider [dir=none,style=dotted]; } subgraph { rank=same; - DirectoryAccessor -> Ext2DirectoryAccessor - [dir=none,style=dotted]; + PageMapper_note [shape=note,style=filled,fillcolor=gold,label="Provides\npopulated\nfile pages"]; + PageMapper; + + PageMapper_note -> PageMapper [dir=none,style=dotted]; + } + + ProviderRegistry [shape=record,label=" ProviderRegistry | ... | { file-n | provider-n } | ... "]; + + subgraph { + rank=same; + + FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; + FileOpening; + + FileOpening_note -> FileOpening [dir=none,style=dotted]; } subgraph { - node [shape=ellipse]; rank=same; - Directory [label="dir"]; - File [label="dir/file"]; + Accessor_note [shape=note,style=filled,fillcolor=gold,label="Populates\nfile pages"]; + Accessor; + Accessor_note -> Accessor [dir=none,style=dotted]; } - DirectoryResource -> DirectoryProvider -> Ext2DirectoryAccessor -> Directory; - FilePager -> FileProvider -> Ext2FileAccessor -> File; -} -}}} - -######## - -This document uses C-style interface syntax since the components operating -within the described mechanisms will themselves be described using such -syntax. - -== Accountable == - -This interface provides the following operations: - -{{{ -void attach() -unsigned int detach() -}}} + /* Opening a file. */ -Its purpose is to provide reference counting for components, with the `attach` -operation incrementing the number of users of a component, and with the -`detach` operation decrementing the number of users and returning the -resulting number of users. When components no longer have any attached users, -they may perform operations to tidy up after themselves and permit their -deallocation. - -== Accessors == - -Accessors provide the means of accessing filesystem object data and metadata. -Conceptually, files and directories are both exposed by accessors, although -the interfaces and mechanisms may differ between these types of objects. - -=== Directory Accessors === + Opener -> ResourceRegistry -> Resource; + ResourceRegistry -> FileOpening; -Currently, directory accessors provide support for traversing directory -listings, obtaining the relevant filesystem metadata using the underlying -filesystem access library. - -=== File Accessors === - -File content is accessed through an interface with the following operations: - -{{{ -void close() -void fill(Flexpage *flexpage) -void flush(Flexpage *flexpage) -offset_t get_size() -void set_size(offset_t size) -}}} + Provider -> PageMapper; + FileOpening -> Accessor; -The operations need to be supported with actual filesystem operations. For -example, ext2-based filesystems employ a specific abstraction which invokes -library functions provided by the `libext2fs` package. - -== Providers == - -Providers encapsulate the essential functionality for accessing filesystem -objects. Implementing the `Accountable` interface, they are shared by -resources and discarded when no resources are using them. + /* Closing a file. */ -The following operations are supported by providers: - -{{{ -ProviderRegistry *registry() -long make_resource(offset_t *size, object_flags_t *object_flags, Resource **resource) -bool removal_pending() -void remove() -void remove_pending(bool remove) -}}} - -Providers are associated with filesystem objects in a registry which can be -obtained from each provider using the `registry` operation. + Resource -> Provider -> ProviderRegistry:head; -Providers also support the creation of resources through which each user of a -provider exercises its use of that provider. The `make_resource` operation -performs the creation and returns size, flags and resource instance details. - -The removal of providers can be directed using the `remove_pending` and -`remove` operations. Where `remove_pending` has been called with a true value -as its parameter, the `removal_pending` operation will also return a true -value, and upon the provider being discarded, the `remove` operation will be -invoked. The `remove` operation performs the appropriate underlying operation -to remove the object being provided. - -Typically, removal of providers is managed by the provider registry when -resources are closed and detached from providers. + /* Open file management. */ -######## A graph showing the removal mechanism - -{{{#!graphviz -#format svg -#transform notugly -digraph provider_removal { - node [fontsize="12.0",fontname="sans-serif",shape=box]; - edge [fontsize="12.0",fontname="sans-serif"]; - rankdir=LR; - - ResourceServer -> Resource [label="close"]; - Resource -> ProviderRegistry [label="detach"]; - ProviderRegistry -> Provider [label="remove"]; + ResourceRegistry -> ProviderRegistry:head; } }}} ######## -== Resources == - -Resources are objects accessed by clients that support a basic level of -accounting and management. - -The base interface of a resource is as follows: - -{{{ -void activate() -void close() -}}} - -Activation of a resource is an optional operation that performs any -initialisation before a resource is made available to its user. - -In practice, other operations are required to make resources useful. - -In some cases, resources provide the mechanism by which each user of a -filesystem object may access that object independently. They would then -effectively provide a session in which accesses can occur. - -=== Directory Resources === - -Directory resources primarily expose the contents of directories in the -filesystem to a user. They employ directory accessors which concern themselves -with the actual filesystem content. +An `Opener` requests a `Resource` from a `ResourceRegistry`, each `Resource` +acting as a [[ServerLibrary#Pager|`Pager`]] and providing the actual access +mechanism to file content for a particular program. Since many programs may +access the same file simultaneously, a `Provider` object represents a file +opened in the system, retaining a `PageMapper` that coordinates the collective +access to the file (see the [[Paging|paging documentation]] for more +information). -[[Components#Directories|Directory components]] are provided using directory -resources. - -<> -=== Pagers or File Resources === - -Pagers are resources that support dataspace access operations, thus allowing -the resources to expose filesystem content in mapped memory regions to a -particular user of the object providing the content. - -[[Components#Files|File components]] and [[Components#Pipes|pipe components]] -are provided using pagers. +A `ProviderRegistry` maintains the record of opened files, yielding a new +`Provider` for an unopened file or an existing `Provider` for a file that is +already open. The `ProviderRegistry` monitors the usage of files, discarding +any `Provider` no longer in use. -=== Filesystem Resources === +== Opening a File == -Filesystem resources provide the entry point for access to a filesystem by -other components or programs. Since filesystems often enforce identity-based -access controls, a filesystem resource will typically support the -`open_for_user` operation in various forms, with the result of this operation -being the instantiation of an `OpenerResource` configured for the indicated -user identity. +Opening a file involves the identification of the filesystem object involved, +the acquisition of a `Provider` representing the file, and the creation of a +`Resource` to deliver file content to the requesting program. -== Registries == - -The basic mechanism for obtaining a resource involves a registry, as -illustrated by the following diagram. - -######## A graph showing the use of a registry in obtaining resources +######## A graph showing the opening mechanism for an already-open file {{{#!graphviz #format svg #transform notugly -digraph registry { +digraph opening_open { node [fontsize="12.0",fontname="sans-serif",shape=box]; edge [fontsize="12.0",fontname="sans-serif"]; rankdir=LR; subgraph { - node [label="OpenerContextResource"]; rank=same; - OpenerContextResource1 -> OpenerContextResource2 [dir=none,style=dotted]; + Opener_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfile open\noperation"]; + Opener; + + Opener_note -> Opener [dir=none,style=dotted]; } subgraph { - node [label="OpenerResource"]; rank=same; - OpenerResource1 -> OpenerResource2 [dir=none,style=dotted]; + ResourceRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen file\nsessions"]; + ResourceRegistry; + + Resource_returned [label="Resource\n(Pager)"]; + Resource_returned_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"]; + + ResourceRegistry_note -> ResourceRegistry [dir=none,style=dotted]; + Resource_returned -> Resource_returned_note [dir=none,style=dotted]; } subgraph { rank=same; - ResourceRegistry -> Resource; + Resource [label="Resource\n(Pager)"]; + + FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; + FileOpening; + + FileOpening_note -> FileOpening [dir=none,style=dotted]; } subgraph { - rank=same; + rank=max; + + ProviderRegistry_note [shape=note,style=filled,fillcolor=gold,label="Provides\nopen files"]; + ProviderRegistry; - ProviderRegistry -> Provider; + Provider; + Provider_note [shape=note,style=filled,fillcolor=gold,label="Represents\nopen file"]; + + ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted]; + Provider -> Provider_note [dir=none,style=dotted]; } - OpenerContextResource1 -> OpenerResource1 [label="open"]; - OpenerResource1 -> ResourceRegistry [label="get_resource"]; - ResourceRegistry -> ProviderRegistry [label="get/set"]; + /* Obtaining a resource from the registry. */ + + Opener -> ResourceRegistry [label="get_resource"]; + ResourceRegistry -> Resource_returned [dir=none]; + Resource_returned -> Opener; + + /* Obtaining a provider. */ - Provider -> Resource -> OpenerResource2 -> OpenerContextResource2; + ResourceRegistry -> FileOpening [label="get_fileid"]; + ResourceRegistry -> ProviderRegistry [label="get(fileid)"]; + ProviderRegistry -> Provider; + + /* Obtaining the resource from the provider. */ + + ResourceRegistry -> Provider [label="make_resource"]; + Provider -> Resource [dir=none]; + Resource -> ResourceRegistry; } }}} ######## -The `ResourceRegistry` coordinates access to filesystem resources and, through -synchronisation, prevents conflicting operations from occurring concurrently. -For example, a removal operation on a file may not be allowed to occur while -an opening operation is in progress. +Where a provider does not already exist, with the file not having been opened +already, some extra interactions occur: -To achieve this, a common lock is employed to serialise access, with any -underlying filesystem operations being initiated from the registry while the -lock is held. An object providing the `FileOpening` interface for a filesystem -provides such operations, and the following interaction pattern is thereby -employed: - -######## A graph showing the registry coordinating filesystem operations +######## A graph showing opening mechanism details for an unopened file {{{#!graphviz #format svg #transform notugly -digraph registry_operations { +digraph opening_unopened { node [fontsize="12.0",fontname="sans-serif",shape=box]; edge [fontsize="12.0",fontname="sans-serif"]; rankdir=LR; - OpenerContextResource -> OpenerResource -> ResourceRegistry -> FileOpening; + Opener; + + ResourceRegistry [label="ResourceRegistry"]; + + subgraph { + rank=same; + + Provider_note [shape=note,style=filled,fillcolor=gold,label="Created\nand set in\nregistry"]; + Provider [label="Provider"]; + + Accessor [label="Accessor"]; + Accessor_note [shape=note,style=filled,fillcolor=gold,label="Created\nfor provider"]; + + Provider_note -> Provider [dir=none,style=dotted]; + Accessor -> Accessor_note [dir=none,style=dotted]; + } + + subgraph { + rank=max; + + ProviderRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen files"]; + ProviderRegistry [label="ProviderRegistry"]; + FileOpening [label="FileOpening"]; + FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; + + ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted]; + FileOpening -> FileOpening_note [dir=none,style=dotted]; + } + + Opener -> ResourceRegistry [dir=none,style=dashed]; + + /* Obtaining a new provider. */ + + ResourceRegistry -> FileOpening [label="make_accessor"]; + FileOpening -> Accessor [dir=none]; + Accessor -> ResourceRegistry; + + ResourceRegistry -> Provider [dir=none]; + Provider -> ProviderRegistry [label="set(fileid)"]; } }}} ######## - -Since the `ResourceRegistry` functionality is generic, it could be specialised -for each filesystem or be configured with an appropriate reference to a -`FileOpening` object. The `OpenerResource` would then be generic, invoking the -registry which would, in turn, invoke the opening object. - -However, the chosen approach is to permit `OpenerResource` objects to -implement the `FileOpening` interface for each filesystem, meaning that the -`ResourceRegistry` will end up being called by the opener and then invoking -the opener in return. This is slightly more convenient than the alternative -since the opener can be configured with a given user identity, and such -identity details will ultimately be employed when accessing the underlying -filesystem itself. diff -r 6997274d527a -r 1c4421a0046f docs/wiki/Filesystems --- a/docs/wiki/Filesystems Thu Jun 23 23:05:52 2022 +0200 +++ b/docs/wiki/Filesystems Sat Jun 25 22:50:02 2022 +0200 @@ -13,4 +13,6 @@ that manage filesystem resources and content * [[Paging]] - the mechanism by which filesystem content is provided for use by client programs + * [[ServerLibrary|Server Library]] - abstractions to expose and manage access + to filesystem objects * [[Users]] diff -r 6997274d527a -r 1c4421a0046f docs/wiki/Paging --- a/docs/wiki/Paging Thu Jun 23 23:05:52 2022 +0200 +++ b/docs/wiki/Paging Sat Jun 25 22:50:02 2022 +0200 @@ -88,8 +88,8 @@ ######## -A [[FilesystemAccess#Pager|`Pager`]], being a resource that provides -filesystem content to clients for a particular file, requests pages from the +A [[ServerLibrary#Pager|`Pager`]], being a resource that provides filesystem +content to clients for a particular file, requests pages from the `PageMapper`, itself having the role of supplying populated pages of content from the given file. The `PageMapper` relies on the `AccessMap` to discover existing pages for any accessed region of a file, involving the `Pages` object @@ -111,6 +111,15 @@ the queue are themselves recycled and it is then removed from the queue for recycling. +== Satisfying Requests for Pages == + +The general procedure for satisfying requests for pages is as follows: + + 1. Attempt to find an existing page for the affected file region. + 1. With an existing page, attempt to reserve the page for issuing. + 1. Without an existing or reserved page, obtain a fresh page for populating. + 1. Queue the page for eventual reuse. + Since many files are likely to be in use, and since a fixed amount of memory may be shared to provide access to file content, the interaction between different `PageMapper` objects, operating on behalf of different files, must @@ -131,11 +140,13 @@ /* First page mapper activities. */ subgraph { - rank=min; + rank=same; PageMapper1_note [shape=note,style=filled,fillcolor=gold,label="Find\nflexpage"]; PageMapper1 [label="PageMapper"]; + Accessor1 [label="Accessor"]; + PageMapper1_reserve_note [shape=note,style=filled,fillcolor=gold,label="Reserve\nflexpage"]; PageMapper1_reserve [label="PageMapper"]; @@ -154,6 +165,7 @@ AccessMap_note [shape=note,style=filled,fillcolor=gold,label="Provides\nflexpages\ntentatively"]; AccessMap1 [label="AccessMap"]; + Flexpage1 [label="Flexpage",shape=note,style=dashed]; Flexpage1_note [shape=note,style=filled,fillcolor=gold,label="Needs reservation\nif found\nby mapper"]; @@ -164,7 +176,6 @@ Flexpage1_queue [label="Flexpage",shape=note]; AccessMap_note -> AccessMap1 [dir=none,style=dotted]; - Flexpage1 -> Flexpage1_note [dir=none,style=dotted]; Flexpage1_note -> Flexpage1_reserve [dir=none,style=dotted]; Flexpage1_reserved -> Flexpage1_reserved_note [dir=none,style=dotted]; @@ -228,6 +239,8 @@ PageMapper2_note [shape=note,style=filled,fillcolor=gold,label="Obtain\nflexpage"]; PageMapper2 [label="PageMapper"]; + Accessor2 [label="Accessor"]; + PageMapper2_record_note [shape=note,style=filled,fillcolor=gold,label="Record\nflexpage"]; PageMapper2_record [label="PageMapper"]; @@ -268,13 +281,16 @@ /* Flexpage removal from the access map. */ - Pages -> AccessMap1 [color="red",label="remove"]; + Pages -> AccessMap1 [color="red",label="remove\n(via PageMapper)"]; + AccessMap1 -> PageMapper1 [color="red",label="flush"]; + PageMapper1 -> Accessor1 [color="red",label="flush"]; /* Second pager dataflow. */ PageMapper2 -> Pages [color="red",label="flexpage"]; Pages -> Flexpage2 [color="red",dir=none]; Flexpage2 -> PageMapper2 [color="red"]; + PageMapper2 -> Accessor2 [label="fill"]; PageMapper2_record -> Flexpage2_reserved [dir=none,label="insert"]; Flexpage2_reserved -> AccessMap2; @@ -290,16 +306,17 @@ When a `PageMapper` requests a page from the `Pages` object and when the `Pages` object needs to reclaim such a previously issued page, the page -obtained from the head of the queue is removed from the `AccessMap` that -currently owns it. The "owner" of such a page is employing the page to satisfy -requests for content in a region of a particular file. If modified, the page's -contents may be flushed to the underlying file when it is removed from the -owner. As a consequence of its removal, the `AccessMap` will no longer be able -to offer this page to satisfy a request for data in the affected region to its -`PageMapper`. A reclaimed page is then returned to the `PageMapper` requiring -it, and since the page will no longer reside in the `PageQueue`, it will be -exclusively available for that `PageMapper`, being populated with data from -the underlying file and then issued to its client. +obtained from the head of the queue is removed from the `AccessMap` employed +by the `PageMapper` that currently owns it. The "owner" of such a page is +employing the page to satisfy requests for content in a region of a particular +file. If modified, the page's contents may be flushed to the underlying file +when it is removed from the owner. As a consequence of its removal, the +`AccessMap` will no longer be able to offer this page to satisfy a request for +data in the affected region to its `PageMapper`. A reclaimed page is then +returned to the `PageMapper` requiring it, and since the page will no longer +reside in the `PageQueue`, it will be exclusively available for that +`PageMapper`, being populated with data from the underlying file and then +issued to its client. == Reserving Available Pages == diff -r 6997274d527a -r 1c4421a0046f docs/wiki/ServerLibrary --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/wiki/ServerLibrary Sat Jun 25 22:50:02 2022 +0200 @@ -0,0 +1,245 @@ += Server Library = + +Within the filesystem server library, a number of different abstractions and +mechanisms are employed to provide access to filesystem objects. + +<> + +This document uses C-style interface syntax since the components operating +within the described mechanisms will themselves be described using such +syntax. + +== Accountable == + +This interface provides the following operations: + +{{{ +void attach() +unsigned int detach() +}}} + +Its purpose is to provide reference counting for components, with the `attach` +operation incrementing the number of users of a component, and with the +`detach` operation decrementing the number of users and returning the +resulting number of users. When components no longer have any attached users, +they may perform operations to tidy up after themselves and permit their +deallocation. + +== Accessors == + +Accessors provide the means of accessing filesystem object data and metadata. +Conceptually, files and directories are both exposed by accessors, although +the interfaces and mechanisms may differ between these types of objects. + +=== Directory Accessors === + +Currently, directory accessors provide support for traversing directory +listings, obtaining the relevant filesystem metadata using the underlying +filesystem access library. + +=== File Accessors === + +File content is accessed through an interface with the following operations: + +{{{ +void close() +void fill(Flexpage *flexpage) +void flush(Flexpage *flexpage) +offset_t get_size() +void set_size(offset_t size) +}}} + +The operations need to be supported with actual filesystem operations. For +example, ext2-based filesystems employ a specific abstraction which invokes +library functions provided by the `libext2fs` package. + +== Providers == + +Providers encapsulate the essential functionality for accessing filesystem +objects. Implementing the `Accountable` interface, they are shared by +resources and discarded when no resources are using them. + +The following operations are supported by providers: + +{{{ +ProviderRegistry *registry() +long make_resource(offset_t *size, object_flags_t *object_flags, Resource **resource) +bool removal_pending() +void remove() +void remove_pending(bool remove) +}}} + +Providers are associated with filesystem objects in a registry which can be +obtained from each provider using the `registry` operation. + +Providers also support the creation of resources through which each user of a +provider exercises its use of that provider. The `make_resource` operation +performs the creation and returns size, flags and resource instance details. + +The removal of providers can be directed using the `remove_pending` and +`remove` operations. Where `remove_pending` has been called with a true value +as its parameter, the `removal_pending` operation will also return a true +value, and upon the provider being discarded, the `remove` operation will be +invoked. The `remove` operation performs the appropriate underlying operation +to remove the object being provided. + +Typically, removal of providers is managed by the provider registry when +resources are closed and detached from providers. + +######## A graph showing the removal mechanism + +{{{#!graphviz +#format svg +#transform notugly +digraph provider_removal { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + ResourceServer -> Resource [label="close"]; + Resource -> ProviderRegistry [label="detach"]; + ProviderRegistry -> Provider [label="remove"]; +} +}}} + +######## + +== Resources == + +Resources are objects accessed by clients that support a basic level of +accounting and management. + +The base interface of a resource is as follows: + +{{{ +void activate() +void close() +}}} + +Activation of a resource is an optional operation that performs any +initialisation before a resource is made available to its user. + +In practice, other operations are required to make resources useful. + +In some cases, resources provide the mechanism by which each user of a +filesystem object may access that object independently. They would then +effectively provide a session in which accesses can occur. + +=== Directory Resources === + +Directory resources primarily expose the contents of directories in the +filesystem to a user. They employ directory accessors which concern themselves +with the actual filesystem content. + +[[Components#Directories|Directory components]] are provided using directory +resources. + +<> +=== Pagers or File Resources === + +Pagers are resources that support dataspace access operations, thus allowing +the resources to expose filesystem content in mapped memory regions to a +particular user of the object providing the content. + +[[Components#Files|File components]] and [[Components#Pipes|pipe components]] +are provided using pagers. + +=== Filesystem Resources === + +Filesystem resources provide the entry point for access to a filesystem by +other components or programs. Since filesystems often enforce identity-based +access controls, a filesystem resource will typically support the +`open_for_user` operation in various forms, with the result of this operation +being the instantiation of an `OpenerResource` configured for the indicated +user identity. + +== Registries == + +The basic mechanism for obtaining a resource involves a registry, as +illustrated by the following diagram. + +######## A graph showing the use of a registry in obtaining resources + +{{{#!graphviz +#format svg +#transform notugly +digraph registry { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + node [label="OpenerContextResource"]; + rank=same; + + OpenerContextResource1 -> OpenerContextResource2 [dir=none,style=dotted]; + } + + subgraph { + node [label="OpenerResource"]; + rank=same; + + OpenerResource1 -> OpenerResource2 [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + ResourceRegistry -> Resource; + } + + subgraph { + rank=same; + + ProviderRegistry -> Provider; + } + + OpenerContextResource1 -> OpenerResource1 [label="open"]; + OpenerResource1 -> ResourceRegistry [label="get_resource"]; + ResourceRegistry -> ProviderRegistry [label="get/set"]; + + Provider -> Resource -> OpenerResource2 -> OpenerContextResource2; +} +}}} + +######## + +The `ResourceRegistry` coordinates access to filesystem resources and, through +synchronisation, prevents conflicting operations from occurring concurrently. +For example, a removal operation on a file may not be allowed to occur while +an opening operation is in progress. + +To achieve this, a common lock is employed to serialise access, with any +underlying filesystem operations being initiated from the registry while the +lock is held. An object providing the `FileOpening` interface for a filesystem +provides such operations, and the following interaction pattern is thereby +employed: + +######## A graph showing the registry coordinating filesystem operations + +{{{#!graphviz +#format svg +#transform notugly +digraph registry_operations { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + OpenerContextResource -> OpenerResource -> ResourceRegistry -> FileOpening; +} +}}} + +######## + +Since the `ResourceRegistry` functionality is generic, it could be specialised +for each filesystem or be configured with an appropriate reference to a +`FileOpening` object. The `OpenerResource` would then be generic, invoking the +registry which would, in turn, invoke the opening object. + +However, the chosen approach is to permit `OpenerResource` objects to +implement the `FileOpening` interface for each filesystem, meaning that the +`ResourceRegistry` will end up being called by the opener and then invoking +the opener in return. This is slightly more convenient than the alternative +since the opener can be configured with a given user identity, and such +identity details will ultimately be employed when accessing the underlying +filesystem itself.