\documentclass[11pt,twoside]{article}
\usepackage{macros-course-lectures}
%\renewcommand{\bar}[1]{\overline{#1}}

\begin{document}

\lecture{notes3}{Algorithms for Unbounded Concurrency}{September 12, 2002}

\begin{tabbing}
XXX\=XXX\=XXX\=XXX\= \kill
Bring:  \\
Extra notes for scribe. 
\end{tabbing}

\noindent
Today's papers: \\
Merritt, Taubenfeld \\
Gafni, Merritt, Taubenfeld \\
\\
Next time (actually, this will be next Thursday---we're interrupting
the normal flow with a class on Byzantine quorum systems---see handout
4. \\
*** Afek, Greenberg, Merritt, Taubenfeld, Computing with Faulty Shared
Objects \\
*** Jayanti, Chandra, Toueg, Fault-Tolerant Wait-Free Shared Objects

\section{Some residual remarks on adaptive algorithms for renaming,
and other problems}

\noindent
Last time, we discussed the renaming problem, from Moir-Anderson and
Attiya-Fouren.  \\
Main result of AF gives adaptive, $6k-1$-renaming with time $O(k log k)$. \\
One-shot.  MA also define long-lived, but their algorithm isn't
adaptive (time complexity depends on $N$, the size of the identifier
space). \\
\\
Afek and Merritt have improved the main result of AF to obtain
$2k-1$-renaming with time $O(k^2)$ (still one-shot). \\
Their algorithm is based on the AF algorithms. \\
In particular, they use a version of the AF shrinking network
algorithm in which they depend more on using the lower right part of
the network. \\
Too complicated to present here. \\
\\
Q:  What about long-lived renaming? \\
Q:  Are versions of these algorithms useful in distributed systems? \\
\\
Today:  I'll say a few words about a couple of other papers I read in
this area, but didn't ask you to read. \\
Then go on to the new material on computing with infinitely many
processes.

\subsection{Attiya, Fouren II, adaptive long-lived $(2k-1)$-renaming}

\noindent
Polynomial and Adaptive Long-lived $(2k-1)$-renaming. DISC 2000 \\
\\
Long-lived renaming:  Processes can request (getName) and return
(releaseName) names repeatedly. \\
They obtain an adaptive $2k-1$-renaming algorithm with $O(k^4)$ step
complexity. \\
\\
Now $k$ is not the total number of participants that ever appear. \\
Rather, they define $k$ in terms of maximum concurrency, specifically, 
{\em point contention}: \\
$k$ is an upper bound on the number of processes that are ever
simultaneously outside of their ``remainder regions'', when they are
not involved with requesting, holding, or releasing a name. \\
\\
They use another building block called ``sieve''. \\
A sort of arbiter. \\
Can be ``reserved'' for a process based on suggestions by processes
that invoke the sieve. \\
Seems complicated, not a clean abstraction. \\
\\
(The renaming algorithm works by having a process choose a timestamp,
which is used for priority. \\
Then it tries to reserve sieves for itself and processes with smaller
timestamps.  \\
It does this until it finds that some sieve has been reserved for itself. \\
Gets a name from that sieve.) \\
\\
What's interesting:  The very strong result statement.

\subsection{Afek, Stupp, Touitou}

\noindent
Long-lived, adaptive collect with applications.  \\
\\
They define Collect and ActiveSet problems, and give
long-lived, adaptive algorithms for them. \\
These appear to be useful building blocks, for transforming other
long-lived algorithms into adaptive versions. \\
\\
E.g., Collect: \\
Supports operations Write, Collect. \\
Abstract spec:  An automaton whose state contains $N$ single-writer,
multi-reader registers, one for each process id. \\
To Write, a process writes its value in its own register. \\
To Collect, it reads the $N$ registers one at a time, in any order,
and returns all the values read (set of (process,value) pairs), for
values that have ever been written. \\
\\
Their implementation is adaptive, according to the point-contention
definition of AF. \\
It uses multi-writer, multi-reader registers, each of which can hold
as many as $N$ values. \\
It uses an $O(N^2)$ element linear array in which processes store
their values. \\
In order to make it adaptive, processes try to capture an element as
close to the beginning of the array as possible, for writing their
values. \\
Also, they work on ``bubbling up'' their values to the beginning of
the array. \\
Many values can be put into the first entry. \\
Somewhat complicated. \\
\\
Applications:  \\
Many long-lived algorithms follow a discipline where they alternately
write their own single-writer register and read everyone's register. \\
Can transform such algorithms using a Collect service: \\
Do the writes to the Collect service; replace all the reads with Collects. \\
\\
Sometimes this yields an adaptive version. \\
Examples:  The standard atomic snapshot algorithm. \\
An $\ell$-exclusion algorithm. \\
\\
Warning:  The adaptive version isn't adaptive according to the usual
point-contention definition. \\
Rather, according to a weaker definition of ``interval contention''. \\
In this definition, contention is calculated as the maximum number of
operations whose intervals overlap any single operation. \\
(diagram) \\
\\
Q:  Why is this? \\
A:  During a snapshot, $k$ processes perform updates, one at a time. \\
The snapshot's time will depend on this $k$ (because it can repeatedly
restart every time there's a change).  But the point contention is
only 2.) \\
\\
Suggests that point contention isn't such a robust notion. \\
What is the right notion? \\
\\
\\
ActiveSet: \\
A more basic service, which simply detects who appears to be currently
active. \\
It can be used to implement a variation of a Collect object: \\
Use ActiveSet to determine who seems to be active, and only read those
processes' individual registers. \\
\\
ActiveSet supports operations join, leave, and getSet. \\
Abstractly, the service has $N$ single-writer multi-reader Boolean
flag registers, one per process, initially all false. \\
join:  process writes true in its flag register. \\
leave:  writes false. \\
get-set:  goes around reading in any order, returns the set of
processes for which the flag is true. \\
Nice abstraction. \\
\\
They produce an algorithm with a sort of ``adaptivity'' property,
though not in terms of point contention. \\
Rather, the time cost depends on the size of the current set, that is,
they count in $k$ all those that have invoked a join and haven't
received a leave-ack. \\
Not just on the number of processes currently engaged in join and
leave operations. \\
\\
Example using ActiveSet:  Lamport's bakery algorithm, p. 9, which they
make adaptive by using ActiveSet. \\
This (I think) uses the point contention definition, based on the
interval when the process isn't in its remainder region. \\
\\
Significance:  Ways of measuring contention---what are good, robust
measures? \\
Some interesting building blocks. \\
Adaptive algorithms for these.

\subsection{Attiya, Fouren, Gafni, adaptive collect with applications}

\subsubsection{Overview}

\noindent\\
This time, adaptive refers to total contention ($k =$ total number
that have ever participated). \\
\\
Collect problem formulated slightly differently from before:  a list
of properties rather than an automaton spec. \\
\\
Write:  Write value in your own location. \\
Collect:  Returns a view, which behaves as if it resulted from a global
read of all potential locations. \\
\\
Their implementation of this object incurs latency: \\
O(k) for the initial write by each process \\
O(1) for subsequent writes \\
O(k) for collect. \\
\\
Nice. \\
Weaker than Afek, Stupp, Touitou because they don't try to make it
adaptive to point contention, just total contention. \\
Thus, they don't ``bubble up'' values;  just leave values where they
are, once written. \\
\\
Show how to use adaptive Collect to implement adaptive one-shot atomic
snapshot object, ``immediate snapshot'' object. \\
Use these to get a (2k-1) adaptive renaming algorithm with $O(k^3)$
latency. \\
Not as small complexity as Afek-Merritt, but more modular. 

\subsubsection{The Collect implementation}

\noindent
Simple.  Uses complete binary tree of Lamport (or Moir-Anderson)
splitters (now using ``left'' and ``right'' instead of ``right'' and
``down''). \\
$n$ nodes, where $n$ is an upper bound on the number of potential
participants. \\
\\
Each splitter has X and Y plus other variables: \\
mark, which allows the node to be marked (when it is first visited) \\
id, which holds the identity of a process that stops at the node \\
value, which holds the latest value written by that process. \\
\\
The first time a process writes, it obtains a node in the tree. \\
It writes there and remembers the node. \\
On later writes, it just goes directly to its node, overwrites. \\
\\
To obtain the node in doing the first write, a process traverses the
tree from the root. \\
At each node, it enters the splitter, and marks it. \\
If outcome is ``stop'', that is its node (it's guaranteed to be the
only process that ever stops there. \\
If outcome is ``left'' or ``right'', it goes down that branch and
continues. \\
\\
To do a collect, any process does a DFS of the tree, visiting the
marked nodes only.  \\
It collects all the (process, value) pairs at all the nodes, and
returns that as its view. \\
Satisfies the requirements of the Collect object. \\
\\
Time bounds: \\
\\
Note:  The marked nodes are solid from the top of the tree. \\
\\
Claim:  At most $2k-1$ nodes are ever marked (by at most $k$ processes
that write). \\
\\
Proof:  Consider a finite execution in which at most $k$ processes
have begun write operations. \\
Let $S$ be the set of vertices that are marked in that finite execution. \\
Show $|S| \leq 2k-1$. \\
\\
Order $S$ in in-order: for every marked vertex $v$, vertices in left 
subtree are ordered before $v$, vertices in the right 
subtree are ordered after $v$. \\
\\
Color the vertices: \\
Grey:  If both its children are marked. \\
Black:  If one or zero of its children are marked. \\
\\
Claim:  In the (in-order) ordering of $S$: \\
1.  The first vertex is black. \\
2.  The last vertex is black. \\
3.  No two consecutive vertices are grey. \\
\\
Proof: \\
1.  If the first vertex were grey, its left child would be marked. \\
But then it isn't numbered first in in-order. \\
2.  Symmetric. \\
\\
3.  Assume two consecutive vertices in in-order say $v_i$
and $v_{i+1}$, are grey. \\
If they are incomparable, then their LCA is between them in in-order,
so they aren't consecutive. \\
So one is an ancestor of the other.  WLOG, say $v_i$ is an ancestor of
$v_{i+1}$. \\
Then by in-order, $v_{i+1}$ must be in the right subtree of $v_i$. \\
But then $v_{i+1}$'s left child $w$ can't be marked:  if it were, it
would be between $v_i$ and $v_{i+1}$ in in-order. \\
But that contradicts the assumption that $v_{i+1}$ is grey. \\
\\
Claim:  With each black vertex, we can associate some process that
either stops their or fails there. \\
\\
Proof:  The node gets marked, so someone arrives.  \\
Not all the entrants to the node can go to one child, by the splitter
definition. \\
Since no one goes to the other child, someone must stop here. \\
\\
So, this means that there are at most $k$ black vertices. \\
That implies at most $2k-1$ total marked vertices. \\
\\
This linear bound gives a linear bound on collect operations. \\
Also, the first write incurs a linear time bound, and the rest are
constant. \\
(Not logarithmic---this isn't balanced.)

\subsubsection{Applications}

\noindent
As Afek et al., they plug their adaptive collect into other algorithms
and speed them up. \\
\\
Ex:  Atomic snapshot:  Recall the def. from my course.  \\
But here they modify it a bit, using only a single operation that
both writes a value and returns a view. \\
The effect is as if the view were read all at once.  \\
However, they don't require the write and the view snapshot together
to be atomic---there can be interleaving between the two. \\
\\
Their algorithm is a modification of an $O(n log n)$ snapshot
algorithm of Attiya and Rachman, modified to make it adaptive by:
using a tree of successive-increasing-size binary trees,
and using adaptive collect. \\
(This trick is similar to one we saw last time, using a sequence of
range-independent renaming algorithms to get an adaptive renaming
algorithm.) \\
Get latency $O(k log k)$. \\
LTTR. \\
\\
Ex:  Immediate snapshots.  Like snapshots, with upscan, except that
the entire upscan operation looks atomic.  (I think---their definition
isn't expressed this way, but in terms of axioms.) \\
They give a somewhat-complicated algorithm, with latency $O(k^2)$. \\
\\
Ex:  One-shot, $2k-1$-renaming, with $O(k^3)$ latency. \\
Not quite as good as Afek-Merritt, but done in a stepwise, modular
fashion. 

\subsection{Discussion:  Renaming, Collect, Lattice Agreement, in
distributed systems}

\noindent
Useful?  How are the problems different?  How to implement?  

\section{Merritt, Taubenfeld}

\noindent
Computing with Infinitely Many Processes. \\
Longer version, written after the conference version, DISC 2000. \\
Looks like it's submitted for journal publication, or about to be. \\
\\
Warning:  This is less polished than the other papers we've seen so
far---those are final versions of journal papers. \\
Note the date.  That's because I found some errors reading it, and
they did some quick fixing-up.

\subsection{Overview}

\noindent
Paper considers leader election, mutual exclusion, and consensus. \\
\\
They consider both read/write registers and stronger primitives, like
test-and-set, semaphores, etc. \\
I'll focus on read/write. \\
\\
Allow infinitely many potential participants. \\
\\
So far, nearly all the papers we have covered have assumed a finite
bound of some $n$. \\
Exception:  Friedberg-Peterson m.e. algorithm with two weak semaphores.  \\
They assume no bound on the number of participants. \\
Still get no-lockout, even if more and more processes keep arriving
while a process $i$ is active. \\
\\
Here, individual results assume partial info about the number of
participants, e.g., at least $c$ will participate. \\
\\
Lots of results (see overwhelming-looking but helpful charts on p. 4
and 5).  \\
Hard to get a handle on what's important. \\
I'll cover: \\
A lower bound for leader election, Theorem 2.1. \\
Theorem 2.4 (election with 2 registers) \\
Theorem 3.2 (adaptive starvation-free mutual exclusion.) \\
And Theorem 5.2, which relates back to Friedberg-Peterson. \\
\\
All their results assume no failures. \\
\\
% You could use Theorem 2.5 (election with 1 register) as an extra-credit
% assignment,.  
% Theorem 3.3 (non-adaptive starvation-free mutual exclusion for
% infinite concurrency using only bits) is pretty simple, and can be
% used to emphasize that the algorithm of Theorem 3.2 is adaptive.
%
Something funny:  \\
Not only don't they assume an a priori bound $n$ on the number of
processes, they also say they allow infinitely many processes to be
active at the same time. \\
This is hard to understand. \\
I understand infinitely many potential participants, no bound
on how many may participate in the course of one execution,
infinitely many allowed over the entire (infinite) execution. \\
But don't like to think about infinitely many processes taking steps
together---an infinite amount of activity happening at once! \\
\\
MT's real aim is to design algorithms that scale well. \\
I don't think allowing infinite concurrency at one point in time is
crucial. \\
It certainly doesn't arise in any of the main results I've selected. \\
So I'll ignore this. \\
\\
Concurrency level:  \\
They consider upper bounds on the point contention (number of processes that
may be active simultaneously):
\begin{itemize}
\item
finite:  There is a finite upper bound $c$ on the number of processes
that are simultaneously active. \\
This bound holds for all runs.
\item
bounded:  In each run, there is a finite upper bound on the number of
processes that are simultaneously active.  The bound may be different
in different runs. 
\item
unbounded:  In each run, at each point, only finitely many processes
are active, but there is no bound. 
\item
infinite:  (The one I don't like)  Infinitely many may be
simultaneously active.  They talk about ``group steps'' involving
infinite amount of activity.
\end{itemize}

\noindent
Participation level: \\
They also consider lower bounds on how many processes {\em must\/}
participate: \\
$\ell$-participation:  Restrict attention to (fair) executions in
which at least $\ell$ distinct processes participate at some point. 
(So, a process can wait for that many.) \\
\\
Actually, they define upper bounds on participation also, but seem not
to use them in the paper.  So I'll ignore. 

\noindent
Properties considered: 
\begin{itemize}
\item
Adaptivity:  As we have been using the term.  Time complexity of
operations are a function of the actual concurrency.  Point
contention. 
\item
Symmetric:  Processes have uids, but are otherwise indistinguishable.
Uids are from a set with no operations except comparison for equality
only.  No ordering.  Cannot use uids to index registers. 
\end{itemize}

\noindent
Key results (Read/write registers):
\begin{enumerate}
\item
Leader election can't be done with finite shared space.  Theorem 2.1.

\item
Leader election and consensus can be solved with two unbounded-size
registers, assuming the number of participants is at least $c$
and concurrency (number of concurrent participants) is at most $c$. \\
Their algorithm is adaptive and symmetric.  Theorem 2.4.

\item
Mutual exclusion needs an infinite number of registers (Burns, Lynch).
They give an adaptive, symmetric algorithm for mutual exclusion, using
an infinite number of infinite-size registers, with no required
participation, and unbounded concurrency.  Theorem 3.2.
\end{enumerate}

\subsection{Impossibility result for leader election with finite space}

\noindent
Can make rather strong assumptions. \\
Assume a fixed number $c > 1$ of participants. \\
We don't assume that the process identifers are all
known ahead of time---this proof is based on an infinite
universe of potential participants.  \\
We just know the exact {\em number\/} that will participate. \\
\\
In their version of the leader election problem, they just require
that exactly one participant be elected as leader.  \\
The others don't need to know who he is. \\
\\
Proof:  By contradiction.  Assume finitely many registers of finite
size.  We'll construct a bad execution. \\
\\
WLOG assume that each process that participates continues taking steps
forever (can add dummy steps). \\
\\
For each process $p$ in the (potential) universe, we may construct an
execution $solo(p)$, in which p runs alone forever. \\
He does not need to get elected, because that is only required if $c$
processes participate.  However, the run does something. \\
This execution may be infinite. \\
\\
Now, for each $p$, in $solo(p)$, some state of the shared memory must
repeat infinitely many times. \\
Choose such a state, call it $repeat(p)$. \\
\\
Also, for each $p$, consider the sequence of states of the shared
memory that appear up to the {\em first\/} occurrence of $repeat(p)$,
say $x_0,x_1,\ldots x_k$. \\
Let $sign(p)$ be a reduced version of this that is obtained by
cutting out loops in a systematic way:  \\
Start with the first state $x_j$ that reappears and look for the
latest reappearance.  Delete everything after $x_j$ up to and
including that latest reappearance. \\
Repeat this starting after the deleted sequence. \\
\\
The result, $sign(p)$, has no repeated states. \\
Thus, its length is bounded by the number of possible states of the
shared memory. \\
Moreover, by the way it was constructed: \\
1. The first state is the start state of memory. \\
2. The last state is $repeat(p)$. \\
3. Each state differs from the next in the value of exactly one shared
variable. \\
\\
Since there are only finitely many such sequences and finitely many
memory states, there are infinitely many processes that have the same
$repeat(p)$, call it $s$, and same $sign(p)$, call it $\gamma =
y_0,y_1,\ldots,...$. \\
\\
Now, they show that they can take any group of $c$ of these processes
and run them in a compatible way, interleaving them so that they all
think that they are running alone.  \\
But since there are really $c$ of them, the requirements of the
problem say that exactly one of them must elect itself the leader. \\
This sounds dangerous, since it doesn't see any evidence at all of the
other processes...\\
\\
To get an actual contradiction (the authors don't do this work): \\
Let $p1,p2,\ldots,pc$ be $c$ processes, and say WLOG say $p1$ elects
itself the leader.  \\
No others do. \\
Then run $p1,\ldots,p(c-1),p(c+1)$.  \\
Again, $p1$ elects itself the leader (looks the same), and no others
do. \\
Now consider $p2,\ldots,pc,p(c+1)$.  \\
None of these elect selves (looks same). \\
\\
Now, how to construct the compatible runs? \\
The main idea is to maneuver the shared memory to state $s$ and
simultaneously get every process to the point where state $s$ first
occurs in its solo run. \\
Once this is done, we can let the processes take turns running, each
starting from $s$ and ending in $s$. \\
Then all can run forever, thinking they are alone. \\
\\
The trickiest part is the initial portion---getting the processes to
state $s$. \\
For this, we use the fact that they all have $sign(p) = \gamma = y_0,
y_1,\ldots$. \\
We proceed step-by-step through $\gamma$, keeping the processes
synchronized. \\
Start with everyone in their start states, the memory in $y_0 =
x_0$. \\
\\
Now start with $p_1$ and let it run just up to the point in its own
execution where it last leaves the memory in state $y_0$.
At that point, it is about to do a write that would send the memory
to $y_1$. \\
This may involve doing a lot of reads and writes, but the final memory
state gets back to $y_0$ again. \\
(Any intervening loop was cut out of $sign(p_1)$.) \\
And $p_1$ has done any reads that precede the actual write step that
would produce $y_1$. \\
Thus, $p_1$ is poised, ready to write the variable that changes in
going from $y_0$ to $y_1$. \\
\\
Now run $p_2$ similarly, to get it to the point in its own execution
where it last leaves the memory in state $y_0$. \\
Then it is also poised, about to write the same variable, the one that
changes in going from $y_0$ to $y_1$.  \\
Note that it is about to write the same value as $p_1$, because the
state $y_1$ determines this new value. \\
Repeat for all the other processes. \\
\\
So, now they are all poised to write the same thing into the same
variable, to convert $y_0$ to $y_1$. \\
Now we'll move them along to where they are about to do the write to
produce $y_2$. \\
First run $p_1$:  it has to start with its (blind) write, which
changes the memory state to $y_1$.  \\
Then run it from $y_1$, possibly back to $y_1$ again, and then any
reads, and stop it just before it makes the transition from $y_1$ to
$y_2$. \\
\\
Now run $p_2$.  \\
Note that it starts in $y_1$, whereas it expects to start in $y_0$.  \\
But (this is the key) $y_0$ and $y_1$ differ only in the value of one
variable, and that's what $p_2$ is about to write anyway (with the
right value).  \\
So, once $p_2$ does that first write, $p_2$ can't tell that it didn't
start from $y_0$. \\
Let $p_2$ reach the point where it is about to convert the memory from
$y_1$ to $y_2$. \\
Repeat for the other processes. \\
\\
Repeat for all steps. \\
Eventually gets the memory to state $s$, and everyone to where they
would be when they first get the memory to state $s$ in their solo
executions.

\subsection{Algorithms for leader election and consensus with at least
$c$ participants and at most $c$ concurrency}

\noindent
Now assume that we have at least $c$ participation and at most $c$
concurrency.  \\
This isn't the same as saying exactly $c$ participants. \\
\\
As before, we don't know ahead of time who the participants are. \\
First, Theorem 2.2 describes a nonadaptive solution to leader election
with an infinite number of read/write Boolean registers. \\
\\
It's also not symmetric.  In fact, they assume that process indices are
natural numbers starting from $1$, and they use the indices for
indexing registers. \\
\\
Proof:  Use Boolean registers $b[i]$, where $i$ is a natural number
$\geq 0$.  All initially $0$. \\
Process $i$:  First reads $b[0]$. \\
If $1$, knows leader already elected and terminates. \\
Otherwise, process $i$ sets $b[i] := 1$. \\
Then scans the array starting from $b[1]$ until it notices that $c$
bits have been set (may have to scan repeatedly, dovetail). \\
Once it sees $c$ bits have been set, it sets $b[0] := 1$. \\
If it sees that its own id is the smallest among those $c$, elects
self leader. \\
\\
\\
Note that the $c$ bound on concurrency implies that the $c+1$st
process doesn't arrive until one of the first $c$ participants
leaves. \\
But no one leaves until $b[0]$ has been set to $1$. \\
That means latecomers won't ever set their bits, and so won't confuse
anyone else in determining if they are the leader. \\
\\
This result relies heavily on participation from at least $c$
processes, though it does not depend on knowing a priori which these
are. \\
\\
Not adaptive:  The time complexity isn't just a function of the number
of total participants, or the number of concurrent participants.  \\
Rather, it depends (at least) on the maximum identifier of one of the
first $c$ participants. \\
Maybe worse. \\
\\
Now jump to Theorem 2.4. \\
This gives an election algorithm for at least $c$ participants, at
most $c$ concurrency, which is both adaptive and symmetric. \\
Furthermore, it uses only two read/write shared variables. \\
Both of unbounded size. \\
\\
Much more interesting. \\
\\
Adaptive here means that the time bound depends only on the number of
actual participants.  Start counting time after all the required $c$
participants have begun. \\
\\
Registers: \\
Union, holds a set of at most $c$ process identifiers. \\
(Leader,Marked), holds a process id and a flag. \\
\\
The first $c$ participating processes use Union to collect all of the
first $c$ participants' identifiers. \\
Each such process $i$ executes a loop where it reads Union and
compares it to the set of ids it already knows locally (in
$local-union-2$, which is initialized to $\{ i \}$). \\
On each comparison, it updates both Union and lu2 to be the
union of the information read and the local info remembers. \\
(It doesn't write Uniont if it has nothing new to write.) \\
This phase ends when a process sees $c$ different ids in Union. \\
\\
The second variable holds an identifier of a proposed leader, and a
Boolean mark.  \\
When the mark is set to $1$, it means that the leader has been chosen
(and it is the process whose id is in the Leader field). \\
The leader chosen is simply the last participant to write the Leader
field. \\
\\
By the concurrency bound, the $c+1$st participant doesn't enter until
after one of the first $c$ leaves. \\
By then, Marked is set to $1$. \\
So no ``latecomer'' enters the main loop, modifying Union, etc. \\
They just read the final result. \\
(This mechanism is similar to the use of $b[0]$ in the simpler
nonadaptive algorithm above.) \\
\\
Go over code, p. 9 \\
\\
Start by checking Marked.  If 1, skip the loop and return. \\
If 0, engage in the main loop. \\
Repeatedly read Union into $local-union-1$, check to see if it contains
at least $c$ elements.  \\
If so, done. \\
If not, set both Union and $local-union-2$ to the union of your info and
what you've read. \\
Repeat. \\
\\
If at any time, you see that Marked is already set, you stop doing the
loop. \\
\\
Correctness:  \\
\\
No more than one leader elected: \\
This is written to actually return the leader. \\
(Technically, we only need to tell the leader itself.) \\
It suffices to show that no two processes return different leaders. \\
\\
Claim that only the first $c$ participants can write into Leader. \\
That's because the next participant doesn't arrive until after Marked
has been set to $1$ (concurrency limitation). \\
And then any latecomers skip the assignment to Leader. \\
\\
Claim that the last of the $c$ participants to write to Leader is the
only one that can be returned as a leader: \\
By the time the first process gets past the loop, all $c$ early
processes have written to Union, which means they have already set
Leader. \\
After that, Leader no longer changes. \\
So any process that returns gets its return value by reading the
Leader field after this point, so gets the same value. \\
\\
Termination: \\
The argument as written isn't so clear. \\
Here's another attempt. \\
\\
Basically, suppose someone doesn't terminate. \\
If Marked is ever set to 1 he would terminate, so this never
happens---Marked stays 0 forever. \\
Eventually all the $c$ processes arrive. \\
If any of them got past the loop, then eventually the Marked bit would
be set, so it must be that all $c$ continue looping forever. \\
We have to argue that eventually they all learn each other's
information, by their discipline of reading the Union variable and
writing back what they know. \\
\\
Suppose not.  Then eventually all the lu2 variables stop
increasing (they can only increase), in a situation where not everyone
knows about everyone. \\
After this point, no one ever learns anything new by reading the Union
variable (or the lu2 variable would increase). \\
So from that point on, the only thing anyone writes is the value of
its lu2 variable. \\
\\
Define a process to be ``minimal'' if its lu2 set is not a proper
superset of anyone else's lu2 set. \\
\\
Claim that no minimal process can write more than once after the
``stabilization point''. \\
Suppose minimal process $i$ does write twice. \\
That means that in between $i$'s first and second write, process $i$
has to read Union and observe a set that is missing some process index
that is in $i$'s lu2 set. \\
We know that this set can't contain anything not in $i$'s lu2 set. \\
So, the set observed has to be a proper subset of $i$'s lu2 set. \\
But after $i$'s first write, such a proper subset could not have been
written by anyone, because $i$ is minimal. \\
\\
So, go far enough out in the execution to where all the minimal
processes have finished all their writing. \\
\\
After this point, any minimal process $i$ continues to read,
infinitely often. \\
Every time it reads, it has to see its own lu2 set in Union (can't see
any elements not in its lu2 or it would learn something new, and can't
see anything missing, or it would write). \\
Since the minimal processes have stopped writing, but keep seeing
their own lu2 sets, it means that one of these sets must persist in
the Union variable forever. \\
\\
But that set is missing some index. \\
The process with that index eventually writes (because it has
something that isn't in Union). \\
Contradicts the persistence of this smaller set. \\
\\
\\
Latency bound (adaptivity): \\
We need to argue not just termination, but termination within time
that's a function of the number of participants.  \\
This should work, should depend on $c$. LTTR. \\
(The time must be measured from when the last of the $c$ participants
arrives.) \\
\\
\\
They claim that the algorithm leads to a consensus algorithm,
with the same kinds of properties.  \\
Just elect a leader and choose its value.  \\
Of course, this is non-fault-tolerant, and consensus without failures
is pretty easy in general. 

\end{document}

\subsection{Adaptive, symmetric mutual exclusion algorithm with
read/write registers}

\noindent
Now they consider the case where no participation is required, and
concurrency is unbounded. \\
As usual for mutual exclusion. \\
\\
They get an adaptive (to point contention), symmetric algorithm that
solves mutual exclusion, with no lockout. \\
Similar to Friedberg-Peterson. \\
But now only read/write variables. \\
Recall we can't do this with finite many shared variables.  \\
So, use infinitely many. \\
\\
Theorem 3.2, p. 12. \\
\\
They start with a deadlock-free m.e. algorithm and modify...\\
\\
3.2.1: Splitters \\
\\
They use our old friend, the splitter, again. \\
Prop. 3.1 lists the properties that are assumed. \\
These are a little different from (stronger than) what has been stated
elsewhere. \\
Namely, then divide the processes into ``early'' and ``late'', where
``late'' means that they invoke the splitter after the first process
exits it. \\
``Early'' are the others, who invoke before the first exit. \\
\\
Instead of just saying ``not all processes go right'', they strengthen
this to say that ``not all the early processes go right''. \\
\\
Instead of just saying ``not all processes go down'', then strengthen
this to say that ``the latecomers all move right'', and
``either at least one latecomer moves right, or else at most n-1 (early)
processes move down''. \\
Actually, I think this should read 
``either at least one latecomer does its first assignment, or else at
most n-1 (early) processes move down''. \\
After all, the processes are supposed to be wait-free, so it's
possible for someone to do the assignment but never actually reach the
point of going right. \\
\\
(I found a counterexample to the strengthened version they had
originally, which didn't include the clause about the latecomer: \\
-- processes 1,2,...,n all arrive at once, set x to their indices in
turn, see y = 0 so don't move right, set y = 1. \\
-- processes 1,2,...,n-1 see that x is unequal to their indices, move
down, thus exiting the splitter \\
-- process n+1 enters the system, setting x to n+1  (I think he can do
this, because you've defined n to be the number that enter before the
first one exits, and by now, the first one has exited.) \\
-- process n sees x unequal to its index, and moves down. \\
\\
So we have n processes moving down. )\\
\\
The rest of the properties are as before.  Lamport's algorithm still
satisfies this. \\
Now they modify the splitter, getting the weaker ``starvation
freedom'' property instead of wait-freedom. \\
This is weaker because it counts on everyone continuing to take steps,
that is, no failures. \\
\\
What they gain from this is an additional property:  If any process
wins then no one moves down. \\
\\
See code, p. 15. \\
Not exciting. \\
They add a couple of new flags, $b$, and $z$. \\
The await statement adds some dependencies, so termination is only
ensured if everyone keeps taking steps. \\
LTTR. \\
\\
The flags ensure the new safety property:  If anyone wins then no one
moves down. \\
First, we can't have someone winning while someone goes down as a
result of the final ``else move down'' clause.  \\
That's because this is an ``implementation'' of Lamport's splitter,
and no 2 processes get to this point in Lamport's splitter. \\
\\
Claim it's not possible to get a process winning and another process
going down as a result of the first ``move down'' statement. \\
That's because a process can only win if it sees that 
$z$ is set to $1$ before $b$ is first set to $1$. \\
But a process can only move down as a result of the first ``move
down'' statement if $b$ is set to $1$ before $z$ is first set to
$1$. \\
Incompatible. \\
\\
Also claim that the other properties needed for the Lamport splitter
carry over. \\
LTTR. \\
\\
Now for the mutual exclusion algorithm.  This is higher-level and more
understandable/interesting. \\
\\
A network of new-splitters. \\
Connect them all in a 1-dimensional chain, going in the downward
direction. \\
\\
See code, p. 16, figure 6. \\
It's just what you'd expect, except that moving right is convered to
another await statement, which waits for a shared variable ``next'' to
get set to a new splitter, larger than the one where you're currently
contending. \\
At that point, the process goes back to the beginning, starting with
the new splitter. \\
(As time goes on, the splitters that are used progress down the
array.) \\
\\
Each splitter either has a winner (who is the process who gets the
critical section), or else eliminates at least one process who moves
right. \\
\\
At the first splitter, all the latecomers move right, so only finitely
many processes move down. \\
So, within finitely many levels, a process wins. \\
Then it goes to the CS, and then sets the shared var next to the next
unused level. \\
That's where new competitions will start. \\
\\
Thus, the competitions begin at later and later splitters, as time
progresses. \\
\\
The properties of the new splitter ensure that when a process wins at
a level, no process is active in any splitter at a greater level. \\
\\
\\
Satisfies mutual exclusion. \\
No deadlock. \\
\\
Time complexity:  Measure from when no one is in CS and someone is
waiting until someone enters CS. \\
At worst, someone has to progress through $O(n)$ splitters before
entering CS, where $n$ is the number of early processes that arrive at
the ``starting splitter'' for the new mutual exclusion execution. \\
Time $O(n)$. \\
Since $n \leq$ the point contention, that gives an adaptive time
bound. \\
\\
\\
This algorithm doesn't guarantee lockout-freedom as is. \\
To add lockout-freedom, they hand-wave a bit about adding some flags
and having a process that leaves the CS examine the flags to help
select the next process. \\
This is too handwavy for me.  \\
E.g., how can such a check be designed to preserve lockout-freedom? \\
The way it's described involves a global diagonalization, which seems
to incur time delays like those for the algorithm in Theorem 2.2. \\
\\
Even deadlock-free m.e. is enough to solve leader election or
consensus. \\
Using m.e., the first process selected can be the leader, can use its
value for consensus.  \\
This seems to preserve the adaptivity and symmetry properties. \\
\\
Q:  Does this subsume Theorem 2.4, then?  \\
Recall, that used only two registers, whereas this uses infinitely
many.  \\
M.e. is a harder problem than leader election.

\subsection{Friedberg, Peterson revisited}

\noindent
Here, they just revisit FP, restating their result in terms of the new
definitions. \\
The FP algorithm is a lockout-free mutual exclusion algorithm using 2
weak semaphores and two r/w shared vars. \\
It allows unbounded concurrency, and has no required participation. \\
It is adaptive and symmetric.

\subsection{Significance}

\noindent
The adaptive, symmetric read/write register results for leader
election (Theorem 2.4) and m.e. (Theorem 3.2) seem quite powerful. \\
Provided that the termination/latency bounds for the leader election
algorithm work out OK. \\
\\
They are working hard to try to obtain general classification
results. \\
They have set up a nice set of definitions to use for classification:
concurrency and participation bounds, adaptivity, symmetry. \\
They would like results that say exactly which problems can be solved
under which of these assumptions, with which properties. \\
Of course, the work is quite incomplete. 

\subsection{Further work}

\noindent
Polish this up!  Lots of things don't quite work, aren't well
explained, etc. \\
\\
Continue working on the classification theory:
\begin{itemize}
\item
Exactly which problems can be solved under which sets of
concurrency/participation assumptions, with which properties?
Which results imply which other results? 
\item
Extend this to fault-tolerant algorithms, for problems for which we
normally consider this, such as renaming, lattice agreement, snapshot,
collect, consensus. 
\item
Get separation theorems for different sets of requirements.
E.g., are there interesting problems that can be solved for any finie,
known number of processes but can't be solved for a finite, unknown
number of processes?
\item
General transformations to convert algorithms for finite numbers of
processes to algorithms for infinitely many processes (perhaps under
certain assumptions, like symmetry)?
\end{itemize}

\section{Gafni, Merritt, Taubenfeld}

\subsection{Overview}

\noindent
Continuing on the same theme, same authors plus Gafni. \\
\\
This time, unlike the other paper, they are considering faults, in
fact, wait-free computation. \\
\\
The problems, then are different from the leader election, mutual
exclusion, etc., problems considered in the other paper (for which we
generally consider the fault-free case), but rather, snapshot,
renaming, etc. \\
\\
The paper really has two foci:
\begin{enumerate}
\item
They are trying to get a general classification theory for problems,
based on the kind of concurrency assumptions that are required for
solving the problems. 
The most interesting case is ``unbounded concurrency'', as defined in
the other paper.  Any number of processes might participate at once. 
\item
They try to obtain algorithms that work with unbounded concurrency.
\end{enumerate}
This paper does not deal with participation lower bounds. \\
\\
I think that the unbounded concurrency algorithms are more
interesting, especially Algorithm 3.1 and the other things they
discuss in Section 3.1.  \\
So I'll focus on that. \\
\\
Basically, Section 2 contains hierarchy results. \\
They show that ``there exist problems'' that can be solved with
bounded concurrency but not unbounded, and in fact, with $k$-bounded
concurrency but not $k+1$-bounded. \\
These are bounded versions of snapshot problems.  \\
A little contrived, though not terrible. \\
\\
Section 3 gives the algorithms.  \\
Section 3.1 focuses on a cute snapshot algorithm that handles
unbounded concurrency.  \\
Its time complexity, in the initial presentation, depends not 
only on the actual contention but on the process indices. \\
Not exactly what we want, not ``adaptive''.  \\
But then they describe how to improve this by doing renaming and
thereby get the complexity to where it depends only on the actual
contention. \\
\\
The kinds of restrictions on concurrency that they consider are: 
\begin{itemize}
\item
$k$-bounded:  A finite bound $k$ on the number of processes that are
active at any point in any execution (point contention).
\item
bounded:  In each execution, the concurrency is bounded by some $k$.
\item
unbounded:  No bound on the concurrency.
\end{itemize}

\noindent
Consider read/write registers only. 

\subsection{The hierarchy results}

\noindent
They show the existence of a problem that can be solved with bounded
concurrency but not unbounded. \\
And for each $k$, a problem that can be solved with $k$ concurrency
but not $k+1$. \\
\\
Bounded snapshot problem:  \\
They fomulate the atomic snapshot problem in a somewhat simplified,
unusual way. \\
\\
First, they make it a one-shot problem (everyone invokes at most
once). \\
\\
Usually, the operations are update and scan, where update writes a
value and the scan returns a value for everyone. \\
Here, they dispense with the values, and have only one kind of
operation, which they don't name. \\
We could call it upscan. \\
The operation is supposed to return a set of processes, all of which
have at least invoked an upscan operation. \\
Each process' set should include itself. \\
The sets should be totally ordered by containment. \\
So far, isn't this just lattice agreement? \\
\\
The new wrinkle is that there should be some bound for each execution,
say $r$, on the difference between two consecutive sets in the nesting
order. \\
\\
Now, I'm not sure this quite makes sense, in a wait-free setting. \\
It's not clear if we are allowed to assign sets to processes that
don't finish.  Probably we should be.  Maybe we have to. \\
Cast the problem in terms of existence of an assignment of sets
to invocations such that...??? \\
\\
Theorem 2.1:  This problem isn't solvable is the unbounded concurrency
model. \\
\\
It's trivially solvable with bounded concurrency---just use an
ordinary snapshot algorithm.  \\
If concurrency is bounded in an execution by, say, $c$, then in that
execution, the difference between the sizes of two consecutive
snapshots will be at most $c$. \\
\\
(Proof:  Enumerate the sets returned in nondecreasing order, counting
repeats. \\
The smallest set in this sequence must have size between $1$ and $c$,
inclusive.  \\
That's because someone terminates before the $c+1$st process starts. \\
The second smallest must have size between 2 and $c+1$, because two
must terminate before the $c+2$nd process starts. \\
(And the second must contain the first.) \\
Etc. \\
The $k$th must have size between $k$ and $c+k-1$, inclusive. \\
\\
Then the difference between two consecutive entries in this list is at
most $(c+k-1) - (k-1) = c$.) \\
\\
Claim it's not solvable with unbounded concurrency. \\
To see this, divide the processes into batches, $G_1, G_2, \ldots$,
each with more processes than the preceding one. \\
They prove a rather odd lemma saying that, for any snapshot algorithm,
if a batch comes in all together, it must be possible for them all to
return the entire batch. \\
Proof of that is somewhat tricky, and based on previous impossibility
results for renaming. \\
\\
Needs some careful working out, LTTR. \\
\\
Since the batches get larger and larger, this isn't bounded. \\
\\
\\
A similar construction is used to ``separate'' $k$-concurrency from
$k+1$-concurrency. \\
Based on a $k$-bounded version of the snapshot problem. \\
Similar to ``bounded snapshot'', except the difference is always,
uniformly, at most $k$. 

\subsection{One-shot snapshot algorithm whose latency depends on
contention and process indices}

\noindent
Now consider Theorem 3.1, which presents a snapshot algorithm
(ordinary, not bounded snapshot) that handles unbounded
concurrency. \\
\\
The time complexity here depends on both the concurrency that happens
to occur, and the maximum index of a participant. \\
But then they show, using various tricks, how to remove the dependency
on the max index. \\
\\
The basic algorithm is quite interesting. \\
\\
One-shot version, but any number of processes.  \\
(Modifiable simply by letting each process use multiple names.) \\
\\
Again, just returns process ids, not values. \\
\\
Code, p. 5. \\
\\
The main data structures used by this code are an infinite array of
start bits and an infinite array of flag bits. \\
There's also an infinite array of ``snap'' sets, but let's not look at
these yet. \\
Basically, they provide short-cuts---answers that someone has already
calculated, that another process can borrow rather than completing the
work of doing its own snapshot. \\
\\
Let's look at the code without the snap values for the moment. \\
\\
A process $i$ arrives and sets its start[i] flag, then proceeds to
collect the names of other processes who have set their start
flags. \\
\\
How does it know where to look?  It can't just read all the
registers. \\
It traverses registers from the smallest as long as the flag bits are
set. \\
Along the way, it collects all the indices of processes whose start
bits are set. \\
It puts these into its set ``collect''. \\
\\
It repeats this. \\
The second time, and subsequent times, it saves the result of its
previous collect in a set ``doublecollect''. \\
Keeping two sets will allow it to compare the results it obtains on
two successive attempts. \\
\\
A stopping condition is that collect = doublecollect (so nothing new
has been found on the last pass), and also $i$ itself is in the
set. \\
Until this condition is reached, the process just keeps making
attempts. \\
\\
Now, there is one case where it takes some positive action before
beginning the next attempt: \\
(line 10) If collect = doublecollect but $i$ itself isn't in the
set. \\
That means that the traversal didn't get far enough---didn't reach the
$i$ entry in the arrays. \\
That would be because not enough flags were set. \\
In this case, before beginning the next attempt, process $i$ sets its
own flag[i] and all smaller flag variables. \\
Then tries again. \\
\\
The next time it sees collect = doublecollect, it will also have $i$
itself in the set, because it is guaranteed to reach $i$ the next time
(enough flags are set). \\
\\
Now, there is still the problem that process $i$ might continue making
attempts forever, and on each pass, learn about more new entrants.  \\
That could (as in the original snapshot algorithm of Afek, Attiya, et
al.) cause nontermination. \\
To get around this, we use the same trick as in the earlier algorithm:
let a process that determines a good snapshot record it someplace for
other processes to use. \\
\\
In this case, a ``good'' snapshot, worth recording, is one that sees
collect = doublecollect, and where the process itself isn't in the set. \\
(Why make that restriction?) \\
\\
So in this case, at the same time when process $i$ is setting the
flags, it also records the ``good'' snapshot. \\
\\
Now look how the snapshots are used in the code:  in line 5, 9, 15, as
a stopping condition---when $i$ sees someone else's snap that already
contains $i$, $i$ can just take that snapshot and stop. \\
\\
\\
The safety arguments aren't given in this paper but are apparently
(seems believable) similar to those for standard snapshot. \\
\\
Q:  Doesn't seem to be exactly an atomic object. \\
Two internal steps---to add your own index and to record all added
indices? \\
\\
Termination: \\
Process $i$ can't loop forever reading a fixed number of shared
variables. \\
(Or would eventually see collect = doublecollect, set flags, see
again, terminate.) \\
So the only problem is if a process, over time, reads all the flag
variables, 1,2,3,...\\\
Suppose it does. \\
\\
After some point, we have $flag[k] = 1$ for all $k \leq i$. \\
At that point, let $max \geq i$ be the maximum id of any process that
has ever been active up to this point. \\
Now, eventually $flag[max + 1]$ has to get set to $1$, and this has to
be done by some process with index $p \geq max + 1$. \\
{\em Before\/} it does this, it also sets $snap[p]$ to a snapshot set. \\
That set must contain $i$, because the snapshot that produces this set
starts after the flags are already set through $i$. \\
\\
\\
Some key ideas:
\begin{enumerate}
\item
The flag bits are only set after a new snap has been written. \\
Thus, a process helps other processes out (by supplying a snapshot for
them) before extending the flag prefix (which could cause other
processes to go on forever). 
\item
Can extend this easily to a long-lived algorithm, with multiple
snapshots per process, by allocating multiple indices per process.
\end{enumerate}

Nothing is adaptive so far---latency depends on process id as well as
number of participants. 

\subsection{Bells and whistles, and making the snapshot adaptive}

\noindent
Suppose we want a snapshot algorithm that is:
\begin{itemize}
\item
Adaptive, that is, latency depending on the actual number of
participants.
\item
Handles unbounded concurrency.
\item
Still just one-shot.
\end{itemize}

\noindent
This can be done by a combination of tricks. \\
\\
Basically, use a one-shot $f(k)$-renaming algorithm that is adaptive
(latency depends on $k$), and handles potentially unbounded number of
participants, arriving in any concurrency pattern. \\
\\
Q:  How to get this? \\
Q:  How to use it? \\
\\
Using it:  \\
First apply the renaming algorithm to reduce the set of
names so that they depend on the actual number of arrivals.  \\
(Careful---should be the names $1,...,f(k)$, not just an arbitrary set
of $f(k)$ names.) \\
\\
Then use the resulting names in the snapshot algorithm just
sketched. \\
\\
Getting it: \\
Take a standard one-shot adaptive renaming algorithm like
Attiya-Fouren's, which uses an upper bound $n$ on the size of the
namespace, and modify it. \\
Algorithms like AF (they say) can be expressed in a form where they
compete for names in order, 1,2,3,...; when they fail to get one, they
try to get the next one.  (Check this.) \\
\\
``Interleaving trick'':  Let the processes in AF compete for only the
odd names. \\
If process $i$ fails to acquire any odd number through $2i-1$, it just
chooses $2i$. \\
Thus, it defaults to twice its own index, a name that is reserved for
it.  \\
Although this depends on $i$, it's OK here because the fact that $i$
reaches this point means that at least (approx) $i$ processes are
participating.  \\
\\
\\
They also give a second trick, which works for all adaptive one-shot
renaming algorithms, but causes the namespace to be squared. \\
LTTR. \\
\\
Go over if time, from p. 6. \\
\\
\\
Other results in the paper extend these to long-lived algorithms for
renaming, collect, and snapshot, all for unbounded concurrency.

%Also include more algorithms.
%Powerpoint we're sending you demonstrates some of the modularity that
%can be emphasized in building long-lived and adaptive solutions.

\subsection{Significance}

\noindent
I'm not so interested in the hierarchy results. \\
But the algorithmic results seem excellent. \\
Very strong.  \\
Unbounded processes, with unbounded concurrency, can solve all kinds
of problems in a wait-free manner. \\
Using just read/write registers.  Infinitely many. \\
Costs are adaptive, based on number of actual participants. \\
\\
Modularity is very interesting. 

\subsection{Future work}

\noindent
Lots of clean-up needed. \\
\\
Some more digesting, to turn this into a general theory with the best
results presented in the best way, the best modularity, etc. \\
\\
What does all this have to do with real distributed computing?
\end{document}
