You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a simple ABA problem example and more description
Because ABA problem is not so intuitive, the plan is to have a simple
example first, then reusing the new example in section 5. A nother stack
example by "null program" will be mentioned for additional examples.
Next commit will provide solutions to fix ABA problem in both
examples.
Copy file name to clipboardExpand all lines: concurrency-primer.tex
+52-10Lines changed: 52 additions & 10 deletions
Original file line number
Diff line number
Diff line change
@@ -886,25 +886,67 @@ \subsection{Conclusion about lock-free}
886
886
\subsection{ABA problem}
887
887
We have introduced CAS as one of the read-modify-write operations.
888
888
However, does the target object not changing really mean that no other threads modified it halfway through?
889
+
If the target object is changed to something by other thread and changed back, the result of comparison is still equal.
890
+
Although the target object has indeed been changed, causing the operation not remaining atomic.
891
+
We call this \introduce{ABA problem}.
889
892
Consider the following scenario,
890
893
894
+
\inputminted{c}{./examples/simple_aba_example.c}
895
+
896
+
Compile it with \monobox{gcc -std=c11 -Wall -Wextra -pthread simple\_aba\_example.c}.
897
+
The execution result would be:
898
+
899
+
\begin{ccode}
900
+
A: v = 42
901
+
B: v = 47
902
+
B: v = 42
903
+
A: v = 52
904
+
\end{ccode}
905
+
906
+
In the example provided, the presence of ABA problem results in thread A being unaware that variable \monobox{v} has been altered.
907
+
Since the comparison result indicates \monobox{v} unchanged, \monobox{v + 10} is swapped in.
908
+
Here sleeping is only used to ensure the occurance of ABA problem.
909
+
In real world scenario, instead of sleeping, thread A could paused by being context switched for other tasks, including being preempted by higher priority tasks.
910
+
This example seems harmless, but things can get nasty when atomic \textsc{RMW} operations are used in more complex data structures.
911
+
912
+
In a broader context, the ABA problem occurs when changes occur between loading and comparing, but the comparing mechanism is unable to identify that the state of the target object is not the latest, yielding a false positive result.
913
+
914
+
Back to thread pool example in \secref{rmw}, it contains ABA problem as well.
915
+
In \monobox{worker} function, we have the thread trying to claim the job.
916
+
891
917
\begin{ccode}
892
-
/* example code place holder */
893
-
do { /* read and modify */ }
894
-
/* something can happen in between */
895
-
while(CAS);
918
+
job_t *job = atomic_load(&thrd_pool->head->prev);
919
+
while (!atomic_compare_exchange_strong(&thrd_pool->head->prev, &job,
920
+
job->prev)) {
921
+
}
896
922
\end{ccode}
897
923
898
-
If the target object is changed to something by other thread and changed back, the result of comparism is still equal.
899
-
Although the target object has indeed been changed, causing the operation to not remain atomic.
900
-
We call this \introduce{ABA problem}. In the example above, the presents of ABA problem leads to
924
+
Consider the following scenario:
925
+
\begin{enumerate}
926
+
\item There is only one job left.
927
+
\item Thread A loads the pointer to the job by \monobox{atomic\_load()}.
928
+
\item Thread A is preempted.
929
+
\item Thread B claims the job and successfully updates \monobox{thrd\_pool->head->prev}.
930
+
\item Thread B sets thread pool state to idle.
931
+
\item Main thread fininshes waiting and adds more jobs.
932
+
\item Memory allocator reuses the recently freed memory as new jobs addresses.
933
+
\item Fortunately, the first added job has the same address as the one Thread A holded.
934
+
\item Thread A is back in running state. The comparison result is equal so it updates \monobox{thrd\_pool->head->prev} with the old \monobox{job->prev}, which is already a dangling pointer.
935
+
\item Another thread loads the dangling pointer from \monobox{thrd\_pool->head->prev}.
936
+
\end{enumerate}
901
937
902
-
The ABA problem occurs when changes occur between reading and comparing, but the comparing mechanism is unable to identify that the state is not the latest.
903
-
The maximum number we refer to may not be the actual maximum, and the next job may not be the same job.
904
-
Failure to recognize this through comparison can result in outdated information.
938
+
Notice that even though \monobox{job->prev} is not loaded explicitly before comparison, compiler could place loading instructions before comparison.
939
+
At the end, the dangling pointer could either point to garbage or trigger segmentation fault.
940
+
It could be even worse if nested ABA problem occurs in thread B.
941
+
942
+
Failure to recognize changed target object through comparison can result in stale information.
905
943
The general concept of solving this problem involves adding more information to make different state distinguishable, and then making a decision on whether to act on the old state or retry with the new state.
906
944
If acting on the old state is chosen, then safe memory reclamation should be considered as memory may have already been freed by other threads.
907
945
More aggressively, one might consider the programming paradigm where each operation on the target object does not have a side effect on modifying it.
946
+
In the later section, we will introduce a different way of implementing atomic \textsc{RMW} operations by using LL/SC instructions. The exclusiveness provided by LL/SC instructions avoids the pitfall introduced by comparison.
947
+
948
+
To make different state distinguishable, a common solution is adding a version number to be compared as well.
949
+
908
950
909
951
\section{Sequential consistency on weakly-ordered hardware}
0 commit comments