git » libjio » commit 0b8fe71

libjio: Always lock ranges in the same order

author Alberto Bertogli
2009-09-25 00:08:04 UTC
committer Alberto Bertogli
2009-09-25 00:08:04 UTC
parent 57f826ce6f8c83a0227630284dc4673f61b459af

libjio: Always lock ranges in the same order

We must always lock in the same order to avoid deadlocks. I don't know how
could I've missed this all this time.

For more details, see the big comment inside the patch.

Signed-off-by: Alberto Bertogli <albertito@blitiri.com.ar>

libjio/trans.c +34 -14

diff --git a/libjio/trans.c b/libjio/trans.c
index e1747a8..1f7b66f 100644
--- a/libjio/trans.c
+++ b/libjio/trans.c
@@ -80,25 +80,45 @@ void jtrans_free(struct jtrans *ts)
  * be either F_LOCKW or F_UNLOCK. Returns 0 on success, -1 on error. */
 static int lock_file_ranges(struct jtrans *ts, int mode)
 {
-	off_t lr;
-	struct operation *op;
+	unsigned int nops;
+	off_t lr, min_offset;
+	struct operation *op, *start_op;
 
 	if (ts->flags & J_NOLOCK)
 		return 0;
 
-	for (op = ts->op; op != NULL; op = op->next) {
-		if (mode == F_LOCKW) {
-			lr = plockf(ts->fs->fd, F_LOCKW, op->offset, op->len);
-			if (lr == -1)
-				goto error;
-			op->locked = 1;
-		} else if (mode == F_UNLOCK && op->locked) {
-			lr = plockf(ts->fs->fd, F_UNLOCK, op->offset,
-					op->len);
-			if (lr == -1)
-				goto error;
-			op->locked = 0;
+	/* Lock/unlock always in the same order to avoid deadlocks. We will
+	 * begin with the operation that has the smallest start offset, and go
+	 * from there.
+	 * Note that this is O(n^2), but n is usually (very) small, and we're
+	 * about to do synchronous I/O, so it's not really worrying. It has a
+	 * small optimization to help when the operations tend to be in the
+	 * right order. */
+	nops = 0;
+	min_offset = 0;
+	start_op = ts->op;
+	while (nops < ts->numops_r + ts->numops_w) {
+		for (op = start_op; op != NULL; op = op->next) {
+			if (min_offset < op->offset)
+				continue;
+			min_offset = op->offset;
+			start_op = op->next;
+
+			if (mode == F_LOCKW) {
+				lr = plockf(ts->fs->fd, F_LOCKW, op->offset, op->len);
+				if (lr == -1)
+					goto error;
+				op->locked = 1;
+			} else if (mode == F_UNLOCK && op->locked) {
+				lr = plockf(ts->fs->fd, F_UNLOCK, op->offset,
+						op->len);
+				if (lr == -1)
+					goto error;
+				op->locked = 0;
+			}
 		}
+
+		nops++;
 	}
 
 	return 0;