diff --git a/main/pub/fab/site/styles.css b/main/pub/fab/site/styles.css index dedf19c5e..c02e5f460 100644 --- a/main/pub/fab/site/styles.css +++ b/main/pub/fab/site/styles.css @@ -139,7 +139,7 @@ pre, .codeblock { margin-top: 0; margin-bottom: 1rem; padding: 1rem; - font-size: .9rem; + font-size: .8rem; line-height: 1.4; white-space: pre; white-space: pre-wrap; diff --git a/main/pub/src/doc/ref/vol/4c.md b/main/pub/src/doc/ref/vol/4c.md index 97d08ab7e..8dc50518e 100644 --- a/main/pub/src/doc/ref/vol/4c.md +++ b/main/pub/src/doc/ref/vol/4c.md @@ -157,7 +157,7 @@ not a `%da` for now. `%v` requests the `++dome` at the specified commit. -`%w` requests the current revsion number of the desk. +`%w` requests the revsion number of the desk. `%x` requests the file at a specified path at the specified commit. If there is no node at that path or if the node has no contents (that is, if `q:ankh` is @@ -296,7 +296,8 @@ stuff before this time. simply `p:dojo`, all subscribers to the desk, while in foreign desks this is all the subscribers from our ship to the foreign desk. -`ref` is the request manager for the desk. +`ref` is the request manager for the desk. For domestic desks, this is null +since we handle requests ourselves. `dom` is the actual data in the desk. @@ -311,7 +312,7 @@ all the subscribers from our ship to the foreign desk. == :: ``` -This is the request manager for a desk. +This is the request manager for a foreign desk. `nix` is one more than the index of the most recent request. Thus, it is the next available request number. @@ -574,6 +575,32 @@ This represents a request for data about a particular desk. If `q` contains a `rave`, then this opens a subscription to the desk for that data. If `q` is null, then this tells clay to cancel the subscription along this duct. +###`++riot`, response + +``` +++ riot (unit rant) :: response/complete +``` + +A riot is a response to a subscription. If null, the subscription has been +completed, and no more responses will be sent. Otherwise, the `rant` is the +produced data. + +###`++rant`, response data + +``` +++ rant :: namespace binding + $: p=[p=care q=case r=@tas] :: clade release book + q=path :: spur + r=* :: data + == :: +``` + +This is the data at a particular node in the filesystem. `p.p` specifies the +type of data that was requested (and is produced). `q.p` gives the specific +version reported (since a range of versions may be requested in a +subscription). `r.p` is the desk. `q` is the path to the filesystem node. +`r` is the data itself (in the format specified by `p.p`). + Interface --------- @@ -677,3 +704,311 @@ another ship asks for a file from us, that request comes to us in the form of a This is a request for information about a particular desk. This is, in its most general form, a subscription, though in many cases it is the trivial case of a subscription -- a read. See `++riff` for the format of the request. + +Lifecycle of a Local Read +------------------------- + +There are two real types of interaction with a filesystem: you can read, and +you can write. We'll describe each process, detailing both the flow of control +followed by the kernel and the algorithms involved. The simpler case is that +of the read, so we'll begin with that. + +When a vane or an application wishes to read a file from the filesystem, it +sends a `%warp` kiss, as described above. Of course, you may request a file on +another ship and, being a global filesystem, clay will happily produce it for +you. That code pathway will be described in another section; here, we will +restrict ourselves to examining the case of a read from a ship on our own pier. + +The kiss can request either a single version of a file or a range of versions +of a desk. We'll trace through both paths at once. + +As in all vanes, a kiss enters clay via a call to `++call`. Scanning through +the arm, we quickly see where `%warp` is handled. + +``` + ?: =(p.p.q.hic q.p.q.hic) + =+ une=(un p.p.q.hic now ruf) + =+ wex=(di:une p.q.q.hic) + =+ ^= wao + ?~ q.q.q.hic + (ease:wex hen) + (eave:wex hen u.q.q.q.hic) + =+ ^= woo + abet:wao + [-.woo abet:(pish:une p.q.q.hic +.woo ran.wao)] +``` + +We're following the familar patern of producing a list of moves and an updated +state. In this case, the state is `++raft`. + +We first check to see if the sending and receiving ships are the same. If +they're not, then this is a request for data on another ship. We describe that +process later. Here, we discuss only the case of a local read. + +At a high level, the call to `++un` sets up the core for the domestic ship that +contains the files we're looking for. The call to `++di` sets up the core for +the particular desk we're referring to. + +After this, we perform the actual request. If there is no rave in the riff, +then that means we are cancelling a request, so we call `++ease:de`. +Otherwise, we start a subscription with `++eave:de`. We call `++abet:de` to +resolve our various types of output into actual moves. We produce the moves we +found above and the `++un` core resolved with `++pish:un` (putting the modified +desk in the room) and `++abet:un` (putting the modified room in the raft). + +Much of this is fairly straightforward, so we'll only describe `++ease`, +`++eave`, and `++abet:de`. Feel free to look up the code to the other steps -- +it should be easy to follow. + +Although it's called last, it's usually worth examining `++abet` first, since +it defines in what ways we can cause side effects. Let's do that, and also a +few of the lines at the beginning of `++de`. + +``` + =| yel=(list ,[p=duct q=gift]) + =| byn=(list ,[p=duct q=riot]) + =| vag=(list ,[p=duct q=gift]) + =| say=(list ,[p=duct q=path r=ship s=[p=@ud q=riff]]) + |% + ++ abet + ^- [(list move) rede] + :_ red + ;: weld + %+ turn (flop yel) + |=([a=duct b=gift] [hun %give b]) + :: + %+ turn (flop byn) + |=([a=duct b=riot] [a %give [%writ b]]) + :: + %+ turn (flop vag) + |=([a=duct b=gift] [a %give b]) + :: + %+ turn (flop say) + |= [a=duct b=path c=ship d=[p=@ud q=riff]] + :- a + [%pass b %a %want [who c] [%q %re p.q.d (scot %ud p.d) ~] q.d] + == +``` + +This is very simple code. We see there are exactly four different kinds of +side effects we can generate. + +In `yel` we put gifts that we wish to be sent along the `hun:room` duct to +dill. See the documentation for `++room` above. This is how we display +messages to the terminal. + +In `byn` we put riots that we wish returned to subscribers. Recall that a riot +is a response to a subscription. These are returned to our subscribers in the +form of a `%writ` gift. + +In `vag` we put gifts along with the ducts on which to send them. This allows +us to produce arbitrary gifts, but in practice this is only used to produce +`%ergo` gifts. + +In `say` we put messages we wish to pass to ames. These messages are used to +request information from clay on other piers. We must provide not only the +duct and the request (the riff), but also the return path, the other ship to +talk to, and the sequence number of the request. + +Now that we know what kinds of side effects we may have, we can jump into the +handling of requests. + +``` + ++ ease :: release request + |= hen=duct + ^+ +> + =. qyx (~(del by qyx) hen) + ?~ ref +> + |- ^+ +>+.$ + =+ nux=(~(get by fod.u.ref) hen) + ?~ nux +>+.$ + %= +>+.$ + say [[hen [(scot %ud u.nux) ~] for [u.nux syd ~]] say] + fod.u.ref (~(del by fod.u.ref) hen) + bom.u.ref (~(del by bom.u.ref) u.nux) + == +``` + +This is called when we're cancelling a subscription. First, we remove the duct +from our map of subscribers. For domestic desks, `ref` is null, so we're done. +Although we said we're not going to talk about foreign requests yet, it's easy +to see that for foreign desks, we cancel any outstanding requests for this duct +and send a message over ames to the other ship telling them to cancel the +subscription. + +The more interesting case is, of course, when we're not cancelling a +subscription but starting one. + +``` + ++ eave :: subscribe + |= [hen=duct rav=rave] + ^+ +> + ?- -.rav + & + ?: &(=(p.p.rav %u) !=(p.q.p.rav now)) + ~& [%clay-fail p.q.p.rav %now now] + !! + =+ ver=(aver p.rav) + ?~ ver + (duce hen rav) + ?~ u.ver + (blub hen) + (blab hen p.rav u.u.ver) +``` + +There are two types of subscriptions -- either we're requesting a single file +or we're requesting a range of versions of a desk. We'll dicuss the simpler +case first. + +First, we check that we're not requesting the `rang` from any time other than +the present. Since we don't store that information for any other time, we +can't produce it in a referentially transparent manner for any time other than +the present. + +Then, we try to read the requested `mood` `p.rav`. If we can't access the +request data right now, we call `++duce` to put the request in our queue to be +satisfied when the information becomes available. The code for `++duce` is +nearly the exact inverse of `++ease`, which in the case of a domestic desk is +very simple -- we simply put the duct and rave into `qyx`. This case occurs +when we make a request for a case whose (1) date is after the current date, (2) +number is after the current number, or (3) label is not yet used. + +If `++aver` returned `[~ ~]`, then we cancel the subscription. This occurs +when we make (1) a `%x` request for a file that does not exist, (2) a `%w` +request with a case that is not a number, or (3) a `%w` request with a nonempty +path. The `++blub` is exactly what you would expect it to be. + +``` + ++ blub :: ship stop + |= hen=duct + %_(+> byn [[hen ~] byn]) +``` + +We notify the duct that we're cancelling their subscription since it isn't +satisfiable. + +Otherwise, we have received the desired information, so we send it on to the +subscriber with `++blab`. + +``` + ++ blab :: ship result + |= [hen=duct mun=mood dat=*] + ^+ +> + +>(byn [[hen ~ [p.mun q.mun syd] r.mun dat] byn]) +``` + +The most interesting arm called in `++eave` is, of course, `++aver`, where we +actually try to read the data. + +``` + ++ aver :: read + |= mun=mood + ^- (unit (unit ,*)) + ?: &(=(p.mun %u) !=(p.q.mun now)) :: prevent bad things + ~& [%clay-fail p.q.mun %now now] + !! + =+ ezy=?~(ref ~ (~(get by haw.u.ref) mun)) + ?^ ezy ezy + =+ nao=(~(aeon ze lim dom ran) q.mun) + :: ~& [%aver-mun nao [%from syd lim q.mun]] + ?~(nao ~ [~ (~(avid ze lim dom ran) u.nao mun)]) +``` + +We check immediately that we're not requesting the `rang` for any time other +than the present. + +If this is a foreign desk, then we check our cache for the specific request. +If either this is a domestic desk or we don't have the request in our cache, +then we have to actually go read the data from our dome. + +We need to do two things. First, we try to find the number of the commit +specified by the given case, and then we try to get the data there. + +Here, we jump into `arvo/zuse.hoon`, which is where much of the algorithmic +code is stored, as opposed to the clay interface, which is stored in +`arvo/clay.hoon`. We examine `++aeon:ze`. + +``` + ++ aeon :: aeon:ze + |= lok=case :: act count through + ^- (unit ,@ud) + ?- -.lok + %da + ?: (gth p.lok lim) ~ + |- ^- (unit ,@ud) + ?: =(0 let) [~ 0] :: avoid underflow + ?: %+ gte p.lok + =< t + %- ~(got by hut) + %- ~(got by hit) + let + [~ let] + $(let (dec let)) + :: + %tas (~(get by lab) p.lok) + %ud ?:((gth p.lok let) ~ [~ p.lok]) + == +``` + +We handle each type of `case` differently. The latter two types are easy. + +If we're requesting a revision by label, then we simply look up the requested +label in `lab` from the given dome. If it exists, that is our number; else we +produce null, indicating the requested revision does not yet exist. + +If we're requesting a revision by number, we check if we've yet reached that +number. If so, we produce the number; else we produce null. + +If we're requesting a revision by date, we check first if the date is in the +future, returning null if so. Else we start from the most recent revision and +scan backwards until we find the first revision committed before that date, and +we produce that. If we requested a date before any revisions were committed, +we produce `0`. + +Assuming we got a valid version number, `++aver` calls `++avid:ze`, which reads +the requested data at the given revision. + +``` + ++ avid :: avid:ze + |= [oan=@ud mun=mood] :: seek and read + ^- (unit) + ?: &(?=(%w p.mun) !?=(%ud -.q.mun)) :: NB only for speed + ?^(r.mun ~ [~ oan]) + (auto:(argo oan) mun) +``` + +If we're requesting the revision number with a case other than by number, then +we go ahead and just produce the number we were given. Otherwise, we call +`++argo` to rewind our state to the given revision, and then we call `++auto` +to get the requested information. + +``` + ++ argo :: argo:ze + |= oan=@ud :: rewind to aeon + ^+ +> + ?: =(let oan) +> + ?: (gth oan let) !! :: don't have this version + +>(ank (azel q:(need (~(get by hut) (need (~(get by hit) oan))))), let oan) +``` + +``` + | + =+ nab=(~(aeon ze lim dom ran) p.p.rav) + ?~ nab + ?> =(~ (~(aeon ze lim dom ran) q.p.rav)) + (duce hen rav) + =+ huy=(~(aeon ze lim dom ran) q.p.rav) + ?: &(?=(^ huy) |((lth u.huy u.nab) &(=(0 u.huy) =(0 u.nab)))) + (blub hen) + =+ top=?~(huy let.dom u.huy) + =+ fud=(~(gack ze lim dom ran) u.nab let.dom) + =. +>.$ (bleb hen u.nab fud) + ?^ huy + (blub hen) + =+ ^= ptr ^- case + [%ud +(let.dom)] + (duce hen `rave`[%| ptr q.p.rav]) + == +``` + +(this is from `++eave`)