1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/docs/wiki/Filesystem_Access Fri Aug 12 16:41:06 2022 +0200
1.3 @@ -0,0 +1,611 @@
1.4 += Filesystem Access =
1.5 +
1.6 +Within the [[ServerLibrary|filesystem server library]], a number of different
1.7 +abstractions and mechanisms are employed to provide access to filesystem
1.8 +objects. An overview of these abstractions is presented below.
1.9 +
1.10 +######## A graph showing the relationships between filesystem access
1.11 +######## components
1.12 +
1.13 +{{{#!graphviz
1.14 +#format svg
1.15 +#transform notugly
1.16 +digraph components {
1.17 + node [fontsize="12.0",fontname="sans-serif",shape=box];
1.18 + edge [fontsize="12.0",fontname="sans-serif"];
1.19 + rankdir=LR;
1.20 +
1.21 + subgraph {
1.22 + rank=same;
1.23 +
1.24 + Opener_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfile open\noperation"];
1.25 + Opener;
1.26 +
1.27 + Opener_note -> Opener [dir=none,style=dotted];
1.28 + }
1.29 +
1.30 + subgraph {
1.31 + rank=same;
1.32 +
1.33 + ResourceRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen file\nsessions"];
1.34 + ResourceRegistry;
1.35 +
1.36 + ResourceRegistry_note -> ResourceRegistry [dir=none,style=dotted];
1.37 + }
1.38 +
1.39 + subgraph {
1.40 + rank=same;
1.41 +
1.42 + Resource_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"];
1.43 + Resource [label="Resource\n(Pager)"];
1.44 +
1.45 + Resource_note -> Resource [dir=none,style=dotted];
1.46 + }
1.47 +
1.48 + subgraph {
1.49 + rank=same;
1.50 +
1.51 + Provider_note [shape=note,style=filled,fillcolor=gold,label="Represents\nopen file"];
1.52 + Provider;
1.53 +
1.54 + Provider_note -> Provider [dir=none,style=dotted];
1.55 + }
1.56 +
1.57 + subgraph {
1.58 + rank=same;
1.59 +
1.60 + PageMapper_note [shape=note,style=filled,fillcolor=gold,label="Provides\npopulated\nfile pages"];
1.61 + PageMapper;
1.62 +
1.63 + PageMapper_note -> PageMapper [dir=none,style=dotted];
1.64 + }
1.65 +
1.66 + ProviderRegistry [shape=record,label="<head> ProviderRegistry | ... | { file-n | provider-n } | ... "];
1.67 +
1.68 + subgraph {
1.69 + rank=same;
1.70 +
1.71 + FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"];
1.72 + FileOpening;
1.73 +
1.74 + FileOpening_note -> FileOpening [dir=none,style=dotted];
1.75 + }
1.76 +
1.77 + subgraph {
1.78 + rank=same;
1.79 +
1.80 + Accessor_note [shape=note,style=filled,fillcolor=gold,label="Populates\nfile pages"];
1.81 + Accessor;
1.82 + Accessor_note -> Accessor [dir=none,style=dotted];
1.83 + }
1.84 +
1.85 + /* Opening a file. */
1.86 +
1.87 + Opener -> ResourceRegistry -> Resource;
1.88 + ResourceRegistry -> FileOpening;
1.89 +
1.90 + Provider -> PageMapper;
1.91 + FileOpening -> Accessor;
1.92 +
1.93 + /* Closing a file. */
1.94 +
1.95 + Resource -> Provider -> ProviderRegistry:head;
1.96 +
1.97 + /* Open file management. */
1.98 +
1.99 + ResourceRegistry -> ProviderRegistry:head;
1.100 +}
1.101 +}}}
1.102 +
1.103 +########
1.104 +
1.105 +An `Opener` requests a `Resource` from a `ResourceRegistry`, each `Resource`
1.106 +acting as a [[ServerLibrary#Pager|`Pager`]] and providing the actual access
1.107 +mechanism to file content for a particular program. Since many programs may
1.108 +access the same file simultaneously, a `Provider` object represents a file
1.109 +opened in the system, retaining a `PageMapper` that coordinates the collective
1.110 +access to the file (see the [[Paging|paging documentation]] for more
1.111 +information).
1.112 +
1.113 +A `ProviderRegistry` maintains the record of opened files, yielding a new
1.114 +`Provider` for an unopened file or an existing `Provider` for a file that is
1.115 +already open. The `ProviderRegistry` monitors the usage of files, discarding
1.116 +any `Provider` no longer in use.
1.117 +
1.118 +== Opening Files ==
1.119 +
1.120 +Opening a file involves the identification of the filesystem object involved,
1.121 +the acquisition of a `Provider` representing the file, and the creation of a
1.122 +`Resource` to deliver file content to the requesting program.
1.123 +
1.124 +######## A graph showing the opening mechanism for an already-open file
1.125 +
1.126 +{{{#!graphviz
1.127 +#format svg
1.128 +#transform notugly
1.129 +digraph opening_open {
1.130 + node [fontsize="12.0",fontname="sans-serif",shape=box];
1.131 + edge [fontsize="12.0",fontname="sans-serif"];
1.132 + rankdir=LR;
1.133 +
1.134 + subgraph {
1.135 + rank=same;
1.136 +
1.137 + Opener_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfile open\noperation"];
1.138 + Opener;
1.139 +
1.140 + Opener_note -> Opener [dir=none,style=dotted];
1.141 + }
1.142 +
1.143 + subgraph {
1.144 + rank=same;
1.145 +
1.146 + ResourceRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen file\nsessions"];
1.147 + ResourceRegistry;
1.148 +
1.149 + Resource_returned [label="Resource\n(Pager)"];
1.150 + Resource_returned_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"];
1.151 +
1.152 + ResourceRegistry_note -> ResourceRegistry [dir=none,style=dotted];
1.153 + Resource_returned -> Resource_returned_note [dir=none,style=dotted];
1.154 + }
1.155 +
1.156 + subgraph {
1.157 + rank=same;
1.158 +
1.159 + Resource [label="Resource\n(Pager)"];
1.160 +
1.161 + FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"];
1.162 + FileOpening;
1.163 +
1.164 + FileOpening_note -> FileOpening [dir=none,style=dotted];
1.165 + }
1.166 +
1.167 + subgraph {
1.168 + rank=max;
1.169 +
1.170 + ProviderRegistry_note [shape=note,style=filled,fillcolor=gold,label="Provides\nopen files"];
1.171 + ProviderRegistry;
1.172 +
1.173 + Provider;
1.174 + Provider_note [shape=note,style=filled,fillcolor=gold,label="Represents\nopen file"];
1.175 +
1.176 + ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted];
1.177 + Provider -> Provider_note [dir=none,style=dotted];
1.178 + }
1.179 +
1.180 + /* Obtaining a resource from the registry. */
1.181 +
1.182 + Opener -> ResourceRegistry [label="get_resource"];
1.183 + ResourceRegistry -> Resource_returned [dir=none];
1.184 + Resource_returned -> Opener;
1.185 +
1.186 + /* Obtaining a provider. */
1.187 +
1.188 + ResourceRegistry -> FileOpening [label="get_fileid"];
1.189 + ResourceRegistry -> ProviderRegistry [label="get(fileid)"];
1.190 + ProviderRegistry -> Provider;
1.191 +
1.192 + /* Obtaining the resource from the provider. */
1.193 +
1.194 + ResourceRegistry -> Provider [label="make_resource"];
1.195 + Provider -> Resource [dir=none];
1.196 + Resource -> ResourceRegistry;
1.197 +}
1.198 +}}}
1.199 +
1.200 +########
1.201 +
1.202 +Where a provider does not already exist, with the file not having been opened
1.203 +already, some extra interactions occur:
1.204 +
1.205 +######## A graph showing opening mechanism details for an unopened file
1.206 +
1.207 +{{{#!graphviz
1.208 +#format svg
1.209 +#transform notugly
1.210 +digraph opening_unopened {
1.211 + node [fontsize="12.0",fontname="sans-serif",shape=box];
1.212 + edge [fontsize="12.0",fontname="sans-serif"];
1.213 + rankdir=LR;
1.214 +
1.215 + Opener;
1.216 +
1.217 + ResourceRegistry;
1.218 +
1.219 + subgraph {
1.220 + rank=same;
1.221 +
1.222 + Provider_note [shape=note,style=filled,fillcolor=gold,label="Created\nand set in\nregistry"];
1.223 + Provider;
1.224 +
1.225 + Accessor;
1.226 + PageMapper;
1.227 + PageMapper_note [shape=note,style=filled,fillcolor=gold,label="Created\nfor provider"];
1.228 +
1.229 + Provider_note -> Provider [dir=none,style=dotted];
1.230 + PageMapper -> PageMapper_note [dir=none,style=dotted];
1.231 + }
1.232 +
1.233 + subgraph {
1.234 + rank=max;
1.235 +
1.236 + ProviderRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen files"];
1.237 + ProviderRegistry;
1.238 + FileOpening;
1.239 + FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"];
1.240 +
1.241 + ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted];
1.242 + FileOpening -> FileOpening_note [dir=none,style=dotted];
1.243 + }
1.244 +
1.245 + Opener -> ResourceRegistry [dir=none,style=dashed];
1.246 +
1.247 + /* Obtaining a new provider. */
1.248 +
1.249 + ResourceRegistry -> FileOpening [label="make_accessor"];
1.250 + FileOpening -> Accessor [dir=none];
1.251 + Accessor -> PageMapper -> ResourceRegistry;
1.252 +
1.253 + ResourceRegistry -> Provider [dir=none];
1.254 + Provider -> ProviderRegistry [label="set(fileid)"];
1.255 +}
1.256 +}}}
1.257 +
1.258 +########
1.259 +
1.260 +== Notifications ==
1.261 +
1.262 +Within the filesystem access architecture, users of files may act in ways that
1.263 +may notify other users of the same file. To support such notifications, an
1.264 +interface is provided for filesystem clients to subscribe and unsubscribe to
1.265 +notifications. The notifications themselves occur when files are opened,
1.266 +modified and closed. Pipes also generate notifications when a pipe reader
1.267 +makes more space available for the writer.
1.268 +
1.269 +######## A graph showing subscription to notifications
1.270 +
1.271 +{{{#!graphviz
1.272 +#format svg
1.273 +#transform notugly
1.274 +digraph notification_subscriptions {
1.275 + node [fontsize="12.0",fontname="sans-serif",shape=box];
1.276 + edge [fontsize="12.0",fontname="sans-serif"];
1.277 + rankdir=LR;
1.278 +
1.279 + subgraph {
1.280 + rank=same;
1.281 +
1.282 + Client1 [label="Client\nprogram"];
1.283 + Client2 [label="Client\nprogram"];
1.284 + }
1.285 +
1.286 + subgraph {
1.287 + rank=same;
1.288 +
1.289 + Notifier1_note [shape=note,style=filled,fillcolor=gold,label="Created for\nnotifications"];
1.290 + Notifier1 [label="Notifier"];
1.291 + Notifier2 [label="Notifier"];
1.292 +
1.293 + Notifier1_note -> Notifier1 -> Notifier2 [dir=none,style=dotted];
1.294 + }
1.295 +
1.296 + subgraph {
1.297 + rank=same;
1.298 +
1.299 + Resource1 [label="Resource\n(Pager)"];
1.300 + Resource2 [label="Resource\n(Pager)"];
1.301 + }
1.302 +
1.303 + subgraph {
1.304 + rank=same;
1.305 +
1.306 + Notifier1_subscribe_note [shape=note,style=filled,fillcolor=gold,label="Propagated to\nprovider"];
1.307 + Notifier1_subscribe [label="Notifier"];
1.308 + Notifier2_subscribe [label="Notifier"];
1.309 +
1.310 + Notifier1_subscribe_note -> Notifier1_subscribe -> Notifier2_subscribe [dir=none,style=dotted];
1.311 + }
1.312 +
1.313 + subgraph {
1.314 + rank=same;
1.315 +
1.316 + Provider_note [shape=note,style=filled,fillcolor=gold,label="Manages file\nsubscriptions"];
1.317 + Provider [label="Provider"];
1.318 +
1.319 + Provider_note -> Provider [dir=none,style=dotted];
1.320 + }
1.321 +
1.322 + /* Subscribing. */
1.323 +
1.324 + Client1 -> Notifier1 [dir=none];
1.325 + Notifier1 -> Resource1 [label="subscribe"];
1.326 +
1.327 + Resource1 -> Notifier1_subscribe [dir=none];
1.328 + Notifier1_subscribe -> Provider [label="subscribe"];
1.329 +
1.330 + Client2 -> Notifier2 [dir=none];
1.331 + Notifier2 -> Resource2 [label="subscribe"];
1.332 +
1.333 + Resource2 -> Notifier2_subscribe [dir=none];
1.334 + Notifier2_subscribe -> Provider [label="subscribe"];
1.335 +}
1.336 +}}}
1.337 +
1.338 +########
1.339 +
1.340 +An example of a notification scenario is given below:
1.341 +
1.342 +######## A graph showing notification
1.343 +
1.344 +{{{#!graphviz
1.345 +#format svg
1.346 +#transform notugly
1.347 +digraph notifications {
1.348 + node [fontsize="12.0",fontname="sans-serif",shape=box];
1.349 + edge [fontsize="12.0",fontname="sans-serif"];
1.350 + rankdir=LR;
1.351 +
1.352 + subgraph {
1.353 + rank=same;
1.354 +
1.355 + Client1 [label="Client\nprogram"];
1.356 + Client2 [label="Client\nprogram"];
1.357 + }
1.358 +
1.359 + subgraph {
1.360 + rank=same;
1.361 +
1.362 + Notifier1_note [shape=note,style=filled,fillcolor=gold,label="Registered for\nnotifications"];
1.363 + Notifier1 [label="Notifier"];
1.364 +
1.365 + Resource2 [label="Resource\n(Pager)"];
1.366 + Resource2_note [shape=note,style=filled,fillcolor=gold,label="Generates\nnotification"];
1.367 +
1.368 + Notifier1_note -> Notifier1 [dir=none,style=dotted];
1.369 + Resource2 -> Resource2_note [dir=none,style=dotted];
1.370 + }
1.371 +
1.372 + subgraph {
1.373 + rank=same;
1.374 +
1.375 + Provider_note [shape=note,style=filled,fillcolor=gold,label="Propagates\nnotifications"];
1.376 + Provider [label="Provider"];
1.377 +
1.378 + Provider_note -> Provider [dir=none,style=dotted];
1.379 + }
1.380 +
1.381 + /* A notification scenario. */
1.382 +
1.383 + Client2 -> Resource2 [label="resize"];
1.384 + Resource2 -> Provider [label="notify_others"];
1.385 + Provider -> Notifier1 [label="notify"];
1.386 + Client1 -> Notifier1 [label="wait"];
1.387 +}
1.388 +}}}
1.389 +
1.390 +########
1.391 +
1.392 +Notifications provide the basis for blocking reading and writing operations,
1.393 +with pipe endpoints depending on such blocking for efficient use of pipes. The
1.394 +interactions involved when transferring data through pipes are depicted below.
1.395 +
1.396 +######## A graph showing notifications employed by pipe endpoints
1.397 +
1.398 +{{{#!graphviz
1.399 +#format svg
1.400 +#transform notugly
1.401 +digraph pipe_notifications {
1.402 + node [fontsize="12.0",fontname="sans-serif",shape=box];
1.403 + edge [fontsize="12.0",fontname="sans-serif"];
1.404 + rankdir=LR;
1.405 +
1.406 + subgraph {
1.407 + rank=min;
1.408 +
1.409 + Client1_note [shape=note,style=filled,fillcolor=gold,label="Fill pipe\nthen request\nmore space"];
1.410 + Client1 [label="Client\nprogram\n(writer)"];
1.411 + Client1_wait_note [shape=note,style=filled,fillcolor=gold,label="Waiting after\nfilling pipe"];
1.412 + Client1_wait [label="Client\nprogram\n(writer)"];
1.413 +
1.414 + Client1_note -> Client1 [dir=none,style=dotted];
1.415 + Client1_wait_note -> Client1_wait [dir=none,style=dotted];
1.416 +
1.417 + Client1 -> Client1_wait_note [dir=none,style=invis];
1.418 + }
1.419 +
1.420 + subgraph {
1.421 + rank=max;
1.422 +
1.423 + Client2_note [shape=note,style=filled,fillcolor=gold,label="Waiting for\nmore content\nbefore reading"];
1.424 + Client2 [label="Client\nprogram\n(reader)"];
1.425 + Client2_read_note [shape=note,style=filled,fillcolor=gold,label="Empty pipe\nthen request\nmore content"];
1.426 + Client2_read [label="Client\nprogram\n(reader)"];
1.427 +
1.428 + Client2_note -> Client2 [dir=none,style=dotted];
1.429 + Client2_read_note -> Client2_read [dir=none,style=dotted];
1.430 + }
1.431 +
1.432 + subgraph {
1.433 + rank=same;
1.434 +
1.435 + Notifier1 [label="Notifier"];
1.436 + Resource1 [label="Resource\n(PipePager)"];
1.437 + }
1.438 +
1.439 + subgraph {
1.440 + rank=same;
1.441 +
1.442 + Notifier2 [label="Notifier"];
1.443 + Resource2 [label="Resource\n(PipePager)"];
1.444 + }
1.445 +
1.446 + subgraph {
1.447 + rank=same;
1.448 +
1.449 + PipePaging [label="Provider\n(PipePaging)"];
1.450 + PipePaging_space [label="Provider\n(PipePaging)"];
1.451 + }
1.452 +
1.453 + /* Making content available. */
1.454 +
1.455 + Client1 -> Resource1 [label="next_region"];
1.456 + Resource1 -> PipePaging [label="add_region"];
1.457 + PipePaging -> Notifier2 [label="notify"];
1.458 + Client2 -> Notifier2 [label="wait"];
1.459 +
1.460 + /* Making space available. */
1.461 +
1.462 + Client2_read -> Resource2 [label="next_region"];
1.463 + Resource2 -> PipePaging_space [label="next_region"];
1.464 + PipePaging_space -> Notifier1 [label="notify"];
1.465 + Client1_wait -> Notifier1 [label="wait"];
1.466 +}
1.467 +}}}
1.468 +
1.469 +########
1.470 +
1.471 +== Closing Files ==
1.472 +
1.473 +Files are closed when client programs release their references to the resource
1.474 +capabilities used to access files. The mechanism by which this is done
1.475 +involves the registration of an IRQ object that is bound to the same thread as
1.476 +the one used by resources to service file access requests. This IRQ object is
1.477 +associated with the IPC gate through which such requests occur, and the IRQ
1.478 +object is configured to deliver an interrupt message when the final reference
1.479 +to the IPC gate has been released.
1.480 +
1.481 +When a client program terminates, its references to capabilities should be
1.482 +released, and this should cause a reference counter associated with the IPC
1.483 +gate to decrement. Upon the loss of the final reference and the delivery of
1.484 +the interrupt, the server framework will call a `close` method on the
1.485 +`Resource` object concerned, this implementing the
1.486 +[[ServerLibrary#Accountable|`Accountable`]] interface.
1.487 +
1.488 +######## A graph showing the closing mechanism
1.489 +
1.490 +{{{#!graphviz
1.491 +#format svg
1.492 +#transform notugly
1.493 +digraph closing {
1.494 + node [fontsize="12.0",fontname="sans-serif",shape=box];
1.495 + edge [fontsize="12.0",fontname="sans-serif"];
1.496 + rankdir=LR;
1.497 +
1.498 + Client [label="Client\nprogram"];
1.499 +
1.500 + subgraph {
1.501 + rank=same;
1.502 +
1.503 + Gate_note [shape=note,style=filled,fillcolor=gold,label="Actual\nendpoint"];
1.504 + Gate [label="IPC gate",shape="octagon"];
1.505 + IRQ [shape="triangle"];
1.506 + ResourceServer;
1.507 + ResourceServer_note [shape=note,style=filled,fillcolor=gold,label="Handles\nrequests"];
1.508 +
1.509 + Gate_note -> Gate [dir=none,style=dotted];
1.510 + ResourceServer -> ResourceServer_note [dir=none,style=dotted];
1.511 + }
1.512 +
1.513 + Resource [label="Resource\n(Pager)"];
1.514 +
1.515 + Provider;
1.516 +
1.517 + ProviderRegistry;
1.518 +
1.519 + Client -> Gate [style=dashed,label="(release)"];
1.520 + Gate -> IRQ -> ResourceServer;
1.521 + ResourceServer -> Resource [label="close"];
1.522 + Resource -> Provider [label="notify_others\nunsubscribe"];
1.523 + Resource -> ProviderRegistry [label="detach"];
1.524 +}
1.525 +}}}
1.526 +
1.527 +########
1.528 +
1.529 +As a consequence of closing a file, an `unsubscribe` operation performed on
1.530 +the `Provider` should cause any `Notifier` objects associated with the
1.531 +`Resource` object to be released. Meanwhile, the `ResourceServer` will discard
1.532 +the `Resource` object before the server thread terminates.
1.533 +
1.534 +== Removing Files ==
1.535 +
1.536 +Unix filesystem semantics typically allow a file to be "unlinked" while still
1.537 +being accessed by a program, with accesses still being directed to the file,
1.538 +but with the file being removed from a directory and potentially being
1.539 +replaced by another file. To support this, a mechanism is provided to defer
1.540 +any removal of an open file.
1.541 +
1.542 +######## A graph showing the removal mechanism
1.543 +
1.544 +{{{#!graphviz
1.545 +#format svg
1.546 +#transform notugly
1.547 +digraph removing {
1.548 + node [fontsize="12.0",fontname="sans-serif",shape=box];
1.549 + edge [fontsize="12.0",fontname="sans-serif"];
1.550 + rankdir=LR;
1.551 +
1.552 + subgraph {
1.553 + rank=same;
1.554 +
1.555 + Client1_note [shape=note,style=filled,fillcolor=gold,label="Removing\nopen file"];
1.556 + Client1 [label="Client\nprogram"];
1.557 + Client2 [label="Client\nprogram"];
1.558 + Client2_note [shape=note,style=filled,fillcolor=gold,label="Closes\nopen file"];
1.559 +
1.560 + Client1_note -> Client1 [dir=none,style=dotted];
1.561 + Client2 -> Client2_note [dir=none,style=dotted];
1.562 +
1.563 + Client1 -> Client2 [dir=none,style=invis];
1.564 + }
1.565 +
1.566 + subgraph {
1.567 + rank=same;
1.568 +
1.569 + Resource [label="Resource\n(Pager)"];
1.570 + Opener;
1.571 + }
1.572 +
1.573 + subgraph {
1.574 + rank=same;
1.575 +
1.576 + Provider1 [label="Provider"];
1.577 + Provider1_note [shape=note,style=filled,fillcolor=gold,label="Maintains\nremoval\nstate"];
1.578 + Provider2 [label="Provider"];
1.579 +
1.580 + Provider1 -> Provider1_note -> Provider2 [dir=none,style=dotted];
1.581 + }
1.582 +
1.583 + ProviderRegistry1 [label="ProviderRegistry"];
1.584 + ProviderRegistry2 [label="ProviderRegistry"];
1.585 +
1.586 + subgraph {
1.587 + rank=same;
1.588 +
1.589 + FileOpening;
1.590 + Accessor_note [shape=note,style=filled,fillcolor=gold,label="Performing\nfilesystem\noperations"];
1.591 + Accessor;
1.592 +
1.593 + FileOpening -> Accessor_note -> Accessor [dir=none,style=dotted];
1.594 + }
1.595 +
1.596 + /* Removing the file while it remains open. */
1.597 +
1.598 + Client1 -> Opener [label="remove\n(via context)"];
1.599 + Opener -> ResourceRegistry [label="remove_provider"];
1.600 + ResourceRegistry -> FileOpening [label="get_fileid\nunlink_object"];
1.601 + ResourceRegistry -> ProviderRegistry1 [label="get(fileid)"];
1.602 + ResourceRegistry -> Provider1 [label="remove_pending"];
1.603 +
1.604 + /* File closure and deferred removal. */
1.605 +
1.606 + Client2 -> Resource [label="close"];
1.607 + Resource -> Provider2 [label="notify_others\nunsubscribe"];
1.608 + Resource -> ProviderRegistry2 [label="detach"];
1.609 + ProviderRegistry2 -> Provider2 [label="remove"];
1.610 + Provider2 -> Accessor [label="remove"];
1.611 +}
1.612 +}}}
1.613 +
1.614 +########