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