% to get hardcopy of this lecture, you need the following files (plus the
% normal latex and tex base directories):
%     lecture9.tex:     latex source file
%     macros.tex:      figure macros
%     psfig.tex:       postscript figure (psfig) macro definition
%     sequent.idraw:      first figure, created with idraw
%     conc.idraw:  second figure, created with idraw
%     pocs-header.sty: Principles of Computer Systems lecture latex header
%
% run the following programs:
% latex lecture9
%   -> Creates lecture9.dvi, plus latex intermediate files
% latex lecture9
%   -> Run a second time to get cross-references right
% dvi2ps lecture9 > lecture9.ps
%   -> Merges the figures with the text, can also pipe to lpr
%
\documentstyle[12pt,pocs-header]{article}
\Scribes{Steve Keckler and Andrew Myers}
\Lecturer{Bill Weihl}
\LectureNumber{9}
\LectureDate{October 15, 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
20 & Proving Concurrent Modules Correct  \\
21 & Solutions to Problem Set \#2 \\
Notes & Lecture Notes \#4 \\
\hline
\end{tabular}
\end{table}

Problem Set \#3 is due next Monday, October 22. Reading {\em An Introduction to Programming
with Threads} by Andrew D. Birrell is recommended.

\section{Overview}

This lecture covered the topic of proving concurrent modules correct.
The following areas were discussed:
\begin{itemize}
\item Abstraction Functions
\item Transitions
\item Correctness
\end{itemize}
Handout 20 was the basis for this lecture, which also used the examples of
mutexes and condition variables defined in handout 18.

\section{Abstraction Functions}

Proving an implementation correct involves two steps. First, we define
an abstraction function which maps states in the implementation into
states in the specification. Then we show that every transition that
the implementation takes maps onto a transition in the specification which
preserves the abstraction function.

In the sequential case, the only implementation transitions which we needed
to consider were the exported routines of the module, since these routines
were considered to be atomic (Figure ~\ref{sequent}).
In the concurrent case, the exported routines
are not atomic, so there are many
more implementation states to consider -- basically, every possible
value of the program counter between atomic transitions.

\begin{figure}
\centerline{\psfig{width=5in,figure=sequent.idraw}}
\caption{Abstraction Function for a Sequential Module}
\label{sequent}
\end{figure}

An implementation routine which is being mapped onto an atomic
specification routine will in general have a sequence of
possible program counter values. Each of these program counter values
will map onto one of two possible states in the specification: either
the beginning or the end of the atomic routine (Figure~\ref{conc}).

In figure~\ref{conc}, we see that the states $pc_3$ and $pc_4$
map onto the final specification state, and that $pc_0$ through $pc_2$
map onto the initial state.

\begin{figure}
\centerline{\psfig{width=5in,figure=conc.idraw}}
\caption{Abstraction Function for a Concurrent Module}
\label{conc}
\end{figure}

The mapping from program counter to specification state is not unique;
changing the mapping often entails a change to the abstraction function, however.
Looking at page 2 of handout 20 and page 4 of handout 18, we see that
in the {\tt Mutex.Produce} function, the implementation states $p_1$ -- $p_3$ correspond
to the specification state $P1$ (before the procedure), and $p_4$ -- $p_6$ correspond
to $P_2$.

However, we could choose to alter the mapping so that $p_3$ -- $p_6$ were
mapped to $P_2$.  This change would force us to change the abstraction function.
Before the change, our abstraction function was


\newcommand{\bb}{{\tt b\char94}}
\newcommand{\items}{{\tt b\char94.items}}

    \bb\ $=$ \items

With the change, this abstraction mapping no longer works when the implementation
is in state $p_3$, since the specification is now in a state where \bb\ contains
the new value, but \items\ does not. The new abstraction function would have to
patch things up by mapping \bb\ to the temporary used in the assignment.

\section {Transitions}

There are three types of implementation transitions:

\begin{description}
\item[internal transition]
Internal transitions have no effect on the state of the specification.
The abstraction function maps them into a null transition. The transitions
\( tr', tr'', tr'''' \) in figure~\ref{conc}
are internal transitions.
\item[external transition]
An external transition in the implementation is a single transition which
corresponds directly to a single transition in the specification. Examples
of external transitions are procedure invocations and returns. The transition
$tr$ in figure~\ref{sequent} is an external transition: it maps
to itself.
\item[decisive transition]
A decisive transition in the implementation is one which maps onto a
transition in the specification. It corresponds to the place where
work is being done. In figure~\ref{conc}, \( tr''' \) is
a decisive transition. A decisive transition occurs in the middle of
a series of internal transitions.
\end{description}

\section {Concurrent Proofs}
To prove the {\tt Mutex} module correct, we need to show that the mutex is actually
being used for mutual exclusion -- that one thread is dominant and holds the mutex.
This is an example of the kind of representation invariant that we come across
when proving concurrent modules correct.

While we're proving a concurrent module correct, we often find the need for additional
invariants which are not immediately obvious. In the following assignment,

\begin{verbatim}

     Implementation             Specification

     << b^.items := temp >>     << b^ := b^++t >>

\end{verbatim}

we need to be able to show that {\tt temp} is equal to {\tt b\char94++t}.
This implies
that other threads must not be able to come along and step on {\tt temp } while we
are at $p_3$

Making valid proofs for concurrent modules generally involves making assumptions about
the environment in which the module is contained -- that the parts of the program which
are external to the module do not modify the variables of the module, for example.

\section {Safety and Liveness}

Properties of a program can either be classified as {\em safety} or {\em liveness}
properties. Safety properties are properties which ensure that ``nothing bad ever happens'',
and that if a result is returned, the result will be correct. Liveness properties
specify that ``something good will eventually happen'' -- i.e. some result will eventually
be returned.

These terms can be contrasted with {\em total correctness} and {\em partial correctness}.
Total correctness says that the correct result is eventually returned, which is a
combination of safety and liveness properties. Partial correctness is a safety property:
if the program returns, the answer will be correct.

If we apply these properties to the FIFO buffer example, safety implies that requests
are serviced in FIFO order, and only one thread can be dominant. Liveness, on the other
hand requires that all requests are eventually serviced.

People generally don't worry about liveness, because it isn't as useful for describing
systems as safety. In one sense, liveness is too {\em strong}, because it's usually
difficult to implement and unnecessary. On the other hand, liveness is also too {\em weak},
because it doesn't guarantee that a program will return within any useful amount of time.
A requirement that a result be returned within, say, 10 minutes would actually be a
safety property rather than a liveness property!

Fairness, which is the requirement that no starvation occur, is a liveness property.
Nothing in the FIFO buffer example, for example, says that a process making a request
will ever be serviced.

Two other concurrency pitfalls which fall into the liveness camp are deadlock and livelock.
Livelock is a situation in which a set of threads spin uselessly trying to acquire some
mutual resource, when the timing of the system is such that they manage to step on one
another repeatedly. Unlike deadlock, livelock is not necessarily a stable situation; if
one of the threads is swapped out, the livelock may disappear because the competition
is halted temporarily.

\section{Concurrency Mechanisms}

The programming community has tended to converge on a fairly stable set of concurrency
mechanisms in the last few years. The two dominant paradigms are
\begin{itemize}
\item Shared data, mediated by mutexes and condition variables
\item Message passing
\end{itemize}

\end{document}
