git » libjio » commit 05e7ff2

jtrans_commit(): Lock the file regions just before reading the data

author Alberto Bertogli
2009-07-31 01:27:23 UTC
committer Alberto Bertogli
2009-07-31 01:27:23 UTC
parent 94f1fbee1b4be2a510d7a56ae3458b50cb8fbfbd

jtrans_commit(): Lock the file regions just before reading the data

There is no need to lock the file regions until we're about to read the
data, so this patch moves the lock there.

It also isolates the code a bit to make jtrans_commit() more readable.

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

libjio/trans.c +40 -25

diff --git a/libjio/trans.c b/libjio/trans.c
index 431fa05..429ac99 100644
--- a/libjio/trans.c
+++ b/libjio/trans.c
@@ -75,6 +75,37 @@ void jtrans_free(struct jtrans *ts)
 	free(ts);
 }
 
+/** Lock/unlock the ranges of the file covered by the transaction. mode must
+ * 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;
+
+	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;
+		}
+	}
+
+	return 0;
+
+error:
+	return -1;
+}
+
 /** Read the previous information from the disk into the given operation
  * structure. Returns 0 on success, -1 on error. */
 static int operation_read_prev(struct jtrans *ts, struct operation *op)
@@ -203,25 +234,10 @@ ssize_t jtrans_commit(struct jtrans *ts)
 	if (ts->flags & J_RDONLY)
 		goto exit;
 
-	/* first of all lock all the regions we're going to work with;
-	 * otherwise there could be another transaction trying to write the
-	 * same spots and we could end up with interleaved writes, that could
-	 * break atomicity warantees if we need to rollback */
-	if (!(ts->flags & J_NOLOCK)) {
-		off_t lr;
-		for (op = ts->op; op != NULL; op = op->next) {
-			lr = plockf(ts->fs->fd, F_LOCKW, op->offset, op->len);
-			if (lr == -1)
-				/* note it can fail with EDEADLK */
-				goto unlock_exit;
-			op->locked = 1;
-		}
-	}
-
 	/* create and fill the transaction file */
 	jop = journal_new(ts->fs, ts->flags);
 	if (jop == NULL)
-		goto unlock_exit;
+		goto exit;
 
 	for (op = ts->op; op != NULL; op = op->next) {
 		r = journal_add_op(jop, op->buf, op->len, op->offset);
@@ -235,6 +251,13 @@ ssize_t jtrans_commit(struct jtrans *ts)
 
 	fiu_exit_on("jio/commit/tf_data");
 
+	/* lock all the regions we're going to work with; otherwise there
+	 * could be another transaction trying to write the same spots and we
+	 * could end up with interleaved writes, that could break atomicity
+	 * warantees if we need to rollback */
+	if (lock_file_ranges(ts, F_LOCKW) != 0)
+		goto unlink_exit;
+
 	if (!(ts->flags & J_NOROLLBACK)) {
 		for (op = ts->op; op != NULL; op = op->next) {
 			 r = operation_read_prev(ts, op);
@@ -358,18 +381,10 @@ unlink_exit:
 		jop = NULL;
 	}
 
-unlock_exit:
 	/* always unlock everything at the end; otherwise we could have
 	 * half-overlapping transactions applying simultaneously, and if
 	 * anything goes wrong it would be possible to break consistency */
-	if (!(ts->flags & J_NOLOCK)) {
-		for (op = ts->op; op != NULL; op = op->next) {
-			if (op->locked) {
-				plockf(ts->fs->fd, F_UNLOCK,
-						op->offset, op->len);
-			}
-		}
-	}
+	lock_file_ranges(ts, F_UNLOCK);
 
 exit:
 	pthread_mutex_unlock(&(ts->lock));