paul@359 | 1 | = Filesystem Access = |
paul@233 | 2 | |
paul@384 | 3 | Within the [[ServerLibrary|filesystem server library]], a number of different |
paul@384 | 4 | abstractions and mechanisms are employed to provide access to filesystem |
paul@384 | 5 | objects. An overview of these abstractions is presented below. |
paul@233 | 6 | |
paul@384 | 7 | ######## A graph showing the relationships between filesystem access |
paul@384 | 8 | ######## components |
paul@233 | 9 | |
paul@233 | 10 | {{{#!graphviz |
paul@233 | 11 | #format svg |
paul@233 | 12 | #transform notugly |
paul@233 | 13 | digraph components { |
paul@233 | 14 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@233 | 15 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@233 | 16 | rankdir=LR; |
paul@233 | 17 | |
paul@233 | 18 | subgraph { |
paul@233 | 19 | rank=same; |
paul@233 | 20 | |
paul@384 | 21 | Opener_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfile open\noperation"]; |
paul@384 | 22 | Opener; |
paul@384 | 23 | |
paul@384 | 24 | Opener_note -> Opener [dir=none,style=dotted]; |
paul@233 | 25 | } |
paul@233 | 26 | |
paul@233 | 27 | subgraph { |
paul@233 | 28 | rank=same; |
paul@233 | 29 | |
paul@384 | 30 | ResourceRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen file\nsessions"]; |
paul@384 | 31 | ResourceRegistry; |
paul@384 | 32 | |
paul@384 | 33 | ResourceRegistry_note -> ResourceRegistry [dir=none,style=dotted]; |
paul@233 | 34 | } |
paul@233 | 35 | |
paul@233 | 36 | subgraph { |
paul@233 | 37 | rank=same; |
paul@233 | 38 | |
paul@384 | 39 | Resource_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"]; |
paul@384 | 40 | Resource [label="Resource\n(Pager)"]; |
paul@384 | 41 | |
paul@384 | 42 | Resource_note -> Resource [dir=none,style=dotted]; |
paul@384 | 43 | } |
paul@384 | 44 | |
paul@384 | 45 | subgraph { |
paul@384 | 46 | rank=same; |
paul@384 | 47 | |
paul@384 | 48 | Provider_note [shape=note,style=filled,fillcolor=gold,label="Represents\nopen file"]; |
paul@384 | 49 | Provider; |
paul@384 | 50 | |
paul@384 | 51 | Provider_note -> Provider [dir=none,style=dotted]; |
paul@233 | 52 | } |
paul@233 | 53 | |
paul@233 | 54 | subgraph { |
paul@233 | 55 | rank=same; |
paul@233 | 56 | |
paul@384 | 57 | PageMapper_note [shape=note,style=filled,fillcolor=gold,label="Provides\npopulated\nfile pages"]; |
paul@384 | 58 | PageMapper; |
paul@384 | 59 | |
paul@384 | 60 | PageMapper_note -> PageMapper [dir=none,style=dotted]; |
paul@384 | 61 | } |
paul@384 | 62 | |
paul@384 | 63 | ProviderRegistry [shape=record,label="<head> ProviderRegistry | ... | { file-n | provider-n } | ... "]; |
paul@384 | 64 | |
paul@384 | 65 | subgraph { |
paul@384 | 66 | rank=same; |
paul@384 | 67 | |
paul@384 | 68 | FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; |
paul@384 | 69 | FileOpening; |
paul@384 | 70 | |
paul@384 | 71 | FileOpening_note -> FileOpening [dir=none,style=dotted]; |
paul@233 | 72 | } |
paul@233 | 73 | |
paul@233 | 74 | subgraph { |
paul@233 | 75 | rank=same; |
paul@233 | 76 | |
paul@384 | 77 | Accessor_note [shape=note,style=filled,fillcolor=gold,label="Populates\nfile pages"]; |
paul@384 | 78 | Accessor; |
paul@384 | 79 | Accessor_note -> Accessor [dir=none,style=dotted]; |
paul@233 | 80 | } |
paul@233 | 81 | |
paul@384 | 82 | /* Opening a file. */ |
paul@233 | 83 | |
paul@384 | 84 | Opener -> ResourceRegistry -> Resource; |
paul@384 | 85 | ResourceRegistry -> FileOpening; |
paul@233 | 86 | |
paul@384 | 87 | Provider -> PageMapper; |
paul@384 | 88 | FileOpening -> Accessor; |
paul@233 | 89 | |
paul@384 | 90 | /* Closing a file. */ |
paul@233 | 91 | |
paul@384 | 92 | Resource -> Provider -> ProviderRegistry:head; |
paul@233 | 93 | |
paul@384 | 94 | /* Open file management. */ |
paul@305 | 95 | |
paul@384 | 96 | ResourceRegistry -> ProviderRegistry:head; |
paul@305 | 97 | } |
paul@305 | 98 | }}} |
paul@305 | 99 | |
paul@305 | 100 | ######## |
paul@233 | 101 | |
paul@384 | 102 | An `Opener` requests a `Resource` from a `ResourceRegistry`, each `Resource` |
paul@384 | 103 | acting as a [[ServerLibrary#Pager|`Pager`]] and providing the actual access |
paul@384 | 104 | mechanism to file content for a particular program. Since many programs may |
paul@384 | 105 | access the same file simultaneously, a `Provider` object represents a file |
paul@384 | 106 | opened in the system, retaining a `PageMapper` that coordinates the collective |
paul@384 | 107 | access to the file (see the [[Paging|paging documentation]] for more |
paul@384 | 108 | information). |
paul@233 | 109 | |
paul@384 | 110 | A `ProviderRegistry` maintains the record of opened files, yielding a new |
paul@384 | 111 | `Provider` for an unopened file or an existing `Provider` for a file that is |
paul@384 | 112 | already open. The `ProviderRegistry` monitors the usage of files, discarding |
paul@384 | 113 | any `Provider` no longer in use. |
paul@233 | 114 | |
paul@389 | 115 | == Opening Files == |
paul@304 | 116 | |
paul@384 | 117 | Opening a file involves the identification of the filesystem object involved, |
paul@384 | 118 | the acquisition of a `Provider` representing the file, and the creation of a |
paul@384 | 119 | `Resource` to deliver file content to the requesting program. |
paul@304 | 120 | |
paul@384 | 121 | ######## A graph showing the opening mechanism for an already-open file |
paul@233 | 122 | |
paul@233 | 123 | {{{#!graphviz |
paul@233 | 124 | #format svg |
paul@233 | 125 | #transform notugly |
paul@384 | 126 | digraph opening_open { |
paul@233 | 127 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@233 | 128 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@233 | 129 | rankdir=LR; |
paul@233 | 130 | |
paul@233 | 131 | subgraph { |
paul@233 | 132 | rank=same; |
paul@233 | 133 | |
paul@384 | 134 | Opener_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfile open\noperation"]; |
paul@384 | 135 | Opener; |
paul@384 | 136 | |
paul@384 | 137 | Opener_note -> Opener [dir=none,style=dotted]; |
paul@233 | 138 | } |
paul@233 | 139 | |
paul@233 | 140 | subgraph { |
paul@233 | 141 | rank=same; |
paul@233 | 142 | |
paul@384 | 143 | ResourceRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen file\nsessions"]; |
paul@384 | 144 | ResourceRegistry; |
paul@384 | 145 | |
paul@384 | 146 | Resource_returned [label="Resource\n(Pager)"]; |
paul@384 | 147 | Resource_returned_note [shape=note,style=filled,fillcolor=gold,label="Provides\nfilesystem\ncontent"]; |
paul@384 | 148 | |
paul@384 | 149 | ResourceRegistry_note -> ResourceRegistry [dir=none,style=dotted]; |
paul@384 | 150 | Resource_returned -> Resource_returned_note [dir=none,style=dotted]; |
paul@233 | 151 | } |
paul@233 | 152 | |
paul@233 | 153 | subgraph { |
paul@233 | 154 | rank=same; |
paul@233 | 155 | |
paul@384 | 156 | Resource [label="Resource\n(Pager)"]; |
paul@384 | 157 | |
paul@384 | 158 | FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; |
paul@384 | 159 | FileOpening; |
paul@384 | 160 | |
paul@384 | 161 | FileOpening_note -> FileOpening [dir=none,style=dotted]; |
paul@233 | 162 | } |
paul@233 | 163 | |
paul@233 | 164 | subgraph { |
paul@384 | 165 | rank=max; |
paul@384 | 166 | |
paul@384 | 167 | ProviderRegistry_note [shape=note,style=filled,fillcolor=gold,label="Provides\nopen files"]; |
paul@384 | 168 | ProviderRegistry; |
paul@233 | 169 | |
paul@384 | 170 | Provider; |
paul@384 | 171 | Provider_note [shape=note,style=filled,fillcolor=gold,label="Represents\nopen file"]; |
paul@384 | 172 | |
paul@384 | 173 | ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted]; |
paul@384 | 174 | Provider -> Provider_note [dir=none,style=dotted]; |
paul@233 | 175 | } |
paul@233 | 176 | |
paul@384 | 177 | /* Obtaining a resource from the registry. */ |
paul@384 | 178 | |
paul@384 | 179 | Opener -> ResourceRegistry [label="get_resource"]; |
paul@384 | 180 | ResourceRegistry -> Resource_returned [dir=none]; |
paul@384 | 181 | Resource_returned -> Opener; |
paul@384 | 182 | |
paul@384 | 183 | /* Obtaining a provider. */ |
paul@233 | 184 | |
paul@384 | 185 | ResourceRegistry -> FileOpening [label="get_fileid"]; |
paul@384 | 186 | ResourceRegistry -> ProviderRegistry [label="get(fileid)"]; |
paul@384 | 187 | ProviderRegistry -> Provider; |
paul@384 | 188 | |
paul@384 | 189 | /* Obtaining the resource from the provider. */ |
paul@384 | 190 | |
paul@384 | 191 | ResourceRegistry -> Provider [label="make_resource"]; |
paul@384 | 192 | Provider -> Resource [dir=none]; |
paul@384 | 193 | Resource -> ResourceRegistry; |
paul@233 | 194 | } |
paul@233 | 195 | }}} |
paul@233 | 196 | |
paul@233 | 197 | ######## |
paul@303 | 198 | |
paul@384 | 199 | Where a provider does not already exist, with the file not having been opened |
paul@384 | 200 | already, some extra interactions occur: |
paul@303 | 201 | |
paul@384 | 202 | ######## A graph showing opening mechanism details for an unopened file |
paul@303 | 203 | |
paul@303 | 204 | {{{#!graphviz |
paul@303 | 205 | #format svg |
paul@303 | 206 | #transform notugly |
paul@384 | 207 | digraph opening_unopened { |
paul@303 | 208 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@303 | 209 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@303 | 210 | rankdir=LR; |
paul@303 | 211 | |
paul@384 | 212 | Opener; |
paul@384 | 213 | |
paul@389 | 214 | ResourceRegistry; |
paul@384 | 215 | |
paul@384 | 216 | subgraph { |
paul@384 | 217 | rank=same; |
paul@384 | 218 | |
paul@384 | 219 | Provider_note [shape=note,style=filled,fillcolor=gold,label="Created\nand set in\nregistry"]; |
paul@389 | 220 | Provider; |
paul@384 | 221 | |
paul@389 | 222 | Accessor; |
paul@389 | 223 | PageMapper; |
paul@389 | 224 | PageMapper_note [shape=note,style=filled,fillcolor=gold,label="Created\nfor provider"]; |
paul@384 | 225 | |
paul@384 | 226 | Provider_note -> Provider [dir=none,style=dotted]; |
paul@389 | 227 | PageMapper -> PageMapper_note [dir=none,style=dotted]; |
paul@384 | 228 | } |
paul@384 | 229 | |
paul@384 | 230 | subgraph { |
paul@384 | 231 | rank=max; |
paul@384 | 232 | |
paul@384 | 233 | ProviderRegistry_note [shape=note,style=filled,fillcolor=gold,label="Records\nopen files"]; |
paul@389 | 234 | ProviderRegistry; |
paul@389 | 235 | FileOpening; |
paul@384 | 236 | FileOpening_note [shape=note,style=filled,fillcolor=gold,label="Exposes\nfilesystem\nfunctionality"]; |
paul@384 | 237 | |
paul@384 | 238 | ProviderRegistry_note -> ProviderRegistry [dir=none,style=dotted]; |
paul@384 | 239 | FileOpening -> FileOpening_note [dir=none,style=dotted]; |
paul@384 | 240 | } |
paul@384 | 241 | |
paul@384 | 242 | Opener -> ResourceRegistry [dir=none,style=dashed]; |
paul@384 | 243 | |
paul@384 | 244 | /* Obtaining a new provider. */ |
paul@384 | 245 | |
paul@384 | 246 | ResourceRegistry -> FileOpening [label="make_accessor"]; |
paul@384 | 247 | FileOpening -> Accessor [dir=none]; |
paul@389 | 248 | Accessor -> PageMapper -> ResourceRegistry; |
paul@384 | 249 | |
paul@384 | 250 | ResourceRegistry -> Provider [dir=none]; |
paul@384 | 251 | Provider -> ProviderRegistry [label="set(fileid)"]; |
paul@303 | 252 | } |
paul@303 | 253 | }}} |
paul@303 | 254 | |
paul@303 | 255 | ######## |
paul@389 | 256 | |
paul@389 | 257 | == Notifications == |
paul@389 | 258 | |
paul@389 | 259 | Within the filesystem access architecture, users of files may act in ways that |
paul@389 | 260 | may notify other users of the same file. To support such notifications, an |
paul@389 | 261 | interface is provided for filesystem clients to subscribe and unsubscribe to |
paul@389 | 262 | notifications. The notifications themselves occur when files are opened, |
paul@389 | 263 | modified and closed. Pipes also generate notifications when a pipe reader |
paul@389 | 264 | makes more space available for the writer. |
paul@389 | 265 | |
paul@389 | 266 | ######## A graph showing subscription to notifications |
paul@389 | 267 | |
paul@389 | 268 | {{{#!graphviz |
paul@389 | 269 | #format svg |
paul@389 | 270 | #transform notugly |
paul@389 | 271 | digraph notification_subscriptions { |
paul@389 | 272 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@389 | 273 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@389 | 274 | rankdir=LR; |
paul@389 | 275 | |
paul@389 | 276 | subgraph { |
paul@389 | 277 | rank=same; |
paul@389 | 278 | |
paul@389 | 279 | Client1 [label="Client\nprogram"]; |
paul@389 | 280 | Client2 [label="Client\nprogram"]; |
paul@389 | 281 | } |
paul@389 | 282 | |
paul@389 | 283 | subgraph { |
paul@389 | 284 | rank=same; |
paul@389 | 285 | |
paul@389 | 286 | Notifier1_note [shape=note,style=filled,fillcolor=gold,label="Created for\nnotifications"]; |
paul@389 | 287 | Notifier1 [label="Notifier"]; |
paul@389 | 288 | Notifier2 [label="Notifier"]; |
paul@389 | 289 | |
paul@389 | 290 | Notifier1_note -> Notifier1 -> Notifier2 [dir=none,style=dotted]; |
paul@389 | 291 | } |
paul@389 | 292 | |
paul@389 | 293 | subgraph { |
paul@389 | 294 | rank=same; |
paul@389 | 295 | |
paul@389 | 296 | Resource1 [label="Resource\n(Pager)"]; |
paul@389 | 297 | Resource2 [label="Resource\n(Pager)"]; |
paul@389 | 298 | } |
paul@389 | 299 | |
paul@389 | 300 | subgraph { |
paul@389 | 301 | rank=same; |
paul@389 | 302 | |
paul@389 | 303 | Notifier1_subscribe_note [shape=note,style=filled,fillcolor=gold,label="Propagated to\nprovider"]; |
paul@389 | 304 | Notifier1_subscribe [label="Notifier"]; |
paul@389 | 305 | Notifier2_subscribe [label="Notifier"]; |
paul@389 | 306 | |
paul@389 | 307 | Notifier1_subscribe_note -> Notifier1_subscribe -> Notifier2_subscribe [dir=none,style=dotted]; |
paul@389 | 308 | } |
paul@389 | 309 | |
paul@389 | 310 | subgraph { |
paul@389 | 311 | rank=same; |
paul@389 | 312 | |
paul@389 | 313 | Provider_note [shape=note,style=filled,fillcolor=gold,label="Manages file\nsubscriptions"]; |
paul@389 | 314 | Provider [label="Provider"]; |
paul@389 | 315 | |
paul@389 | 316 | Provider_note -> Provider [dir=none,style=dotted]; |
paul@389 | 317 | } |
paul@389 | 318 | |
paul@389 | 319 | /* Subscribing. */ |
paul@389 | 320 | |
paul@389 | 321 | Client1 -> Notifier1 [dir=none]; |
paul@389 | 322 | Notifier1 -> Resource1 [label="subscribe"]; |
paul@389 | 323 | |
paul@389 | 324 | Resource1 -> Notifier1_subscribe [dir=none]; |
paul@389 | 325 | Notifier1_subscribe -> Provider [label="subscribe"]; |
paul@389 | 326 | |
paul@389 | 327 | Client2 -> Notifier2 [dir=none]; |
paul@389 | 328 | Notifier2 -> Resource2 [label="subscribe"]; |
paul@389 | 329 | |
paul@389 | 330 | Resource2 -> Notifier2_subscribe [dir=none]; |
paul@389 | 331 | Notifier2_subscribe -> Provider [label="subscribe"]; |
paul@389 | 332 | } |
paul@389 | 333 | }}} |
paul@389 | 334 | |
paul@389 | 335 | ######## |
paul@389 | 336 | |
paul@389 | 337 | An example of a notification scenario is given below: |
paul@389 | 338 | |
paul@389 | 339 | ######## A graph showing notification |
paul@389 | 340 | |
paul@389 | 341 | {{{#!graphviz |
paul@389 | 342 | #format svg |
paul@389 | 343 | #transform notugly |
paul@389 | 344 | digraph notifications { |
paul@389 | 345 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@389 | 346 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@389 | 347 | rankdir=LR; |
paul@389 | 348 | |
paul@389 | 349 | subgraph { |
paul@389 | 350 | rank=same; |
paul@389 | 351 | |
paul@389 | 352 | Client1 [label="Client\nprogram"]; |
paul@389 | 353 | Client2 [label="Client\nprogram"]; |
paul@389 | 354 | } |
paul@389 | 355 | |
paul@389 | 356 | subgraph { |
paul@389 | 357 | rank=same; |
paul@389 | 358 | |
paul@389 | 359 | Notifier1_note [shape=note,style=filled,fillcolor=gold,label="Registered for\nnotifications"]; |
paul@389 | 360 | Notifier1 [label="Notifier"]; |
paul@389 | 361 | |
paul@389 | 362 | Resource2 [label="Resource\n(Pager)"]; |
paul@389 | 363 | Resource2_note [shape=note,style=filled,fillcolor=gold,label="Generates\nnotification"]; |
paul@389 | 364 | |
paul@389 | 365 | Notifier1_note -> Notifier1 [dir=none,style=dotted]; |
paul@389 | 366 | Resource2 -> Resource2_note [dir=none,style=dotted]; |
paul@389 | 367 | } |
paul@389 | 368 | |
paul@389 | 369 | subgraph { |
paul@389 | 370 | rank=same; |
paul@389 | 371 | |
paul@389 | 372 | Provider_note [shape=note,style=filled,fillcolor=gold,label="Propagates\nnotifications"]; |
paul@389 | 373 | Provider [label="Provider"]; |
paul@389 | 374 | |
paul@389 | 375 | Provider_note -> Provider [dir=none,style=dotted]; |
paul@389 | 376 | } |
paul@389 | 377 | |
paul@389 | 378 | /* A notification scenario. */ |
paul@389 | 379 | |
paul@389 | 380 | Client2 -> Resource2 [label="resize"]; |
paul@389 | 381 | Resource2 -> Provider [label="notify_others"]; |
paul@389 | 382 | Provider -> Notifier1 [label="notify"]; |
paul@389 | 383 | Client1 -> Notifier1 [label="wait"]; |
paul@389 | 384 | } |
paul@389 | 385 | }}} |
paul@389 | 386 | |
paul@389 | 387 | ######## |
paul@389 | 388 | |
paul@389 | 389 | Notifications provide the basis for blocking reading and writing operations, |
paul@389 | 390 | with pipe endpoints depending on such blocking for efficient use of pipes. The |
paul@389 | 391 | interactions involved when transferring data through pipes are depicted below. |
paul@389 | 392 | |
paul@389 | 393 | ######## A graph showing notifications employed by pipe endpoints |
paul@389 | 394 | |
paul@389 | 395 | {{{#!graphviz |
paul@389 | 396 | #format svg |
paul@389 | 397 | #transform notugly |
paul@389 | 398 | digraph pipe_notifications { |
paul@389 | 399 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@389 | 400 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@389 | 401 | rankdir=LR; |
paul@389 | 402 | |
paul@389 | 403 | subgraph { |
paul@389 | 404 | rank=min; |
paul@389 | 405 | |
paul@389 | 406 | Client1_note [shape=note,style=filled,fillcolor=gold,label="Fill pipe\nthen request\nmore space"]; |
paul@389 | 407 | Client1 [label="Client\nprogram\n(writer)"]; |
paul@389 | 408 | Client1_wait_note [shape=note,style=filled,fillcolor=gold,label="Waiting after\nfilling pipe"]; |
paul@389 | 409 | Client1_wait [label="Client\nprogram\n(writer)"]; |
paul@389 | 410 | |
paul@389 | 411 | Client1_note -> Client1 [dir=none,style=dotted]; |
paul@389 | 412 | Client1_wait_note -> Client1_wait [dir=none,style=dotted]; |
paul@389 | 413 | |
paul@389 | 414 | Client1 -> Client1_wait_note [dir=none,style=invis]; |
paul@389 | 415 | } |
paul@389 | 416 | |
paul@389 | 417 | subgraph { |
paul@389 | 418 | rank=max; |
paul@389 | 419 | |
paul@389 | 420 | Client2_note [shape=note,style=filled,fillcolor=gold,label="Waiting for\nmore content\nbefore reading"]; |
paul@389 | 421 | Client2 [label="Client\nprogram\n(reader)"]; |
paul@389 | 422 | Client2_read_note [shape=note,style=filled,fillcolor=gold,label="Empty pipe\nthen request\nmore content"]; |
paul@389 | 423 | Client2_read [label="Client\nprogram\n(reader)"]; |
paul@389 | 424 | |
paul@389 | 425 | Client2_note -> Client2 [dir=none,style=dotted]; |
paul@389 | 426 | Client2_read_note -> Client2_read [dir=none,style=dotted]; |
paul@389 | 427 | } |
paul@389 | 428 | |
paul@389 | 429 | subgraph { |
paul@389 | 430 | rank=same; |
paul@389 | 431 | |
paul@389 | 432 | Notifier1 [label="Notifier"]; |
paul@389 | 433 | Resource1 [label="Resource\n(PipePager)"]; |
paul@389 | 434 | } |
paul@389 | 435 | |
paul@389 | 436 | subgraph { |
paul@389 | 437 | rank=same; |
paul@389 | 438 | |
paul@389 | 439 | Notifier2 [label="Notifier"]; |
paul@389 | 440 | Resource2 [label="Resource\n(PipePager)"]; |
paul@389 | 441 | } |
paul@389 | 442 | |
paul@389 | 443 | subgraph { |
paul@389 | 444 | rank=same; |
paul@389 | 445 | |
paul@389 | 446 | PipePaging [label="Provider\n(PipePaging)"]; |
paul@389 | 447 | PipePaging_space [label="Provider\n(PipePaging)"]; |
paul@389 | 448 | } |
paul@389 | 449 | |
paul@389 | 450 | /* Making content available. */ |
paul@389 | 451 | |
paul@389 | 452 | Client1 -> Resource1 [label="next_region"]; |
paul@389 | 453 | Resource1 -> PipePaging [label="add_region"]; |
paul@389 | 454 | PipePaging -> Notifier2 [label="notify"]; |
paul@389 | 455 | Client2 -> Notifier2 [label="wait"]; |
paul@389 | 456 | |
paul@389 | 457 | /* Making space available. */ |
paul@389 | 458 | |
paul@389 | 459 | Client2_read -> Resource2 [label="next_region"]; |
paul@389 | 460 | Resource2 -> PipePaging_space [label="next_region"]; |
paul@389 | 461 | PipePaging_space -> Notifier1 [label="notify"]; |
paul@389 | 462 | Client1_wait -> Notifier1 [label="wait"]; |
paul@389 | 463 | } |
paul@389 | 464 | }}} |
paul@389 | 465 | |
paul@389 | 466 | ######## |
paul@389 | 467 | |
paul@389 | 468 | == Closing Files == |
paul@389 | 469 | |
paul@389 | 470 | Files are closed when client programs release their references to the resource |
paul@389 | 471 | capabilities used to access files. The mechanism by which this is done |
paul@389 | 472 | involves the registration of an IRQ object that is bound to the same thread as |
paul@389 | 473 | the one used by resources to service file access requests. This IRQ object is |
paul@389 | 474 | associated with the IPC gate through which such requests occur, and the IRQ |
paul@389 | 475 | object is configured to deliver an interrupt message when the final reference |
paul@389 | 476 | to the IPC gate has been released. |
paul@389 | 477 | |
paul@389 | 478 | When a client program terminates, its references to capabilities should be |
paul@389 | 479 | released, and this should cause a reference counter associated with the IPC |
paul@389 | 480 | gate to decrement. Upon the loss of the final reference and the delivery of |
paul@389 | 481 | the interrupt, the server framework will call a `close` method on the |
paul@389 | 482 | `Resource` object concerned, this implementing the |
paul@389 | 483 | [[ServerLibrary#Accountable|`Accountable`]] interface. |
paul@389 | 484 | |
paul@389 | 485 | ######## A graph showing the closing mechanism |
paul@389 | 486 | |
paul@389 | 487 | {{{#!graphviz |
paul@389 | 488 | #format svg |
paul@389 | 489 | #transform notugly |
paul@389 | 490 | digraph closing { |
paul@389 | 491 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@389 | 492 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@389 | 493 | rankdir=LR; |
paul@389 | 494 | |
paul@389 | 495 | Client [label="Client\nprogram"]; |
paul@389 | 496 | |
paul@389 | 497 | subgraph { |
paul@389 | 498 | rank=same; |
paul@389 | 499 | |
paul@389 | 500 | Gate_note [shape=note,style=filled,fillcolor=gold,label="Actual\nendpoint"]; |
paul@389 | 501 | Gate [label="IPC gate",shape="octagon"]; |
paul@389 | 502 | IRQ [shape="triangle"]; |
paul@389 | 503 | ResourceServer; |
paul@389 | 504 | ResourceServer_note [shape=note,style=filled,fillcolor=gold,label="Handles\nrequests"]; |
paul@389 | 505 | |
paul@389 | 506 | Gate_note -> Gate [dir=none,style=dotted]; |
paul@389 | 507 | ResourceServer -> ResourceServer_note [dir=none,style=dotted]; |
paul@389 | 508 | } |
paul@389 | 509 | |
paul@389 | 510 | Resource [label="Resource\n(Pager)"]; |
paul@389 | 511 | |
paul@389 | 512 | Provider; |
paul@389 | 513 | |
paul@389 | 514 | ProviderRegistry; |
paul@389 | 515 | |
paul@389 | 516 | Client -> Gate [style=dashed,label="(release)"]; |
paul@389 | 517 | Gate -> IRQ -> ResourceServer; |
paul@389 | 518 | ResourceServer -> Resource [label="close"]; |
paul@389 | 519 | Resource -> Provider [label="notify_others\nunsubscribe"]; |
paul@389 | 520 | Resource -> ProviderRegistry [label="detach"]; |
paul@389 | 521 | } |
paul@389 | 522 | }}} |
paul@389 | 523 | |
paul@389 | 524 | ######## |
paul@389 | 525 | |
paul@389 | 526 | As a consequence of closing a file, an `unsubscribe` operation performed on |
paul@389 | 527 | the `Provider` should cause any `Notifier` objects associated with the |
paul@389 | 528 | `Resource` object to be released. Meanwhile, the `ResourceServer` will discard |
paul@389 | 529 | the `Resource` object before the server thread terminates. |
paul@389 | 530 | |
paul@389 | 531 | == Removing Files == |
paul@389 | 532 | |
paul@389 | 533 | Unix filesystem semantics typically allow a file to be "unlinked" while still |
paul@389 | 534 | being accessed by a program, with accesses still being directed to the file, |
paul@389 | 535 | but with the file being removed from a directory and potentially being |
paul@389 | 536 | replaced by another file. To support this, a mechanism is provided to defer |
paul@389 | 537 | any removal of an open file. |
paul@389 | 538 | |
paul@389 | 539 | ######## A graph showing the removal mechanism |
paul@389 | 540 | |
paul@389 | 541 | {{{#!graphviz |
paul@389 | 542 | #format svg |
paul@389 | 543 | #transform notugly |
paul@389 | 544 | digraph removing { |
paul@389 | 545 | node [fontsize="12.0",fontname="sans-serif",shape=box]; |
paul@389 | 546 | edge [fontsize="12.0",fontname="sans-serif"]; |
paul@389 | 547 | rankdir=LR; |
paul@389 | 548 | |
paul@389 | 549 | subgraph { |
paul@389 | 550 | rank=same; |
paul@389 | 551 | |
paul@389 | 552 | Client1_note [shape=note,style=filled,fillcolor=gold,label="Removing\nopen file"]; |
paul@389 | 553 | Client1 [label="Client\nprogram"]; |
paul@389 | 554 | Client2 [label="Client\nprogram"]; |
paul@389 | 555 | Client2_note [shape=note,style=filled,fillcolor=gold,label="Closes\nopen file"]; |
paul@389 | 556 | |
paul@389 | 557 | Client1_note -> Client1 [dir=none,style=dotted]; |
paul@389 | 558 | Client2 -> Client2_note [dir=none,style=dotted]; |
paul@389 | 559 | |
paul@389 | 560 | Client1 -> Client2 [dir=none,style=invis]; |
paul@389 | 561 | } |
paul@389 | 562 | |
paul@389 | 563 | subgraph { |
paul@389 | 564 | rank=same; |
paul@389 | 565 | |
paul@389 | 566 | Resource [label="Resource\n(Pager)"]; |
paul@389 | 567 | Opener; |
paul@389 | 568 | } |
paul@389 | 569 | |
paul@389 | 570 | subgraph { |
paul@389 | 571 | rank=same; |
paul@389 | 572 | |
paul@389 | 573 | Provider1 [label="Provider"]; |
paul@389 | 574 | Provider1_note [shape=note,style=filled,fillcolor=gold,label="Maintains\nremoval\nstate"]; |
paul@389 | 575 | Provider2 [label="Provider"]; |
paul@389 | 576 | |
paul@389 | 577 | Provider1 -> Provider1_note -> Provider2 [dir=none,style=dotted]; |
paul@389 | 578 | } |
paul@389 | 579 | |
paul@389 | 580 | ProviderRegistry1 [label="ProviderRegistry"]; |
paul@389 | 581 | ProviderRegistry2 [label="ProviderRegistry"]; |
paul@389 | 582 | |
paul@389 | 583 | subgraph { |
paul@389 | 584 | rank=same; |
paul@389 | 585 | |
paul@389 | 586 | FileOpening; |
paul@389 | 587 | Accessor_note [shape=note,style=filled,fillcolor=gold,label="Performing\nfilesystem\noperations"]; |
paul@389 | 588 | Accessor; |
paul@389 | 589 | |
paul@389 | 590 | FileOpening -> Accessor_note -> Accessor [dir=none,style=dotted]; |
paul@389 | 591 | } |
paul@389 | 592 | |
paul@389 | 593 | /* Removing the file while it remains open. */ |
paul@389 | 594 | |
paul@389 | 595 | Client1 -> Opener [label="remove\n(via context)"]; |
paul@389 | 596 | Opener -> ResourceRegistry [label="remove_provider"]; |
paul@389 | 597 | ResourceRegistry -> FileOpening [label="get_fileid\nunlink_object"]; |
paul@389 | 598 | ResourceRegistry -> ProviderRegistry1 [label="get(fileid)"]; |
paul@389 | 599 | ResourceRegistry -> Provider1 [label="remove_pending"]; |
paul@389 | 600 | |
paul@389 | 601 | /* File closure and deferred removal. */ |
paul@389 | 602 | |
paul@389 | 603 | Client2 -> Resource [label="close"]; |
paul@389 | 604 | Resource -> Provider2 [label="notify_others\nunsubscribe"]; |
paul@389 | 605 | Resource -> ProviderRegistry2 [label="detach"]; |
paul@389 | 606 | ProviderRegistry2 -> Provider2 [label="remove"]; |
paul@389 | 607 | Provider2 -> Accessor [label="remove"]; |
paul@389 | 608 | } |
paul@389 | 609 | }}} |
paul@389 | 610 | |
paul@389 | 611 | ######## |