# HG changeset patch # User Paul Boddie # Date 1656370934 -7200 # Node ID 71dbba1bfe81a2585eccb19c8ac684a13e4905ef # Parent 3d935a7f97857ac9b8c6b7a70012235eb17227c1 Added documentation of file closure, removal and notification mechanisms. diff -r 3d935a7f9785 -r 71dbba1bfe81 docs/wiki/FilesystemAccess --- a/docs/wiki/FilesystemAccess Mon Jun 27 16:30:51 2022 +0200 +++ b/docs/wiki/FilesystemAccess Tue Jun 28 01:02:14 2022 +0200 @@ -112,7 +112,7 @@ already open. The `ProviderRegistry` monitors the usage of files, discarding any `Provider` no longer in use. -== Opening a File == +== Opening Files == Opening a file involves the identification of the filesystem object involved, the acquisition of a `Provider` representing the file, and the creation of a @@ -211,27 +211,28 @@ Opener; - ResourceRegistry [label="ResourceRegistry"]; + ResourceRegistry; subgraph { rank=same; Provider_note [shape=note,style=filled,fillcolor=gold,label="Created\nand set in\nregistry"]; - Provider [label="Provider"]; + Provider; - Accessor [label="Accessor"]; - Accessor_note [shape=note,style=filled,fillcolor=gold,label="Created\nfor provider"]; + Accessor; + PageMapper; + PageMapper_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]; + PageMapper -> PageMapper_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"]; + ProviderRegistry; + FileOpening; FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted]; @@ -244,7 +245,7 @@ ResourceRegistry -> FileOpening [label="make_accessor"]; FileOpening -> Accessor [dir=none]; - Accessor -> ResourceRegistry; + Accessor -> PageMapper -> ResourceRegistry; ResourceRegistry -> Provider [dir=none]; Provider -> ProviderRegistry [label="set(fileid)"]; @@ -252,3 +253,359 @@ }}} ######## + +== Notifications == + +Within the filesystem access architecture, users of files may act in ways that +may notify other users of the same file. To support such notifications, an +interface is provided for filesystem clients to subscribe and unsubscribe to +notifications. The notifications themselves occur when files are opened, +modified and closed. Pipes also generate notifications when a pipe reader +makes more space available for the writer. + +######## A graph showing subscription to notifications + +{{{#!graphviz +#format svg +#transform notugly +digraph notification_subscriptions { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=same; + + Client1 [label="Client\nprogram"]; + Client2 [label="Client\nprogram"]; + } + + subgraph { + rank=same; + + Notifier1_note [shape=note,style=filled,fillcolor=gold,label="Created for\nnotifications"]; + Notifier1 [label="Notifier"]; + Notifier2 [label="Notifier"]; + + Notifier1_note -> Notifier1 -> Notifier2 [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Resource1 [label="Resource\n(Pager)"]; + Resource2 [label="Resource\n(Pager)"]; + } + + subgraph { + rank=same; + + Notifier1_subscribe_note [shape=note,style=filled,fillcolor=gold,label="Propagated to\nprovider"]; + Notifier1_subscribe [label="Notifier"]; + Notifier2_subscribe [label="Notifier"]; + + Notifier1_subscribe_note -> Notifier1_subscribe -> Notifier2_subscribe [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Provider_note [shape=note,style=filled,fillcolor=gold,label="Manages file\nsubscriptions"]; + Provider [label="Provider"]; + + Provider_note -> Provider [dir=none,style=dotted]; + } + + /* Subscribing. */ + + Client1 -> Notifier1 [dir=none]; + Notifier1 -> Resource1 [label="subscribe"]; + + Resource1 -> Notifier1_subscribe [dir=none]; + Notifier1_subscribe -> Provider [label="subscribe"]; + + Client2 -> Notifier2 [dir=none]; + Notifier2 -> Resource2 [label="subscribe"]; + + Resource2 -> Notifier2_subscribe [dir=none]; + Notifier2_subscribe -> Provider [label="subscribe"]; +} +}}} + +######## + +An example of a notification scenario is given below: + +######## A graph showing notification + +{{{#!graphviz +#format svg +#transform notugly +digraph notifications { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=same; + + Client1 [label="Client\nprogram"]; + Client2 [label="Client\nprogram"]; + } + + subgraph { + rank=same; + + Notifier1_note [shape=note,style=filled,fillcolor=gold,label="Registered for\nnotifications"]; + Notifier1 [label="Notifier"]; + + Resource2 [label="Resource\n(Pager)"]; + Resource2_note [shape=note,style=filled,fillcolor=gold,label="Generates\nnotification"]; + + Notifier1_note -> Notifier1 [dir=none,style=dotted]; + Resource2 -> Resource2_note [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Provider_note [shape=note,style=filled,fillcolor=gold,label="Propagates\nnotifications"]; + Provider [label="Provider"]; + + Provider_note -> Provider [dir=none,style=dotted]; + } + + /* A notification scenario. */ + + Client2 -> Resource2 [label="resize"]; + Resource2 -> Provider [label="notify_others"]; + Provider -> Notifier1 [label="notify"]; + Client1 -> Notifier1 [label="wait"]; +} +}}} + +######## + +Notifications provide the basis for blocking reading and writing operations, +with pipe endpoints depending on such blocking for efficient use of pipes. The +interactions involved when transferring data through pipes are depicted below. + +######## A graph showing notifications employed by pipe endpoints + +{{{#!graphviz +#format svg +#transform notugly +digraph pipe_notifications { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=min; + + Client1_note [shape=note,style=filled,fillcolor=gold,label="Fill pipe\nthen request\nmore space"]; + Client1 [label="Client\nprogram\n(writer)"]; + Client1_wait_note [shape=note,style=filled,fillcolor=gold,label="Waiting after\nfilling pipe"]; + Client1_wait [label="Client\nprogram\n(writer)"]; + + Client1_note -> Client1 [dir=none,style=dotted]; + Client1_wait_note -> Client1_wait [dir=none,style=dotted]; + + Client1 -> Client1_wait_note [dir=none,style=invis]; + } + + subgraph { + rank=max; + + Client2_note [shape=note,style=filled,fillcolor=gold,label="Waiting for\nmore content\nbefore reading"]; + Client2 [label="Client\nprogram\n(reader)"]; + Client2_read_note [shape=note,style=filled,fillcolor=gold,label="Empty pipe\nthen request\nmore content"]; + Client2_read [label="Client\nprogram\n(reader)"]; + + Client2_note -> Client2 [dir=none,style=dotted]; + Client2_read_note -> Client2_read [dir=none,style=dotted]; + } + + subgraph { + rank=same; + + Notifier1 [label="Notifier"]; + Resource1 [label="Resource\n(PipePager)"]; + } + + subgraph { + rank=same; + + Notifier2 [label="Notifier"]; + Resource2 [label="Resource\n(PipePager)"]; + } + + subgraph { + rank=same; + + PipePaging [label="Provider\n(PipePaging)"]; + PipePaging_space [label="Provider\n(PipePaging)"]; + } + + /* Making content available. */ + + Client1 -> Resource1 [label="next_region"]; + Resource1 -> PipePaging [label="add_region"]; + PipePaging -> Notifier2 [label="notify"]; + Client2 -> Notifier2 [label="wait"]; + + /* Making space available. */ + + Client2_read -> Resource2 [label="next_region"]; + Resource2 -> PipePaging_space [label="next_region"]; + PipePaging_space -> Notifier1 [label="notify"]; + Client1_wait -> Notifier1 [label="wait"]; +} +}}} + +######## + +== Closing Files == + +Files are closed when client programs release their references to the resource +capabilities used to access files. The mechanism by which this is done +involves the registration of an IRQ object that is bound to the same thread as +the one used by resources to service file access requests. This IRQ object is +associated with the IPC gate through which such requests occur, and the IRQ +object is configured to deliver an interrupt message when the final reference +to the IPC gate has been released. + +When a client program terminates, its references to capabilities should be +released, and this should cause a reference counter associated with the IPC +gate to decrement. Upon the loss of the final reference and the delivery of +the interrupt, the server framework will call a `close` method on the +`Resource` object concerned, this implementing the +[[ServerLibrary#Accountable|`Accountable`]] interface. + +######## A graph showing the closing mechanism + +{{{#!graphviz +#format svg +#transform notugly +digraph closing { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + Client [label="Client\nprogram"]; + + subgraph { + rank=same; + + Gate_note [shape=note,style=filled,fillcolor=gold,label="Actual\nendpoint"]; + Gate [label="IPC gate",shape="octagon"]; + IRQ [shape="triangle"]; + ResourceServer; + ResourceServer_note [shape=note,style=filled,fillcolor=gold,label="Handles\nrequests"]; + + Gate_note -> Gate [dir=none,style=dotted]; + ResourceServer -> ResourceServer_note [dir=none,style=dotted]; + } + + Resource [label="Resource\n(Pager)"]; + + Provider; + + ProviderRegistry; + + Client -> Gate [style=dashed,label="(release)"]; + Gate -> IRQ -> ResourceServer; + ResourceServer -> Resource [label="close"]; + Resource -> Provider [label="notify_others\nunsubscribe"]; + Resource -> ProviderRegistry [label="detach"]; +} +}}} + +######## + +As a consequence of closing a file, an `unsubscribe` operation performed on +the `Provider` should cause any `Notifier` objects associated with the +`Resource` object to be released. Meanwhile, the `ResourceServer` will discard +the `Resource` object before the server thread terminates. + +== Removing Files == + +Unix filesystem semantics typically allow a file to be "unlinked" while still +being accessed by a program, with accesses still being directed to the file, +but with the file being removed from a directory and potentially being +replaced by another file. To support this, a mechanism is provided to defer +any removal of an open file. + +######## A graph showing the removal mechanism + +{{{#!graphviz +#format svg +#transform notugly +digraph removing { + node [fontsize="12.0",fontname="sans-serif",shape=box]; + edge [fontsize="12.0",fontname="sans-serif"]; + rankdir=LR; + + subgraph { + rank=same; + + Client1_note [shape=note,style=filled,fillcolor=gold,label="Removing\nopen file"]; + Client1 [label="Client\nprogram"]; + Client2 [label="Client\nprogram"]; + Client2_note [shape=note,style=filled,fillcolor=gold,label="Closes\nopen file"]; + + Client1_note -> Client1 [dir=none,style=dotted]; + Client2 -> Client2_note [dir=none,style=dotted]; + + Client1 -> Client2 [dir=none,style=invis]; + } + + subgraph { + rank=same; + + Resource [label="Resource\n(Pager)"]; + Opener; + } + + subgraph { + rank=same; + + Provider1 [label="Provider"]; + Provider1_note [shape=note,style=filled,fillcolor=gold,label="Maintains\nremoval\nstate"]; + Provider2 [label="Provider"]; + + Provider1 -> Provider1_note -> Provider2 [dir=none,style=dotted]; + } + + ProviderRegistry1 [label="ProviderRegistry"]; + ProviderRegistry2 [label="ProviderRegistry"]; + + subgraph { + rank=same; + + FileOpening; + Accessor_note [shape=note,style=filled,fillcolor=gold,label="Performing\nfilesystem\noperations"]; + Accessor; + + FileOpening -> Accessor_note -> Accessor [dir=none,style=dotted]; + } + + /* Removing the file while it remains open. */ + + Client1 -> Opener [label="remove\n(via context)"]; + Opener -> ResourceRegistry [label="remove_provider"]; + ResourceRegistry -> FileOpening [label="get_fileid\nunlink_object"]; + ResourceRegistry -> ProviderRegistry1 [label="get(fileid)"]; + ResourceRegistry -> Provider1 [label="remove_pending"]; + + /* File closure and deferred removal. */ + + Client2 -> Resource [label="close"]; + Resource -> Provider2 [label="notify_others\nunsubscribe"]; + Resource -> ProviderRegistry2 [label="detach"]; + ProviderRegistry2 -> Provider2 [label="remove"]; + Provider2 -> Accessor [label="remove"]; +} +}}} + +########