Skip to content

Commit

Permalink
Indent enumerate items
Browse files Browse the repository at this point in the history
  • Loading branch information
jserv committed Apr 5, 2024
1 parent adcb95c commit 0980af4
Showing 1 changed file with 46 additions and 51 deletions.
97 changes: 46 additions & 51 deletions concurrency-primer.tex
Original file line number Diff line number Diff line change
Expand Up @@ -416,17 +416,16 @@ \subsection{Exchange}
To see where this might be useful,
let's tweak our example from \secref{atomicity}:
instead of displaying the total number of processed files,
the \textsc{ui} might want to show how many were processed per second.
We could implement this by having the \textsc{ui} thread read the counter then zero it each second.
the \textsc{UI} might want to show how many were processed per second.
We could implement this by having the \textsc{UI} thread read the counter then zero it each second.
But we could get the following race condition if reading and zeroing are separate steps:
\begin{enumerate}
\item The \textsc{ui} thread reads the counter.
\item Before the \textsc{ui} thread has the chance to zero it,
the worker thread increments it again.
\item The \textsc{ui} thread now zeroes the counter, and the previous increment
is lost.
\item The \textsc{UI} thread reads the counter.
\item Before the \textsc{UI} thread has the chance to zero it,
the worker thread increments it again.
\item The \textsc{UI} thread now zeroes the counter, and the previous increment is lost.
\end{enumerate}
If the \textsc{ui} thread atomically exchanges the current value with zero,
If the \textsc{UI} thread atomically exchanges the current value with zero,
the race disappears.

\subsection{Test and set}
Expand Down Expand Up @@ -462,10 +461,10 @@ \subsection{Fetch and…}
all as part of a single atomic operation.
You might recall from the exchange example that additions by the worker thread must be atomic to prevent races, where:
\begin{enumerate}
\item The worker thread loads the current counter value and adds one.
\item Before that thread can store the value back,
the \textsc{ui} thread zeroes the counter.
\item The worker now performs its store, as if the counter was never cleared.
\item The worker thread loads the current counter value and adds one.
\item Before that thread can store the value back,
the \textsc{UI} thread zeroes the counter.
\item The worker now performs its store, as if the counter was never cleared.
\end{enumerate}

\subsection{Compare and swap}
Expand Down Expand Up @@ -718,10 +717,10 @@ \subsection{Spurious LL/SC failures}
Many lockless algorithms use \textsc{CAS} loops like this to atomically update a variable when calculating its new value is not atomic.
They:
\begin{enumerate}
\item Read the variable.
\item Perform some (non-atomic) operation on its value.
\item \textsc{CAS} the new value with the previous one.
\item If the \textsc{CAS} failed, another thread beat us to the punch, so try again.
\item Read the variable.
\item Perform some (non-atomic) operation on its value.
\item \textsc{CAS} the new value with the previous one.
\item If the \textsc{CAS} failed, another thread beat us to the punch, so try again.
\end{enumerate}
If we use \monobox{compare\_exchange\_strong} for this family of algorithms,
the compiler must emit nested loops:
Expand Down Expand Up @@ -988,14 +987,12 @@ \subsection{Acquire-Release}
Order does not matter when incrementing the reference count since no action is taken as a result.
However, when we decrement, we must ensure that:
\begin{enumerate}
\item All access to the referenced object happens
\emph{before} the count reaches zero.
\item Deletion happens \emph{after} the reference count reaches
zero.\punckern\footnote{This can be optimized even further by
making the acquire barrier only occur conditionally, when the reference
count is zero.
Standalone barriers are outside the scope of this paper,
since they are almost always pessimal compared to a combined load-acquire or store-release.}
\item All access to the referenced object happens \emph{before} the count reaches zero.
\item Deletion happens \emph{after} the reference count reaches zero.\punckern\footnote{%
This can be optimized even further by making the acquire barrier only occur conditionally,
when the reference count is zero.
Standalone barriers are outside the scope of this paper,
since they are almost always pessimal compared to a combined load-acquire or store-release.}
\end{enumerate}

Curious readers might be wondering about the difference between acquire-release and sequentially consistent operations.
Expand Down Expand Up @@ -1170,33 +1167,31 @@ \section{If concurrency is the question, \texttt{volatile} is not the answer.}
(This is how most machines ultimately interact with the outside world.)
\keyword{volatile} implies two guarantees:
\begin{enumerate}
\item The compiler will not elide loads and stores that seem ``unnecessary''\quotekern.
For example, if I have some function:
\begin{colfigure}
\begin{minted}[fontsize=\codesize,autogobble]{cpp}
void write(int *t)
{
*t = 2;
*t = 42;
}
\end{minted}
\end{colfigure}
the compiler would normally optimize it to:
\begin{minted}[fontsize=\codesize,autogobble]{cpp}
void write(int *t)
{
*t = 42;
}
\end{minted}
\mintinline{cpp}{*t = 2} is often considered a \introduce{dead store},
seemingly performing no function.
However, when \texttt{t} is directed at an \textsc{MMIO} register,
this assumption becomes unsafe.
In such cases, each write operation could potentially influence the behavior of the associated hardware.

\item The compiler will not reorder \keyword{volatile}
reads and writes with respect to other \keyword{volatile} ones
for similar reasons.
\item The compiler will not elide loads and stores that seem ``unnecessary''\quotekern.
For example, if I have some function:
\begin{colfigure}
\begin{minted}[fontsize=\codesize,autogobble]{cpp}
void write(int *t)
{
*t = 2;
*t = 42;
}
\end{minted}
\end{colfigure}
the compiler would normally optimize it to:
\begin{minted}[fontsize=\codesize,autogobble]{cpp}
void write(int *t)
{
*t = 42;
}
\end{minted}
\mintinline{cpp}{*t = 2} is often considered a \introduce{dead store},
seemingly performing no function.
However, when \texttt{t} is directed at an \textsc{MMIO} register,
this assumption becomes unsafe.
In such cases, each write operation could potentially influence the behavior of the associated hardware.

\item The compiler will not reorder \keyword{volatile} reads and writes with respect to other \keyword{volatile} ones for similar reasons.
\end{enumerate}

These rules fall short of providing the atomicity and order required for safe communication between threads.
Expand Down

0 comments on commit 0980af4

Please sign in to comment.