all runs. Formal verification may instead show that
a program is correct by proving that the code satis-
fies a given specification. We choose KeY (Beckert
et al., 2007) as our formal verification tool since it
is a highly automated theorem prover, and with sup-
port for ABS. We extended KeY with extra rules for
dealing with history-based properties. At the end we
compare the differences and challenges of using these
two approaches.
Paper overview. Section 2 introduces and explains
the core language, Section 3 presents (1)the reader-
writer example and (2)the publisher-subscriber exam-
ple, Section 4 formalizes the observable behavior in
the distributed systems, Section 5 shows the result of
runtime assertion checking on the examples (1) and
(2), Section 6 shows the result of theorem proving
on the examples (1) and (2), Section 7 compares run-
time assertion checking with theorem proving, Sec-
tion 8 discusses related work and we then close with
remarks about future work.
2 THE CORE LANGUAGE
Our core language is presented in Fig 1. It includes
basic statements for first order futures, taken from
ABS (HATS, 2011), which is an executable model-
ing language designed with hindsight to the modeling,
formal analysis and code generation of concurrent and
distributed systems.
Methods are organized in classes in a standard
manner. A class C takes a list of formal param-
eters
cp, and defines fields w, an optional initial-
ization block s, and methods M. There is read-
only access to class parameters cp as well as method
parameters. A method definition has the form
m(x){var y; s; return e}, ignoring type informa-
tion, where x is the list of parameters, y an optional list
of method-local variables, s a sequence of statements,
and the value of e is returned upon termination. As in
the Creol language (Johnsen and Owe, 2007), a ref-
erence to the caller is an implicit parameter denoted
caller.
A future is a placeholder for the return value of a
method call. Each future has a unique identity gen-
erated when the method is invoked. The future is re-
solved upon method termination, by placing the return
value of the method call in the future. Unlike the tra-
ditional method call mechanism, the callee does not
send the return value directly back to the caller. How-
ever, the caller may keep a reference to the future,
allowing the caller to fetch the future value once re-
solved. References to futures may be shared between
objects, e.g., by passing them as parameters. Thus a
Cl ::= class C([T cp]
∗
) {[T w [:= e]
?
]
∗
[s]
?
M
∗
}
M ::= T m([T x]
∗
) {[var [T x]
∗
]
?
s ; return e}
T ::= C | Int | Bool | String | Void | Fut < T >
v ::= x | w
e ::= null | this | v | cp | f (e)
s ::= v := e | fr := v!m(e) | v := e.get | skip
| await e | await e? | assert e
| while (e) {s} | if (e) {s} [else {s}]
?
| v := new C(e) | s;s
Figure 1: Core language syntax. Expressions e are side-
effect free, e is a (possibly empty) expression list. [v]
∗
and
[v]
?
denote repeated and optional parts.
future reference may be passed to third party objects,
and these may then fetch the future value. A future
value may be fetched several times, possibly by dif-
ferent objects. Hence, shared futures provide an effi-
cient way to distribute method call results to a number
of objects.
A future variable fr is declared by Fut < T > fr,
indicating that fr may refer to futures which may con-
tain values of type T . The call statement fr := v!m(e)
invokes the method m on object v with input values
e. The identity of the generated future is assigned to
fr, and the calling process continues execution with-
out waiting for fr to become resolved. The statement
await fr? releases the process until the future fr is
resolved. The query statement v := fr.get is used to
fetch the value of a future. The statement blocks until
fr is resolved, and then assigns the value contained
in fr to v. The await statement await e releases
the process until the Boolean condition e is satisfied.
The language contains additional statements for as-
signment, skip, conditionals, sequential composi-
tion, and includes an assert statement for asserting
conditions.
We assume that call and query statements are
well-typed. If v refers to an object where m
is defined with no input values and return type
Int, the following is well-typed: Fut < Int > fr :=
v!m(); await fr?; Int x := fr.get, corresponding
to a non-blocking method call, whereas Fut < Int >
fr := v!m(); Int x := fr.get corresponds to a blocking
method call.
Class instances are concurrent, encapsulating their
own state and processor, similarly to the actor model
(Hewitt et al., 1973). Each method invoked on the ob-
ject leads to a new process, and at most one process
is executing on an object at a time. Object communi-
cation is asynchronous, as there is no explicit transfer
of control between the caller and the callee. A release
point may cause the active process to be suspended,
allowing the processor to resume other (enabled) pro-
cesses. Note that a process, as well as the initializa-
tion code of an object, may make self calls to recur-
RuntimeAssertionCheckingandTheoremProvingforConcurrentandDistributedSystems
481