\documentstyle[12pt,pocs-header]{article}
\Scribes{Andr\'e DeHon and John Kubiatowicz}
\Lecturer{Bill Weihl and Butler Lampson}
\LectureNumber{3}
\LectureDate{September 19, 1990}
\include{macros} % used to include figures in this document
\begin{document}
\MakeScribeTop

\section{Administrivia}

\begin{table}[h]
\centering
\begin{tabular}{|c|l|} \hline
\multicolumn{1}{|c|}{Handout} &
\multicolumn{1}{c|}{Title} \\ \hline
7 & Verifying Loops \\
8 & Simple File System Implementation \\
Notes & Notes for Lecture 1 \\
\hline
\end{tabular}
\end{table}
Correction for Handout 8.  Change all instances of ``Length(seq)''
to ``seq.size''.

\section{Ways of Viewing Functions in SPEC}

Since the results of operations in a computer system often depend on
state within the system, we cannot always view these operations as
acting on a single input to produce a single output.  In most cases,
we must consider the history of events up to the invocation of this
operation.  Thus, what we are calling ``functions'' in our system can
be viewed in one of two equivalent fashions:
\begin{enumerate}
\item {\bf Relations:}  A function describes a relation between input
sequences and output sequences -- i.e. a set of pairs of the form
(Input Sequence, Output Sequence)
\item {\bf State Machine:} The function describes actions
and transitions on states within a (possibly infinite) state
machine.
\end{enumerate}
These two descriptions are equivalent, but occasionally, one will
prove easier to reason about than the other.

\subsection{SimpleMemory State Machine Example}

Consider, for example, our {\tt SimpleMemory} specification [Handout
4].  Here, the states are mappings, $A\rightarrow D$.  The state
transitions occur as a result of each {\tt Initialize}, {\tt Read},
{\tt Write}, or {\tt Swap} operation.  Figure~\ref{sm} illustrates
states and transitions for the {\tt SimpleMemory} assuming
$A=\{1,2,3,4\}$ and $D=\{a,b,c\}$.  A {\tt read} returns a value
without changing the state.  A {\tt write} may change the state.
Initialization resets the mapping to a state which is specified by its
argument.

\begin{figure}
\centerline{\psfig{figure=simple-mem.ps,height=2in}}
\caption{State View of the {\tt SimpleMemory} Module}
\label{sm}
\end{figure}

\begin{figure}
\centerline{\psfig{figure=simple-cache.ps,height=2.3in}}
\caption{State View of the {\tt SingleCache} Module}
\label{sc}
\end{figure}

\subsection{SingleCache Examples}

We can view the {\tt SingleCache} implementation [Handout 4] in much
the same way.  The states are somewhat more complicated, since they
include both the values in main-memory and in the cache.
Figure~\ref{sc} shows some states and transitions for a {\tt
SingleCache} memory system assuming $A=\{1,2,3,4\}$ and $D=\{a,b,c\}$.
In this diagram, {\tt sz} represents the size of the cache at
initialization.  Note the non-determinism in operations that require
cache-entry eviction or initialization.

It is perhaps worth noting that {\tt SingleCache} is not {\bf
strictly} and implementation of {\tt SimpleMemory} since the {\tt
Initialize} function has a different interface.  However, this is a
minor detail and could be dealt with by defining an initialization
function that set the cache to a particular size.

\section{Proving that an Implementation is Correct}

We are interested in proving that our implementations satisfy their
specifications ({\it e.g.\ } proving {\tt SingleCache} is an
implementation of {\tt SimpleMemory}).  To achieve this task, we make
use of an {\it abstraction function} and {\it representation
invariant} in arguments of correctness.

\subsection{Abstraction Function}

The {\it abstraction function} maps the states of the implementation
into the states of the specification
($S_{\mbox{specification}}\leftarrow S_{\mbox{implementation}}$).  In
general this is a many to one mapping with many states in an
implementation mapping to a single state in the specification.  The
abstraction function is an important tool for arguing the correctness
of our implementation as we shall see in the following section.

\paragraph{Example}  Figure~\ref{sm-sc} shows the mapping of some {\tt
SingleCache} states to a {\tt SimpleMemory} state.  Clearly, many
states of the implementation map to the same abstract state.

\begin{figure}
\centerline{\psfig{figure=sm-sc.ps,width=4in,height=2.4in}}
\caption{{\tt SimpleMemory} Abstraction Function}
\label{sm-sc}
\end{figure}

\subsection{Proof Strategy}

To argue that an implementation satisfies its specification, we
construct an inductive proof between the behavior of the abstract
state machine of the specification and the concrete state machine of
the implementation.

\begin{itemize}

\item {\bf Base} For the base case in the induction, we must show that
all of the possible initial states in the implementation correspond to
the initial state(s) in the specification.  This argument is made
based on the mapping between concrete and abstract states given by the
abstraction function.

\item {\bf Induction}  Once the initialization is shown correct, we
need to show a correspondence between all non-initializing state
transitions of the implementation and the specification.  That is, we
must show commutativity between the abstraction function and the state
transitions:
\begin{center}
\begin{tabular}{c}
     ${\cal AF}(S_0) = A_0$ \\ ${\cal AF}(S_1) = A_1$ \\
\hline
    $Op_{spec}({\cal AF}(S_0))$ = A1 $\Leftrightarrow$ 
    ${\cal AF}(Op_{impl}(S_0))$ = A1
\end{tabular}
\end{center}    
Figure~\ref{comm} shows this commutativity relation diagrammatically.
Figure~\ref{comm-ex} shows an example of the commutativity using our
{\tt SimpleMemory} and {\tt SingleCache} examples.
\end{itemize}

\begin{figure}
\centerline{\psfig{figure=commutivity.ps,height=2in}}
\caption{Commutativity Relation Diagram}
\label{comm}
\end{figure}

\begin{figure}
\centerline{\psfig{figure=comm-example.ps,height=3in}}
\caption{Commutativity Example}\label{comm-ex}
\end{figure}

\subsection{Representation Invariant}

For the {\tt SimpleMemory} example, the steps given in the previous
section are sufficient to prove correctness.  Often, however, there
are states of the implementation which do not make sense if we attempt
to map them to abstract states.  We can ignore these states if they
never arise in practice.  To argue correctness in these cases, we
introduce a {\it Representation Invariant}.

The invariant states properties of data structures in the
implementation which are true between procedure calls.  That is, it is
true on entry and exit but not necessarily in the body of a procedure.
It restricts the class of objects we must consider when arguing about
correctness (for example, it might rule out the extra states mentioned
above).  In order to make use of the representation invariant, we must
first prove that it is, in fact, invariant.  To do this, we:

\begin{itemize}
\item {\bf Base} Show that all initial states satisfy the invariant.
\item {\bf Induction} Show that all transitions preserve the invariant.
\end{itemize}

\paragraph{Example} In the {\tt HashMemory} example [Handout 6], the
implementation depends on the fact that address/data pairs are stored
in the correct bucket (according to the the hash function).  Once we
have shown that the implementation guarantees the invariant ({\it
e.g.\ } an address/data pair is always in the correct bucket), we
can demonstrate correctness by considering only those states and
transitions which satisfy/maintain the invariant.

\section{Simple File System Example}

In moving to a larger example, we adopt a more informal approach.
Since there is a direct correlation between confidence and the amount
of work required for verification, we have to choose our methods
appropriately.  The strictest possible approach would be needed for
the design of a nuclear reactor or aircraft control system.  A pet
project or the initial phases of a design, on the other hand, could
employ a much more informal approach.

\subsection{Filesystem Model}

In developing a specification for a file system, we will start with a
model of the hardware; this model assumes a certain level of
functionality and has certain performance characteristics.

\subsubsection{Functionality}
Our assumptions of functionality, which directly affect the
specification can be stated as follows:
\begin{itemize}
\item Disk storage is stable across crashes.  We assume that the data
will not become corrupted (i.e. no disk errors), that the disk head
won't crash, etc.
\item The disk is organized as a series of blocks.  Data must be read
and written in blocks.  Further, {\em block writes are atomic}.  A
crash or power failure will not cause partial blocks to be written
(this is not all that unreasonable -- this requires sufficient
capacitance in power supply and an intelligent controller).
\item The processor is allowed to crash at any time.  Further, it has
the {\em fail stop} property, i.e. it fails by stopping {\em before}
starting an incorrect computation.  The fail-stop property can be
enforced by detecting errors and halting appropriately.  Note that in
SPEC, crashes are modeled by placing a {\tt crash} procedure in each
module.  This procedure must destroy volatile state (such as RAM,
etc), then execute appropriate recovery procedures.
\end{itemize}

\subsubsection{Performance}

Concerns of performance are usually orthogonal to concerns of
correctness; for instance, a vacuum-tube computer can correctly
implement the specification for a workstation (while giving abysmal
performance for X-windows).  Further, when performance is part of a
specification, it is often very difficult to prove that an
implementation is correct.  Statements about performance (e.g. cache
miss rate) are often statistical in nature and dependent upon many
factors.  Thus, we will attempt to optimize performance in our
implementations without adding performance constrains to the
specification.

For the simple file system, we will adopt two very simple assumptions
with respect to performance.  The first is that the disk and processor
have speeds which differ by a factor of about $10^{5}$; the processor
can execute an instruction in $\approx$100ns, while the disk takes
$\approx$10ms to access a random byte from the disk.  The second
assumption is that we can lower this differential significantly by
accessing sequential bytes: the disk can transfer a sequential string
of bytes at a rate of one byte every 500ns.

\subsection{File System Specification}

See Handout 10 for the simple specification of the file system and
disk (this supersedes Handout 8).  The filesystem has a directory
which is a mapping from file names to files.  Files are sequences of
bytes.  The disk is a sequence of blocks, each of which has 1024 bytes
of data.

\subsection{File System Implementation}

Our implementation of the file system addresses the tremendous speed
differential between disk and processor.  We have several heuristics
for doing this:
\begin{itemize}
\item Avoid disk accesses as much as possible.  Caching is one of the
best methods for doing this.  Various cache replacement and prefetch
policies can be considered to optimize use of the cache.
\item Attempt to do sequential accesses.  The difference in time
between reading a random sequence of blocks and reading a sequential
sequence of blocks is significant.  Consequently, we would like our
implementation to place the blocks of a file sequentially on the disk
whenever possible.  Also, we can arrange for dirty blocks in the disk
cache to be written back in an order which is ``more sequential'' than
the order in which these blocks were written.
\end{itemize}
The next lecture will examine the implementation further.

\end{document}
