finished leader example in testing tutorial

This commit is contained in:
Ken McMillan 2016-09-20 18:40:25 -07:00
Родитель e6731305ee
Коммит 5654734eae
14 изменённых файлов: 338 добавлений и 413 удалений

Просмотреть файл

@ -28,4 +28,4 @@
docs:
- examples/testing/intro
- examples/testing/specification
- examples/testing/leader

Просмотреть файл

@ -32,16 +32,15 @@ We'll start with a service specification for this protocol:
object asgn = {
function pid(X:node.t) : id.t # map each node to an id
function pid(X:node.t) : id.t
axiom [injectivity] pid(X) = pid(Y) -> X = Y
}
object serv = {
action elect(v:node.t) # called when v is elected leader
action elect(v:node.t)
object spec = { # only the max pid can be elected
object spec = {
before elect {
assert asgn.pid(v) >= asgn.pid(X)
}
@ -64,7 +63,7 @@ top of two services: a network service and a timer service:
![Leader Election Ring Figure 1](leader_fig1-crop-1.png)
Let's consider now the two intermediate interfaces. The specification
for the network service is quite simple and ocmes from IVy's standard
for the network service is quite simple and comes from IVy's standard
library:
module ip_simple(addr,pkt) = {
@ -139,421 +138,234 @@ We create one instance of the network module, and one timer for each
node. The protocol implements the interface actions `timer.timeout`
and `net.recv`. When a node times out, it transmits the node's `id` to
the next process in the ring, defined by the the action
`node.get_next` (which we'll see shortly).
`node.next` (which we'll define shortly).
When a node receives a message, an id value `v`, it checks whether `v`
When a node receives an id value `v`, it checks whether `v`
is its own id according to the id assignment `asgn`. If so, the
process knows it is leader and calls `serv.elect`. Otherwise, if the
received value is greater, it calls `net.send` to send the value on to
the next node in the ring.
With our protocol implemented, let's look at the interfaces that it's
built on, starting with the type of `id` values:
With our protocol implemented, we still need to provide concrete types
for `node` and `id`. If we were formally verifying the protocol, we
would probably treat these as abstract datatypes with specified
mathematical properties. For testing, though, we just need some
concrete types. Here is one possiblity:
module totally_ordered(t) = {
relation (X:t < Y:t)
property [transitivity] X:t < Y & Y < Z -> X < Z
property [antisymmetry] ~(X:t < Y & Y < X)
property [totality] X:t < Y | X = Y | Y < X
}
module total_order = {
object node = {
type t
instantiate totally_ordered(t) # t is totally ordered
interpret t -> bv[1]
action next(x:t) returns (y:t) = {
y := x + 1
}
}
instance id : total_order
Notice that we defined a couple of modules that we can re-use later.
The first gives the axioms of a total order. The second defines a
datatype that obeys these axioms. We then define our abstract type
`id` to be an instance of `total_order`. Later, we will implement
`id.t` with fixed-width integers.
Now let's look at the type of node references, which is a bit more
interesting:
module ring_topology = {
object id = {
type t
instantiate total_order_axioms(t) # t is totally ordered
action get_next(x:t) returns (y:t)
object spec = {
after get_next {
assert (x < y & ~ (x < Z & Z < y)) | (y <= X & X <= x)
}
}
interpret t -> bv[8]
}
instance node : ring_topology
We start by defining a generic module for types that are organized in
a ring topology. We use a totally ordered type `t` for the ring
elements. In addition, the `get_next` action takes an element `x` and
returns the next element `y` in the ring. The specification says that
either `y` is the least element larger than `x` or `x` is the maximum
element and `y` the minimum (that is, we "wrap around" to the
beginning). In principle, we should also say that the set `t` is finite,
but we won't actually need this fact to prove safety of our protocol.
This is one way of specifying a ring topology. Later we will
see a different way that can make the proofs a little simpler. We can
implement either version with fixed-width integers.
Finally, we need the service specification for the network transport
layer. It's quite simple:
object trans = {
relation sent(V:id.t, N:node.t)
init ~sent(V, N)
action send(dst:node.t, v:id.t)
action recv(dst:node.t, v:id.t)
object spec = {
before send {
sent(v,dst) := true
}
before recv {
assert sent(v,dst)
}
}
}
The relation `sent` tells us which values have been sent to which
nodes. Initially, nothing has been sent. The interface provides two
actions `send` and `recv` which are called, respectively, when a value
is sent or received. The `dst` parameter gives the destination node of
the message.
The specification says that a value is marked as sent when `send`
occurs and a value must be marked sent before it can be received.
This describes a network service that can duplicate or re-order
messages, but cannot corrupt messages.
## Verifying the leader election protocol
Now let's try to verify that our leader election protocol `app`
guarantees its service specification `serv` , assuming the
specifications of `trans`, `node`, `id` and `asgn`. Here is the
isolate declaration:
isolate iso_p = app with node,id,trans,serv,asgn
We are trying to prove that, when any node calls `serv.elect`, it in
fact has the highest `id`. That is, the `before` specification of
`serv.elect` is a guarantee of `app` when it calls `serv.elect`.
To get a sense of what we're proving, let's have a look at the
isolate:
$ ivy_show isolate=iso_app leader_election_ring.ivy
... various declarations ...
action trans.send(dst:node.t,v:id.t) = {
trans.sent(v,dst) := true
}
action ext:trans.recv(dst:node.t,v:id.t) = {
assume trans.sent(v,dst);
if v = asgn.pid(dst) {
call serv.elect(dst)
}
else {
if v > asgn.pid(dst) {
local loc[0] {
call loc[0] := node.get_next(dst);
call trans.send(loc[0], v)
}
}
}
}
action serv.elect(v:node.t) = {
assert asgn.pid(v) >= asgn.pid(X)
}
action ext:app.send(me:node.t) = {
local loc[0] {
call loc[0] := node.get_next(me);
call trans.send(loc[0], asgn.pid(me))
}
}
action node.get_next(x:node.t) returns(y:node.t) = {
assume ((x < y & ~(Z:node.t < y & x < Z)) | (x = node.tail & y = node.head))
}
Notice that the assertion in `serv.elect` is preserved as a guarantee. However,
the other assertions have become assumptions.
Obviously, we will need an inductive invariant at this point. We will
try to discover one using IVy's [CTI
method](client_server_example.html). We start IVy using this command:
$ ivy ui=cti isolate=iso_app leader_election_ring.ivy
We start, as usual, with `Invariant|Check induction`. Ivy tells us the
the safety check failed, meaning that an assert fails in some action:
![IVy screenshot](images/leader1.png)
When we click OK, this is what we see:
![IVy screenshot](images/leader2.png)
On the left, we see a failing transition starting in state `0`, using
one of our two exported actions, `app.async` and `trans.recv`. To
discover which action failed, and explore the execution path to the
failure, we can left-click on the transition and choose `Decompose`.
In this case, however, we already have a pretty good idea of what went
wrong: some node must have received its own `id` and declared itself
leader without having the highest `id`. This is not surprising, since
with no invariant conjectures, the only thing we know about state `0`
is that it satisfies our axioms.
On the right, we see a depiction of the state `0`. There are two
elements in each of the two sorts `id.t` and `node.t`. Since we
haven't yet selected any relations to view, this is all we see. We
could start selecting relations to see more information, but let's
instead choose the command `Invariant|Diagram` to see what information
IVy thinks might be relevant to the failure. Here's what we see:
![IVy screenshot](images/leader3.png)
The arrow from id 0 to id 1 is depicting the relation `X:id.t < Y`.
Because the arrow goes from `X` to `Y`, we interpret the arrow to mean
`0:id.t < 1`. Similarly, the arrow from node 0 to id 0 means that
`asgn.pid(0) = 0`. That is, the id of node 0 is 0. We can also see
that node 1's id is 1 and that `trans.sent(0,1)`, in other words, id 0
has been sent to node 0. These facts are displayed below the graph
under the heading "Constraints".
The problem is clear: node 1 is receiving its own id, so it is about
to become leader, but it does not have highest id. This situation
clearly has to be ruled out. We do this by selecting the
`Conjecture|Strengthen` command. Here is what IVy says:
![IVy screenshot](images/leader4.png)
In other words, we conjecture that it never happens that a node N has
id less than a node P, and node N is receiving its own id. Now we choose
`Invariant|Check induction` again, to see if our conjecture is inductive.
Of course, it isn't. Here's what Ivy says:
![IVy screenshot](images/leader5.png)
And here is the counterexample to induction:
![IVy screenshot](images/leader6.png)
Here, some externally called action transitions from state `0`
satisfying our conjecture to state `1` *not* satisfying the
conjecture. IVy is depicting state `0`, with all the relations that
appear in the conjecture. It's not immediately clear what's wrong here,
so let's try `Invariant|Diagram` again to see what IVy thinks:
![IVy screenshot](images/leader7.png)
Now we can see the problem: node 1's id is arriving at node 0, but it
should never have passed node 2, because node 2 has a higher
id. Notice, though, that there are two additional arrows in the diagram
we didn't mention (the ones from node 0 to id 0 and id 0 to id 1). Maybe
these are actually irrelevant. We could remove these manually by
clicking on the corresponding facts below. However, we have another
trick up our sleeve. We can use bounded checking to see if some arrows
can be removed. We choose `Conjecture|Minimize` and (somewhat
arbitrarily) select a bound of 4 steps. Here is what IVy says:
![IVy screenshot](images/leader8.png)
This conjecture says that we never have nodes in the order `N < P < Q`
such that `P` has a smaller id than `Q` and the id of `P` is arriving
at `N`. In the graph, we see that the highlights have been removed
from the two irrelevant arrows:
![IVy screenshot](images/leader9.png)
IVy discovered that, within 4 steps, we
can rule out the highlighted facts. By ruling out a more general
situation, we obtain a stronger conjecture. Since this new conjecture
seems right, let's add it to our set by selecting
`Conjecture|Strengthen`.
Now let's try `Invariant|Check induction` again. IVy is still unhappy
and says that our first conjecture is still not relatively inductive. We
try `Invariant|Diagram` again and here is what we see:
![IVy screenshot](images/leader10.png)
This looks very similar to the diagram that led to our previous
conjecture. Here, however, it's the id of node 0 that is arriving at
node 2, when it couldn't have passed through node 1. This situation is
symmetric to the previous one by rotating the ring. Unfortunately, the
way we described the ring topology using a linear order has broken the
ring's rotational symmetry, so this appears as a distinct case. Later,
we'll see a way to avoid this symmetry breaking. For now, though,
we'll just slog through the cases. As before, we minimize this
diagram by bounded checking. Here is the result:
![IVy screenshot](images/leader11.png)
IVy conjectures that we do not have nodes `N < P < Q` such that `N`
has a lower id than `P` and the id of `N` is arriving at `Q`. This is
just another case of the general proposition that a lower id cannot
pass a higher id. We chose `Conjecture|Strengthen` to add this new
conjecture to our set.
Now we try `Invariant|Check induction` (the reader may be able to
guess what happens next). Again, IVy says that our first conjecture is
not relatively inductive. After `Invariant|Diagram`, we see this:
![IVy screenshot](images/leader12.png)
This describes the same situation, where the id of node 2 is arriving at node 1.
Once again, we generalize using `Conjecture|Minimize`, giving this conjecture:
![IVy screenshot](images/leader13.png)
We add this conjecture using `Conjecture|Strengthen`. Now when we
use `Invariant|Check induction`, we get the following:
![IVy screenshot](images/leader14.png)
That is, we have found a proof for the isolate. We can save this invariant to
a file using the `File|Save invariant` command. We edit these conjectures
into our implementation object `app`, like this:
object app = {
action async(me:node.t) = {
call trans.send(node.get_next(me),asgn.pid(me))
}
implement trans.recv(me:node.t,v:id.t) {
if v = asgn.pid(me) { # Found a leader!
call serv.elect(me)
}
else if v > asgn.pid(me) { # pass message to next node
call trans.send(node.get_next(me),v)
}
}
conjecture ~(asgn.pid(N) < asgn.pid(P) & trans.sent(asgn.pid(N),N))
conjecture ~(asgn.pid(P) < asgn.pid(Q) & N:node.t < P & P < Q & trans.sent(asgn.pid(P),N))
conjecture ~(asgn.pid(N) < asgn.pid(P) & N < P & P < Q & trans.sent(asgn.pid(N),Q))
conjecture ~(asgn.pid(Q) < asgn.pid(N) & N < P & P < Q & trans.sent(asgn.pid(Q),P))
}
Of course, if we don't want to mess up our pretty implementation with parts of the proof,
we can put the conjectures in a separate object and include that object in the isolate definition,
like this:
object app_proof = {
conjecture ~(asgn.pid(N) < asgn.pid(P) & trans.sent(asgn.pid(N),N))
conjecture ~(asgn.pid(P) < asgn.pid(Q) & N:node.t < P & P < Q & trans.sent(asgn.pid(P),N))
conjecture ~(asgn.pid(N) < asgn.pid(P) & N < P & P < Q & trans.sent(asgn.pid(N),Q))
conjecture ~(asgn.pid(Q) < asgn.pid(N) & N < P & P < Q & trans.sent(asgn.pid(Q),P))
}
isolate iso_p = app with node,id,trans,serv,app_proof
## How not to break the rotational symmetry
As we observed above, this proof is a bit messy because of the way we
described the ring topology using a totally ordered set. There's a
different way to describe rings that avoids this problem. Here is an
alternative specification of `ring_topology`:
module ring_topology = {
type t
relation btw(X:t,Y:t,Z:t)
property btw(W, Y, Z) & btw(W, X, Y) -> btw(X, Y, Z)
property btw(W, X, Z) & btw(X, Y, Z) -> btw(W, X, Y)
property btw(W, X, Z) & btw(X, Y, Z) -> btw(W, Y, Z)
property btw(W, Y, Z) & btw(W, X, Y) -> btw(W, X, Z)
property W = X | btw(W, X, W)
property ~btw(X, X, Y)
property ~btw(X, Y, Y)
property btw(X,Y,Z) | Y = Z | btw(X,Z,Y)
property btw(X,Y,Z) | Y = X | btw(Y,X,Z)
action get_next(x:t) returns (y:t)
object spec = {
after get_next {
assert ~btw(x,Z,y)
}
}
}
Instead of putting the nodes in a total order, we define are relation
`btw(X,Y,Z)` that holds when `Y` occurs on the path form `X` to `Z` in
the ring. The axioms of `btw` are a bit more complex than the axioms
of a total order. One the other hand, it is very easy to specify
`get_next` in terms of `btw`. We say that `y` is next after `x` in the
ring if there is no `Z` between `x` and `y`. You might wonder if the
properties given above for `btw` are really correct. Later, when we
implement `ring_topology`, we'll prove that it has these properties.
Now, let's try to verify the isolate `iso_app` with this new version of `ring_topology`:
$ ivy ui=cti isolate=iso_app leader_election_ring_btw.ivy
As before, after `Invariant|Check induction` and `Invariant|Diagram`, this is what we see:
![IVy screenshot](images/leader15.png)
That is, we have a node `1` that is receiving its own id while node
`0` has a greater id. Notice, though, that on the right the relation
`node.btw` doesn't appear. This is because it is a ternary relation
and IVy doesn't know how to display it graphically in a reasonable
way.
Now, as before, let's add this as a conjecture using
`Conjecture|Strengthen`. As before, `Invariant|Check induction` shows
that this conjecture is not relatively inductive. Applying `Invariant|Diagram`,
this is what we see:
![IVy screenshot](images/leader16.png)
This is similar to what we saw before, but notice the blue arrow from
node `0` to node `1`. This corresponds to a new relation that has
been added on the right: `node.btw(2,X,Y)`. From this arrow, we can
infer that `node.btw(2,0,1)` holds. That is, starting at `2`, and
moving around the ring, we see `0` before `1`. We can also see this
fact stated below under "Constraints". This means that, from `0`, we
must pass through `1` on the way to `2`. Therefore, the id of `0`
cannot possibly reach `2`, as the diagram shows.
We can try generalizing this diagram using `Conjecture|Minimize`, but
in this case, there is no effect. No matter -- since this looks like a
reasonable conjecture, we use `Conjecture|Strengthen` to add it to our
invariant. Here is the result:
![IVy screenshot](images/leader17.png)
This says it is not possible to have nodes `Q,N,P` in ascending order of id,
such that `N` is on the way from `Q` to `P` and the id of `N` is arriving at `Q`.
Now let's try `Invariant|Check induction`:
![IVy screenshot](images/leader18.png)
Because we haven't broken the rotational symmetry of the ring, there
is just one case needed when we state the key invariant that an id
can't bypass a higher id in the ring. This illustrates that the
complexity of the proof can be affected by how we write the
specification. Once again, we can save this invariant and edit it into
the definition of `app`.
That is, node addresses are one-bit binary numbers (meaning we have just two
nodes, and id's are 8-bit numbers. Often it's a good idea to start testing
with small datatypes. In the case of the leader election ring, more nodes
would increase the amount of time needed to see an election event.
## Compositionally testing the leader election protocol
We're going to divide the system into three isolates, one for each of
its major components. Here is what the three isolates will look like:
![Leader Election Ring Figure 2](leader_fig2-crop-1.png)
Here are the isolate definitions:
trusted isolate iso_p = proto with serv,node,id,asgn,net,timer
trusted isolate iso_t = timer
trusted isolate iso_n = net with node,id
We added the keyword `trusted` to these isolates to indicate to IVy
that they will be verified informally by testing. Notice that the
isolate for `proto` contains its service specification as well as the
specifications of all of the other components. We need to test that
`proto` lives up to its guarantees at all of its interfaces, provided
that all of its assumptions hold. On the other hand, `net` needs only
the specifications of `node` and `id` (the `net` object contains its
own service specification).
We can now use IVy to compile test environments for each of the isolates,
based on the interface specifications. Let's start with the high-level protocol:
$ ivy_to_cpp isolate=iso_p target=test build=true leader_election_ring.ivy
g++ -I $Z3DIR/include -L $Z3DIR/lib -g -o leader_election_ring leader_election_ring.cpp -lz3
We run the tester:
$ ./leader_election_ring
> timer.timeout(1)
< net.send(1,0,170)
> net.recv(0,170)
< net.send(0,1,170)
> net.recv(0,170)
< net.send(0,1,170)
> net.recv(0,170)
< net.send(0,1,170)
> timer.timeout(0)
< net.send(0,1,149)
> timer.timeout(0)
< net.send(0,1,149)
> timer.timeout(0)
< net.send(0,1,149)
> net.recv(0,170)
...
> net.recv(0,170)
< net.send(0,1,170)
> timer.timeout(0)
< net.send(0,1,149)
> net.recv(1,170)
< serv.elect(1)
In this run, process 1 times out and sends its id 170 to process
0. Process 0 receives this id and forwards it back to process 1
(meaning process 0's id must be less than 170). Later, process 0 times
out and sends its id 149 to process 1. Eventually, the network decides
to deliver to process 1 the message with its own id, causing process
1 to be elected. When this happens, the assertion monitor checkes that in
fact process 1 has the highest id.
The important point here is that there is no actual network. The
tester is simulating the network by randomly selecting actions
consistent with the history of actions at the interface. In practice,
this means that the tester stores the relation `sent` (the state of
the network interface specification) and only calls `net.recv` with
packets that have been previously sent. When there are multiple
choices, the tester makes an effort to select uniformly. This is a
hard problem in general, however. In practice the distribution will be
non-uniform. If you look at the calls to `net.recv`, you can see that
there is some duplication of sent packets, as we would expect.
We can see here a major advantage of compositional testing over
integration testing. If we used a real network to test our protocol,
we might have to wait a long time for a packet duplication to occur
(in fact, this might never occur unless we take care to stress the
network appropriately). The resilience of our protocol to packet
duplication would not be well tested, as it is here. Similarly, our
protocol might have unintended dependences on timing. Since timeout
events are being generated randomly here, and not based on wall clock
time, we can see timings that might occur rarely in the actual system,
perhaps only under very heavy load.
Now let's turn to the network isolate. We compile and run:
$ ivy_to_cpp isolate=iso_n target=test build=true leader_election_ring.ivy
g++ -I $Z3DIR/include -L $Z3DIR/lib -g -o leader_election_ring leader_election_ring.cpp -lz3
./leader_election_ring
> net.send(0,0,168)
< net.recv(0,168)
> net.send(0,0,243)
< net.recv(0,243)
> net.send(0,0,13)
< net.recv(0,13)
> net.send(1,0,108)
> net.send(0,0,84)
> net.send(0,0,78)
< net.recv(0,108)
< net.recv(0,84)
< net.recv(0,78)
> net.send(1,0,17)
< net.recv(0,17)
> net.send(0,1,167)
< net.recv(1,167)
> net.send(1,0,3)
> net.send(0,1,13)
...
The object `net` is actually just a wrapper around the operating
system's networking API. Therefore, when we test `net`, we are
actually testing the operating system's networking stack. Fortunately,
testing reveals no errors. Notice though, that the tester is
generating a wider variety of packets than the protocol could
actually generate. In fact, it's simply generating packet values
uniformly at random. In this sense, the network stack is also being
better tested than it would be if composed with the actual protocol.
Finally, we can test the timer:
$ ivy_to_cpp isolate=iso_n target=test build=true leader_election_ring.ivy
g++ -I $Z3DIR/include -L $Z3DIR/lib -g -o leader_election_ring leader_election_ring.cpp -lz3
./leader_election_ring
< timer.timeout(0)
< timer.timeout(1)
< timer.timeout(0)
< timer.timeout(1)
< timer.timeout(0)
< timer.timeout(1)
< timer.timeout(0)
< timer.timeout(1)
< timer.timeout(0)
...
$ ivy ui=cti isolate=iso_proto leader_election_ring.ivy
Not very interesting, but it shows both timeouts occurring once every
second, which is what we expect. Again, we are really testing the
operating system here (and also IVy's run time scheduler, which
uses the operating system API).
# Running the protocol
Now that we've tested the components, we can compile the whole system
and run it as a collection of independent processes. Our protocol is an
example of a [parameterized object](../leader.html). Ivy can decompose it
into a collection of processes based on the parameter `me`. Each
value of the parameter becomes an individual process. This
transformation is called *parameter stripping*.
To do this, we use the following declaration:
extract iso_impl(me:node.t) = proto(me),net(me),timer(me),node,id,asgn
This describes a special kind of isolate called an `extract`. Extracts
are intended to be run in production and don't include the
specification monitors. In the extract declaration, `me` is a symbolic
constant that represents the first parameter of of each action and
state component in the object. This parameter is "stripped" by
replacing it with the symbolic constant `me`.
Before thinking about this too hard, let's just run it to see what
happens. We compile as usual, with one difference: we use the option
`target=repl`. Instead of generating a test environment, IVy provides
a read-eval-print loop as the environment:
$ ivy_to_cpp target=repl isolate=iso_impl build=true leader_election_ring.ivy
g++ -I $Z3DIR/include -L $Z3DIR/lib -g -o leader_election_ring leader_election_ring.cpp -lz3
This produces an executable that takes one parameter `me` on the
command line. Let's create two terminals A annd B to run process 0 and
process 1 respectively:
A: $ ./leader_election_ring_udp2 0
A: >
B: $ ./leader_election_ring_udp2 1
B: >
A: < serve.elect
A: < serve.elect
A: < serve.elect
...
Notice that nothing happens when we start node 0. It is sending packets
once per second, but they are being dropped because no port is yet
open to receive them. After we start node 1, it forwards node 0's
packets, which causes node 0 to be elected (and to continues to be
elected once per second).

Просмотреть файл

@ -0,0 +1,114 @@
#lang ivy1.6
################################################################################
#
# Concrete type of node addresses.
#
################################################################################
object node = {
type t
interpret t -> bv[1]
action next(x:t) returns (y:t) = {
y := x + 1
}
}
################################################################################
#
# Concrete type of node ids.
#
################################################################################
object id = {
type t
interpret t -> bv[8]
}
################################################################################
#
# The assignments of id's to nodes
#
################################################################################
object asgn = {
function pid(X:node.t) : id.t # map each node to an id
axiom [injectivity] pid(X) = pid(Y) -> X = Y
}
################################################################################
#
# The network service specification
#
################################################################################
include udp
instance net : udp_simple(node.t,id.t)
################################################################################
#
# The timer service
#
################################################################################
include timeout
instance timer(X:node.t) : timeout_sec
################################################################################
#
# The high-level service specification
#
################################################################################
object serv = {
action elect(v:node.t) # called when v is elected leader
object spec = {
before elect {
assert asgn.pid(v) >= asgn.pid(X) # only the max pid can be elected
}
}
}
################################################################################
#
# The high-level protocol
#
################################################################################
object proto = {
implement timer.timeout(me:node.t) {
call net.send(me,node.next(me),asgn.pid(me))
}
implement net.recv(me:node.t,v:id.t) {
if v = asgn.pid(me) { # Found a leader
call serv.elect(me)
}
else if v > asgn.pid(me) { # pass message to next node
call net.send(me,node.next(me),v)
}
}
}
import serv.elect
trusted isolate iso_p = proto with serv,node,id,asgn,net,timer
trusted isolate iso_t = timer
trusted isolate iso_n = net with node,id
extract iso_impl(me:node.t) = proto(me),net(me),timer(me),node,id,asgn

Двоичные данные
doc/examples/testing/leader_fig1-crop-1.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 19 KiB

После

Ширина:  |  Высота:  |  Размер: 22 KiB

Двоичные данные
doc/examples/testing/leader_fig1-crop.pdf

Двоичный файл не отображается.

Двоичные данные
doc/examples/testing/leader_fig1.pdf

Двоичный файл не отображается.

Двоичные данные
doc/examples/testing/leader_fig1.pptx

Двоичный файл не отображается.

Двоичные данные
doc/examples/testing/leader_fig2-crop-1.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 34 KiB

Двоичные данные
doc/examples/testing/leader_fig2-crop.pdf Normal file

Двоичный файл не отображается.

Двоичные данные
doc/examples/testing/leader_fig2.pdf Executable file

Двоичный файл не отображается.

Двоичные данные
doc/examples/testing/leader_fig2.pptx Executable file

Двоичный файл не отображается.

Просмотреть файл

@ -43,6 +43,5 @@ module timeout_sec = {
}
instance impl : timeout_wrapper
private impl
}

Просмотреть файл

@ -843,10 +843,10 @@ def isolate_component(mod,isolate_name,extra_with=[],extra_strip=None):
if type(isolate) == ivy_ast.IsolateDef:
for action in mod.actions.values():
if isinstance(action,ia.NativeAction):
raise IvyError(action,'trusted code used in untrusted isolate')
raise iu.IvyError(action,'trusted code used in untrusted isolate')
for ldf in mod.definitions:
if isinstance(ldf.formula.args[1],ivy_ast.NativeExpr):
raise IvyError(action,'trusted code used in untrusted isolate')
raise iu.IvyError(action,'trusted code used in untrusted isolate')
# TODO: need a better way to filter signature
@ -1004,7 +1004,6 @@ def create_isolate(iso,mod = None,**kwargs):
extra_strip = {}
if create_imports.get():
set_up_implementation_map(mod)
print 'implementation_map: {}'.format(implementation_map)
newimps = []
outcalls = set()
for imp in mod.imports:

Просмотреть файл

@ -427,7 +427,7 @@ def emit_action_gen(header,impl,name,action,classname):
if len(action.formal_returns) == 0:
code_line(impl,call)
else:
code_line(impl,'std::out << "= " << ' + call)
code_line(impl,'std::cout << "= " << ' + call)
close_scope(impl)
@ -2079,7 +2079,8 @@ def emit_repl_boilerplate3test(header,impl,classname):
for(int cycle = 0; cycle < 100; cycle++) {
int rnd = rand() % (generators.size() + readers.size());
int choices = generators.size() + readers.size() + timers.size();
int rnd = choices ? (rand() % choices) : 0;
if (rnd < generators.size()) {
gen &g = *generators[rnd];
if (g.generate(ivy))