[PATCH] locking: Warn about state preservation when releasing and re-acquiring locks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Elad Lahav <e2lahav@xxxxxxxxx>
---
 CodeSamples/locking/Makefile       |  5 +-
 CodeSamples/locking/rec_tree_itr.c | 96 ++++++++++++++++++++++++++++++
 locking/locking.tex                | 32 +++++++++-
 3 files changed, 130 insertions(+), 3 deletions(-)
 create mode 100644 CodeSamples/locking/rec_tree_itr.c

diff --git a/CodeSamples/locking/Makefile b/CodeSamples/locking/Makefile
index 3663a7d5..cf600636 100644
--- a/CodeSamples/locking/Makefile
+++ b/CodeSamples/locking/Makefile
@@ -1,4 +1,4 @@
-ARCH_INDEPENDENT = locked_list
+ARCH_INDEPENDENT = locked_list rec_tree_itr
 ARCH_DEPENDENT = xchglock
 PROGS = $(ARCH_INDEPENDENT) $(ARCH_DEPENDENT)
 
@@ -20,5 +20,8 @@ locked_list: locked_list.c ../api.h
 xchglock: xchglock.c ../api.h
 	cc -g -Wall -o xchglock xchglock.c -lpthread
 
+rec_tree_itr: rec_tree_itr.c ../api.h
+	cc -g -Wall -o rec_tree_itr rec_tree_itr.c -lpthread
+
 clean:
 	rm -f $(PROGS)
diff --git a/CodeSamples/locking/rec_tree_itr.c b/CodeSamples/locking/rec_tree_itr.c
new file mode 100644
index 00000000..1445668c
--- /dev/null
+++ b/CodeSamples/locking/rec_tree_itr.c
@@ -0,0 +1,96 @@
+/*
+ * locked_list.c: Recursive tree iterator
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * Copyright (c) 2022 Elad Lahav
+ */
+
+#include "../api.h"
+
+#define MAX_CHILDREN 10
+
+struct node {
+    int data;
+	int nchildren;
+	struct node *children[MAX_CHILDREN];
+};
+
+struct tree {
+	spinlock_t s;
+	struct node *root;
+};
+
+//\begin{snippet}[labelbase=ln:locking:rec_tree_iterator:tree_for_each,commandchars=\\\@\$]
+void tree_for_each_rec(struct tree *tr, struct node *nd,
+					   void (*callback)(struct node *))
+{
+	spin_unlock(&tr->s);
+	callback(nd);
+	spin_lock(&tr->s);
+
+	for (int i = 0; i < nd->nchildren; i++) {
+		tree_for_each_rec(tr, nd->children[i], callback);
+	}
+}
+
+void tree_for_each(struct tree *tr,
+				   void (*callback)(struct node *))
+{
+	spin_lock(&tr->s);
+	tree_for_each_rec(tr, tr->root, callback);
+	spin_unlock(&tr->s);
+}
+//\end{snippet}
+
+void print_node_data(struct node *nd)
+{
+    printf("%d\n", nd->data);
+}
+
+int main(int argc, char *argv[])
+{
+    struct tree tr;
+    struct node *nodes = calloc(sizeof(struct node), 10);
+
+    for (int i = 0; i < 10; i++) {
+        nodes[i].data = 100 + i;
+    }
+
+	spin_lock_init(&tr.s);
+
+    tr.root = &nodes[0];
+
+    nodes[0].nchildren = 3;
+    nodes[0].children[0] = &nodes[1];
+    nodes[0].children[1] = &nodes[2];
+    nodes[0].children[2] = &nodes[3];
+
+    nodes[1].nchildren = 2;
+    nodes[1].children[0] = &nodes[4];
+    nodes[1].children[1] = &nodes[5];
+
+    nodes[2].nchildren = 1;
+    nodes[2].children[0] = &nodes[6];
+
+    nodes[5].nchildren = 3;
+    nodes[5].children[0] = &nodes[7];
+    nodes[5].children[1] = &nodes[8];
+    nodes[5].children[2] = &nodes[9];
+
+    tree_for_each(&tr, print_node_data);
+
+	return 0;
+}
diff --git a/locking/locking.tex b/locking/locking.tex
index 45a12caa..c8343638 100644
--- a/locking/locking.tex
+++ b/locking/locking.tex
@@ -375,6 +375,34 @@ deadlock is avoided if each module separately avoids deadlock.
 This rule therefore greatly simplifies deadlock analysis and greatly
 improves modularity.
 
+Nevertheless, the golden rule comes with a warning.
+The state protected by the lock cannot be trusted to survive the release
+and re-acquisition of the lock by the module.
+Such assumptions on state preservation can often be subtle and thus the
+source of many bugs.
+The use of \co{qsort()} in the examples above may not illustrate the
+danger.
+
+Consider, however, the recursive tree iterator in
+\cref{lst:locking:Recursive Tree Iterator}.
+The iterator visits every node in the tree, invoking a user's callback
+function.
+The tree lock is released before the invocation and re-acquired after.
+This code makes dangerous assumptions about the preservation of state,
+such as that the number of children of the current node has not changed,
+that the ancestors stored on the stack by the recursion are still there,
+or even that the visited node itself has not been removed and freed.
+The module author must therefore take great care to ensure either that
+state is preserved (e.g., by acquiring a reference on a node to prevent
+it from being freed) or re-initialized when the lock is acquired after
+the return of the callback function.
+
+\begin{listing}
+\input{CodeSamples/locking/rec_tree_iterator@tree_for_each.fcv}
+\caption{Concurrent List Iterator}
+\label{lst:locking:Recursive Tree Iterator}
+\end{listing}
+
 \subsubsection{Layered Locking Hierarchies}
 \label{sec:locking:Layered Locking Hierarchies}
 
@@ -1399,7 +1427,7 @@ for example:
 	that some FIFO ordering applies for threads of the same priority.
 \item	Random, so that the new lock holder is chosen randomly from
 	all threads attempting acquisition, regardless of timing.
-\item	
+\item
 	Unfair, so that a given acquisition might never acquire the lock
 	(see \cref{sec:locking:Unfairness}).
 \end{enumerate}
@@ -1486,7 +1514,7 @@ when switching from read-holder to write-holder mode.
 Here are a few possible approaches:
 
 \begin{enumerate}
-\item	
+\item
 	Reader-preference implementations unconditionally favor readers
 	over writers, possibly allowing write acquisitions to be
 	indefinitely blocked.
-- 
2.25.1




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux