Merge branch 'test' of https://github.com/urbit/urbit into test

Conflicts:
	urb/urbit.pill
This commit is contained in:
Anton Dyudin 2014-09-10 13:03:06 -07:00 committed by Anton Dyudin
commit d54584a164
3 changed files with 397 additions and 6 deletions

View File

@ -439,7 +439,7 @@
vix=(bex +((cut 0 [25 2] mag))) :: width of sender
tay=(cut 0 [27 5] mag) :: message type
==
?> =(7 vez)
?> =(1 vez)
?> =(chk (end 0 20 (mug bod)))
:+ [(end 3 wix bod) (cut 3 [wix vix] bod)]
(kins tay)
@ -459,7 +459,7 @@
=+ tay=(ksin q.kec)
%+ mix
%+ can 0
:~ [3 7]
:~ [3 1]
[20 (mug bod)]
[2 yax]
[2 qax]
@ -1047,7 +1047,7 @@
++ gnaw :: gnaw:am
|= [kay=cape ryn=lane pac=rock] :: process packet
^- [p=(list boon) q=fort]
?. =(7 (end 0 3 pac)) [~ fox]
?. =(1 (end 0 3 pac)) [~ fox]
=+ kec=(bite pac)
?: (goop p.p.kec) [~ fox]
?. (~(has by urb.ton.fox) q.p.kec)

View File

@ -338,8 +338,9 @@
=+ nex=(~(get by haw.u.ref) nez)
?~ nex +>+.^$
?~ u.nex +>+.^$ :: should never happen
=. +>+.^$ =+ roo=(edis ((hard nako) u.u.nex))
?>(?=(^ ref.roo) roo)
=. +>+.^$
=+ roo=(edis ((hard nako) u.u.nex))
?>(?=(^ ref.roo) roo)
%= $
haw.u.ref (~(del by haw.u.ref) nez)
==
@ -397,7 +398,7 @@
..wake =- (blub:- p.i.xiq)
=+ ^= ear
(~(lobes-at-path ze lim dom ran) u.huy r.p.q.i.xiq)
?: =(s.p.q.i.xiq ear) ..wake
?: =(s.p.q.i.xiq ear) (blub p.i.xiq)
=+ fud=(~(make-nako ze lim dom ran) u.nab u.huy)
(bleb p.i.xiq +(u.nab) fud)
==

View File

@ -1645,3 +1645,393 @@ Here, we just update the set of lobes we're given with the commits we're given
and produce both sets.
This concludes our discussion of a local subscription.
Lifecycle of a Foreign Read or Subscription
-------------------------------------------
Foreign reads and subscriptions are handled in much the same way as local ones.
The interface is the same -- a vane or app sends a `%warp` kiss with the
request. The difference is simply that the `sock` refers to the foreign ship.
Thus, we start in the same place -- in `++call`, handling `%warp`. However,
since the two side of the `sock` are different, we follow a different path.
```
=+ wex=(do now p.q.hic p.q.q.hic ruf)
=+ ^= woo
?~ q.q.q.hic
abet:(ease:wex hen)
abet:(eave:wex hen u.q.q.q.hic)
[-.woo (posh q.p.q.hic p.q.q.hic +.woo ruf)]
```
If we compare this to how the local case was handled, we see that it's not all
that different. We use `++do` rather than `++un` and `++de` to set up the core
for the foreign ship. This gives us a `++de` core, so we either cancel or
begin the request by calling `++ease` or `++eave`, exactly as in the local
case. In either case, we call `++abet:de` to resolve our various types of
output into actual moves, as described in the local case. Finally, we call
`++posh` to update our raft, putting the modified rung into the raft.
We'll first trace through `++do`.
```
++ do
|= [now=@da [who=ship him=ship] syd=@tas ruf=raft]
=+ ^= rug ^- rung
=+ rug=(~(get by hoy.ruf) him)
?^(rug u.rug *rung)
=+ ^= red ^- rede
=+ yit=(~(get by rus.rug) syd)
?^(yit u.yit `rede`[~2000.1.1 ~ [~ *rind] *dome])
((de now ~ ~) [who him] syd red ran.ruf)
```
If we already have a rung for this foreign ship, then we use that. Otherwise,
we create a new blank one. If we already have a rede in this rung, then we use
that, otherwise we create a blank one. An important point to note here is that
we let `ref` in the rede be `[~ *rind]`. Recall, for domestic desks `ref` is
null. We use this to distinguish between foreign and domestic desks in `++de`.
With this information, we create a `++de` core as usual.
Although we've already covered `++ease` and `++eave`, we'll go through them
quickly again, highlighting the case of foreign request.
```
++ 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)
==
```
Here, we still remove the duct from our cult (we maintain a cult even for
foreign desks), but we also need to tell the foreign desk to cancel our
subscription. We do this by sending a request (by appending to `say`, which
gets resolved in `++abet:de` to a `%want` kiss to ames) to the foreign ship to
cancel the subscription. Since we don't anymore expect a response on this
duct, we remove it from `fod` and `bom`, which are the maps between ducts,
raves, and request sequence numbers. Basically, we remove every trace of the
subscription from our request manager.
In the case of `++eave`, where we're creating a new request, everything is
exactly identical to the case of the local request except `++duce`. We said
that `++duce` simply puts the request into our cult. This is true for a
domestic request, but distinctly untrue for foreign requests.
```
++ duce :: produce request
|= [hen=duct rov=rove]
^+ +>
=. qyx (~(put by qyx) hen rov)
?~ ref +>.$
|- ^+ +>+.$ :: XX why?
=+ rav=(reve rov)
=+ ^= vaw ^- rave
?. ?=([%& %v *] rav) rav
[%| [%ud let.dom] `case`q.p.rav r.p.rav]
=+ inx=nix.u.ref
%= +>+.$
say [[hen [(scot %ud inx) ~] for [inx syd ~ vaw]] say]
nix.u.ref +(nix.u.ref)
bom.u.ref (~(put by bom.u.ref) inx [hen vaw])
fod.u.ref (~(put by fod.u.ref) hen inx)
==
```
If we have a request manager (i.e. this is a foreign desk), then we do the
approximate inverse of `++ease`. We create a rave out of the given request and
send it off to the foreign desk by putting it in `say`. Note that the rave is
created to request the information starting at the next revision number. Since
this is a new request, we put it into `fod` and `bom` to associate the request
with its duct and its sequence number. Since we're using another sequence
number, we must increment `nix`, which represents the next available sequence
number.
And that's really it for this side of the request. Requesting foreign
information isn't that hard. Let's see what it looks like on the other side.
When we get a request from another ship for information on our ship, that comes
to us in the form of a `%wart` from ames.
We handle a `%wart` in `++call`, right next to where we handle the `%warp` case.
```
%wart
?> ?=(%re q.q.hic)
=+ ryf=((hard riff) s.q.hic)
:_ ..^$
:~ :- hen
:^ %pass [(scot %p p.p.q.hic) (scot %p q.p.q.hic) r.q.hic]
%c
[%warp [p.p.q.hic p.p.q.hic] ryf]
==
```
Every request we receive should be of type `riff`, so we coerce it into that
type. We just convert this into a new `%warp` kiss that we pass to ourself.
This gets handled like normal, as a local request. When the request produces
a value, it does so like normal as a `%writ`, which is returned to `++take`
along the path we just sent it on.
```
%writ
?> ?=([@ @ *] tea)
=+ our=(need (slaw %p i.tea))
=+ him=(need (slaw %p i.t.tea))
:_ ..^$
:~ :- hen
[%pass ~ %a [%want [our him] [%r %re %c t.t.tea] p.+.q.hin]]
==
```
Since we encoded the ship we need to respond to in the path, we can just pass
our `%want` back to ames, so that we tell the requesting ship about the new
data.
This comes back to the original ship as a `%waft` from ames, which comes into
`++take`, right next to where we handled `%writ`.
```
%waft
?> ?=([@ @ ~] tea)
=+ syd=(need (slaw %tas i.tea))
=+ inx=(need (slaw %ud i.t.tea))
=+ ^= zat
=< wake
(knit:(do now p.+.q.hin syd ruf) [inx ((hard riot) q.+.q.hin)])
=^ mos ruf
=+ zot=abet.zat
[-.zot (posh q.p.+.q.hin syd +.zot ruf)]
[mos ..^$(ran.ruf ran.zat)] :: merge in new obj
```
This gets the desk and sequence number from the path the request was sent over.
This determines exactly which request is being responded to. We call
`++knit:de` to apply the changes to our local desk, and we call `++wake` to
update our subscribers. Then we call `++abet:de` and `++posh` as normal (like
in `++eave`).
We'll examine `++knit` and `++wake`, in that order.
```
++ knit :: external change
|= [inx=@ud rot=riot]
^+ +>
?> ?=(^ ref)
|- ^+ +>+.$
=+ ruv=(~(get by bom.u.ref) inx)
?~ ruv +>+.$
=> ?. |(?=(~ rot) ?=(& -.q.u.ruv)) .
%_ .
bom.u.ref (~(del by bom.u.ref) inx)
fod.u.ref (~(del by fod.u.ref) p.u.ruv)
==
?~ rot
=+ rav=`rave`q.u.ruv
%= +>+.$
lim
?.(&(?=(| -.rav) ?=(%da -.q.p.rav)) lim `@da`p.q.p.rav)
::
haw.u.ref
?. ?=(& -.rav) haw.u.ref
(~(put by haw.u.ref) p.rav ~)
==
?< ?=(%v p.p.u.rot)
=. haw.u.ref
(~(put by haw.u.ref) [p.p.u.rot q.p.u.rot q.u.rot] ~ r.u.rot)
?. ?=(%w p.p.u.rot) +>+.$
|- ^+ +>+.^$
=+ nez=[%w [%ud let.dom] ~]
=+ nex=(~(get by haw.u.ref) nez)
?~ nex +>+.^$
?~ u.nex +>+.^$ :: should never happen
=. +>+.^$ =+ roo=(edis ((hard nako) u.u.nex))
?>(?=(^ ref.roo) roo)
%= $
haw.u.ref (~(del by haw.u.ref) nez)
==
```
This is kind of a long gate, but don't worry, it's not bad at all.
First, we assert that we're not a domestic desk. That wouldn't make any sense
at all.
Since we have the sequence number of the request, we can get the duct and rave
from `bom` in our request manager. If we didn't actually request this data (or
the request was canceled before we got it), then we do nothing.
Else, we remove the request from `bom` and `fod` unless this was a subscription
request and we didn't receive a null riot (which would indicate the last
message on the subscription).
Now, if we received a null riot, then if this was a subscription request by
date, then we update `lim` in our request manager (representing the latest time
at which we have complete information for this desk) to the date that was
requested. If this was a single read request, then we put the result in our
simple cache `haw` to make future requests faster. Then we're done.
If we received actual data, then we put it into our cache `haw`. Unless it's a
`%w` request, we're done.
If it is a `%w` request, then we try to get the `%w` at our current head from
the cache. In general, that should be the thing we just put in a moment ago,
but that is not guaranteed. The most common case where this is different is
when we receive desk updates out of order. At any rate, since we now have new
information, we need to apply it to our local copy of the desk. We do so in
`++edis`, and then we remove the stuff we just applied from the cache, since
it's not really a true "single read", like what should really be in the cache.
```
++ edis :: apply subscription
|= nak=nako
^+ +>
%= +>
hit.dom (~(uni by hit.dom) gar.nak)
let.dom let.nak
lat.ran %+ roll (~(tap in bar.nak) ~)
=< .(yeb lat.ran)
|= [sar=blob yeb=(map lobe blob)]
=+ zax=(blob-to-lobe sar)
%+ ~(put by yeb) zax sar
hut.ran %+ roll (~(tap in lar.nak) ~)
=< .(yeb hut.ran)
|= [sar=yaki yeb=(map tako yaki)]
%+ ~(put by yeb) r.sar sar
==
```
This shows, of course, exactly why nako is defined the way it is. To become
completely up to date with the foreign desk, we need to merge `hit` with the
foreign one so that we have all the revision numbers. We update `let` so that
we know which revision is the head.
We merge the new blobs in `lat`, keying them by their hash, which we get from a
call to `++blob-to-lobe`. Recall that the hash is stored in the actual blob
itself. We do the same thing to the new yakis, putting them in `hut`, keyed by
their hash.
Now, our local dome should be exactly the same as the foreign one.
This concludes our discussion of `++knit`. Now the changes have been applied to
our local copy of the desk, and we just need to update our subscribers. We do
so in `++wake:de`.
```
++ wake :: update subscribers
^+ .
=+ xiq=(~(tap by qyx) ~)
=| xaq=(list ,[p=duct q=rove])
|- ^+ ..wake
?~ xiq
..wake(qyx (~(gas by *cult) xaq))
?- -.q.i.xiq
&
=+ cas=?~(ref ~ (~(get by haw.u.ref) `mood`p.q.i.xiq))
?^ cas
%= $
xiq t.xiq
..wake ?~ u.cas (blub p.i.xiq)
(blab p.i.xiq p.q.i.xiq u.u.cas)
==
=+ nao=(~(case-to-aeon ze lim dom ran) q.p.q.i.xiq)
?~ nao $(xiq t.xiq, xaq [i.xiq xaq])
$(xiq t.xiq, ..wake (balk p.i.xiq u.nao p.q.i.xiq))
::
|
=+ mot=`moot`p.q.i.xiq
=+ nab=(~(case-to-aeon ze lim dom ran) p.mot)
?~ nab
$(xiq t.xiq, xaq [i.xiq xaq])
=+ huy=(~(case-to-aeon ze lim dom ran) q.mot)
?~ huy
=+ ptr=[%ud +(let.dom)]
%= $
xiq t.xiq
xaq [[p.i.xiq [%| ptr q.mot r.mot s.mot]] xaq]
..wake =+ ^= ear
(~(lobes-at-path ze lim dom ran) let.dom r.p.q.i.xiq)
?: =(s.p.q.i.xiq ear) ..wake
=+ fud=(~(make-nako ze lim dom ran) u.nab let.dom)
(bleb p.i.xiq let.dom fud)
==
%= $
xiq t.xiq
..wake =- (blub:- p.i.xiq)
=+ ^= ear
(~(lobes-at-path ze lim dom ran) u.huy r.p.q.i.xiq)
?: =(s.p.q.i.xiq ear) (blub p.i.xiq)
=+ fud=(~(make-nako ze lim dom ran) u.nab u.huy)
(bleb p.i.xiq +(u.nab) fud)
==
==
--
```
This is even longer than `++knit`, but it's pretty similar to `++eave`. We loop
through each of our subscribers `xiq`, processing each in turn. When we're done,
we just put the remaining subscribers back in our subscriber list.
If the subscriber is a single read, then, if this is a foreign desk (note that
`++wake` is called from other arms, and not only on foreign desks). Obviously,
if we find an identical request there, then we can produce the result
immediately. Referential transparency for the win. We produce the result with
a call to `++blab`. If this is a foreign desk but the result is not in the
cache, then we produce `++blub` (canceled subscription with no data) for
reasons entirely opaque to me. Seriously, it seems like we should wait until
we get an actual response to the request. If someone figures out why this is,
let me know. At any rate, it seems to work.
If this is a domestic desk, then we check to see if the case exists yet. If
it doesn't, then we simply move on to the next subscriber, consing this one
onto `xaq` so that we can check again the next time around. If it does exist,
then we call `++balk` to fulfill the request and produce it.
`++balk` is very simple, so we'll describe it here before we get to the
subscription case.
```
++ balk :: read and send
|= [hen=duct oan=@ud mun=mood]
^+ +>
=+ vid=(~(read-at-aeon ze lim dom ran) oan mun)
?~ vid (blub hen) (blab hen mun u.vid)
```
We call `++read-at-aeon` on the given request and aeon. If you recall, this
processes a `mood` at a particular aeon and produces the result, if there is
one. If there is data at the requested location, then we produce it with
`++blab`. Else, we call `++blub` to notify the subscriber that no data can
ever come over this subscriptioin since it is now impossible for there to ever
be data for the given request. Because referential transparency.
At any rate, back to `++wake`. If the given rave is a subscription request,
then we proceed similarly to how we do in `++eave`. We first try to get the
aeon referred to by the starting case. If it doesn't exist yet, then we can't
do anything interesting with this subscription, so we move on to the next one.
Otherwise, we try to get the aeon referrred to by the ending case. If it
doesn't exist yet, then we produce all the information we can. We call
`++lobes-at-path` at the given aeon and path to see if the requested path has
actually changed. If it hasn't, then we don't produce anything; else, we
produce the correct nako by calling `++bleb` on the result of `++make-nako`, as
in `++eave`. At any rate, we move on to the next subscription, putting back
into our cult the current subscription with a new start case of the next aeon
after the present.
If the aeon referred to by the ending case does exist, then we drop this
subscriber from our cult and satisfy its request immediately. This is the same
as before -- we check to see if the data at the path has actually changed,
producing it if it has; else, we call `++blub` since no more data can be
produced over this subscription.
This concludes our discussion of foreign requests.