[dovecot-cvs] dovecot/src/lib-index mail-index-lock.c, 1.6, 1.7 mail-index-private.h, 1.4, 1.5 mail-index-sync.c, 1.3, 1.4 mail-index.c, 1.109, 1.110

cras at procontrol.fi cras at procontrol.fi
Wed Apr 28 21:52:09 EEST 2004


Update of /home/cvs/dovecot/src/lib-index
In directory talvi:/tmp/cvs-serv12460/lib-index

Modified Files:
	mail-index-lock.c mail-index-private.h mail-index-sync.c 
	mail-index.c 
Log Message:
locking fixes, sync fix



Index: mail-index-lock.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/mail-index-lock.c,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -d -r1.6 -r1.7
--- mail-index-lock.c	28 Apr 2004 02:39:03 -0000	1.6
+++ mail-index-lock.c	28 Apr 2004 18:52:06 -0000	1.7
@@ -1,30 +1,21 @@
 /* Copyright (C) 2003-2004 Timo Sirainen */
 
 /*
-   Locking is meant to be as transparent as possible. Anything that locks
-   the index must either keep it only a short time, or be prepared that the
-   lock is lost.
-
-   Lock is lost in only one situation: when we try to get an exclusive lock
-   but we already have a shared lock. Then we'll drop all shared locks and
-   get the exclusive lock.
-
    Locking should never fail or timeout. Exclusive locks must be kept as short
-   time as possible. Shared locks can be long living, so if can't get exclusive
-   lock directly within 2 seconds, we'll replace the index file with a copy of
-   it. That means the shared lock holders can keep using the old file while
-   we're modifying the new file.
-
-   lock_id is used to figure out if acquired lock is still valid. Shared
-   locks have even numbers, exclusive locks have odd numbers. The number is
-   increased by two every time the lock is dropped.
+   time as possible. Shared locks can be long living, so if we can't get
+   exclusive lock directly within 2 seconds, we'll replace the index file with
+   a copy of it. That means the shared lock holders can keep using the old file
+   while we're modifying the new file.
 
-   mail_index_lock_shared() -> lock_id=2
-   mail_index_lock_shared() -> lock_id=2
-   mail_index_lock_exclusive() -> lock_id=5 (had to drop shared locks)
-   mail_index_lock_shared() -> lock_id=4
+   lock_id is used to figure out if acquired lock is still valid. When index
+   file is reopened, the lock_id can become invalid. It doesn't matter however,
+   as no-one's going to modify the old file anymore.
 
-   Only 4 and 5 locks are valid at this time.
+   lock_id also tells if we're referring to shared or exclusive lock. This
+   allows us to drop back to shared locking once all exclusive locks are
+   dropped. Shared locks have even numbers, exclusive locks have odd numbers.
+   The number is increased by two every time the lock is dropped or index file
+   is reopened.
 */
 
 #include "lib.h"
@@ -38,23 +29,69 @@
 
 static int mail_index_reopen(struct mail_index *index, int fd)
 {
-	int ret;
+	struct mail_index_map *old_map;
+	unsigned int old_shared_locks, old_lock_id, lock_id = 0;
+	int ret, old_fd, old_lock_type;
+
+	old_map = index->map;
+	old_fd = index->fd;
 
-	mail_index_unmap(index, index->map);
 	index->map = NULL;
+	index->hdr = NULL;
 
-	if (close(index->fd) < 0)
-		mail_index_set_syscall_error(index, "close()");
-	index->fd = fd;
+	/* new file, new locks. the old fd can keep it's locks, they don't
+	   matter anymore as no-one's going to modify the file. */
+	old_lock_type = index->lock_type;
+	old_lock_id = index->lock_id;
+	old_shared_locks = index->shared_lock_count;
+ 
+	if (index->lock_type == F_RDLCK)
+		index->lock_type = F_UNLCK;
+	index->lock_id += 2;
+	index->shared_lock_count = 0;
 
-	ret = fd < 0 ? mail_index_try_open(index, NULL) :
-		mail_index_map(index, FALSE);
-	if (ret <= 0) {
-		// FIXME: serious problem, we'll just crash later..
-		return -1;
+	if (fd != -1) {
+		index->fd = fd;
+		ret = 0;
+	} else {
+		i_assert(index->excl_lock_count == 0);
+		ret = mail_index_try_open_only(index);
+		if (ret > 0)
+			ret = mail_index_lock_shared(index, FALSE, &lock_id);
+		else if (ret == 0) {
+			/* index file is lost */
+			ret = -1;
+		}
 	}
 
-	return 0;
+	if (ret == 0) {
+		if (mail_index_map(index, FALSE) <= 0)
+			ret = -1;
+	}
+
+	if (lock_id != 0)
+		mail_index_unlock(index, lock_id);
+
+	if (ret == 0) {
+		mail_index_unmap(index, old_map);
+		if (close(old_fd) < 0)
+			mail_index_set_syscall_error(index, "close()");
+	} else {
+		if (index->map != NULL)
+			mail_index_unmap(index, index->map);
+		if (index->fd != -1) {
+			if (close(index->fd) < 0)
+				mail_index_set_syscall_error(index, "close()");
+		}
+
+		index->map = old_map;
+		index->hdr = index->map->hdr;
+		index->fd = old_fd;
+		index->lock_type = old_lock_type;
+		index->lock_id = old_lock_id;
+		index->shared_lock_count = old_shared_locks;
+	}
+	return ret;
 }
 
 static int mail_index_has_changed(struct mail_index *index)
@@ -102,6 +139,17 @@
 
 	i_assert(lock_type == F_RDLCK || lock_type == F_WRLCK);
 
+	if (lock_type == F_RDLCK && index->lock_type != F_UNLCK) {
+		index->shared_lock_count++;
+		*lock_id_r = index->lock_id;
+		return 1;
+	}
+	if (lock_type == F_WRLCK && index->lock_type == F_WRLCK) {
+		index->excl_lock_count++;
+		*lock_id_r = index->lock_id + 1;
+		return 1;
+	}
+
 	if (index->fcntl_locks_disable) {
 		/* FIXME: exclusive locking will rewrite the index file every
 		   time. shouldn't really be needed.. reading doesn't require
@@ -114,65 +162,32 @@
 		}
 		if (mail_index_lock_mprotect(index, lock_type) < 0)
 			return -1;
-		index->lock_type = lock_type;
-		return 1;
-	}
-
-	if (lock_type == F_WRLCK && index->lock_type == F_RDLCK) {
-		/* drop shared locks */
-		i_assert(index->excl_lock_count == 0);
-
-		if (file_wait_lock(index->fd, F_UNLCK) < 0)
-			mail_index_set_syscall_error(index, "file_wait_lock()");
-
-		index->shared_lock_count = 0;
-		index->lock_type = F_UNLCK;
-		index->lock_id += 2; /* make sure failures below work right */
-	}
 
-	if (index->excl_lock_count > 0 || index->shared_lock_count > 0) {
-		i_assert(lock_type == F_RDLCK || index->excl_lock_count > 0);
-		if (lock_type == F_RDLCK) {
-			index->shared_lock_count++;
-			*lock_id_r = index->lock_id;
-		} else {
-			index->excl_lock_count++;
-			*lock_id_r = index->lock_id + 1;
-		}
+		index->shared_lock_count++;
+		index->lock_type = F_RDLCK;
+		*lock_id_r = index->lock_id;
 		return 1;
 	}
 
-	i_assert(index->lock_type == F_UNLCK);
-
-	if (update_index && lock_type != F_WRLCK) {
+	if (update_index) {
 		if (mail_index_has_changed(index) < 0)
 			return -1;
 	}
 
-	do {
-		ret = file_wait_lock_full(index->fd, lock_type, timeout_secs,
-					  NULL, NULL);
-		if (ret <= 0) {
-			if (ret == 0)
-				return 0;
-			mail_index_set_syscall_error(index, "file_wait_lock()");
-			return -1;
-		}
-
-		if (lock_type == F_WRLCK) {
-			/* we need to have the latest index file locked -
-			   check if it's been updated. */
-			if ((ret = mail_index_has_changed(index)) < 0) {
-				(void)file_wait_lock(index->fd, F_UNLCK);
-				return -1;
-			}
-			if (ret > 0)
-				continue;
+	ret = file_wait_lock_full(index->fd, lock_type, timeout_secs,
+				  NULL, NULL);
+	if (ret <= 0) {
+		if (ret == 0 || errno == EDEADLK) {
+			/* deadlock equals to timeout */
+			return 0;
 		}
-	} while (0);
+		mail_index_set_syscall_error(index, "file_wait_lock()");
+		return -1;
+	}
 
+	if (index->lock_type == F_UNLCK)
+		index->lock_id += 2;
 	index->lock_type = lock_type;
-	index->lock_id += 2;
 
 	if (lock_type == F_RDLCK) {
 		index->shared_lock_count++;
@@ -215,7 +230,8 @@
 	if (fd == -1)
 		return -1;
 
-	(void)mail_index_lock_mprotect(index, F_RDLCK);
+	if (index->lock_type == F_UNLCK)
+		(void)mail_index_lock_mprotect(index, F_RDLCK);
 
 	ret = write_full(fd, index->map->hdr, sizeof(*index->map->hdr));
 	if (ret < 0 || write_full(fd, index->map->records,
@@ -224,17 +240,20 @@
 		mail_index_file_set_syscall_error(index, path, "write_full()");
 		(void)close(fd);
 		(void)unlink(path);
-		return -1;
+		fd = -1;
+	} else {
+		i_assert(index->copy_lock_path == NULL);
+		index->copy_lock_path = i_strdup(path);
 	}
 
-	i_assert(index->copy_lock_path == NULL);
-	index->copy_lock_path = i_strdup(path);
+	if (index->lock_type == F_UNLCK)
+		(void)mail_index_lock_mprotect(index, F_UNLCK);
 	return fd;
 }
 
 static int mail_index_lock_exclusive_copy(struct mail_index *index)
 {
-	int fd;
+	int fd, old_lock_type;
 
 	i_assert(index->log_locked);
 
@@ -243,31 +262,32 @@
 		return 0;
 	}
 
-	/* copy the index to index.tmp and use it. when */
+        i_assert(index->excl_lock_count == 0);
+
+	/* copy the index to index.tmp and use it */
 	fd = mail_index_copy(index);
 	if (fd == -1)
 		return -1;
 
+	old_lock_type = index->lock_type;
 	index->lock_type = F_WRLCK;
         index->excl_lock_count++;
 
 	if (mail_index_reopen(index, fd) < 0) {
-		/* FIXME: do this without another reopen which drops locks
-		   and causes potential crashes */
 		i_assert(index->excl_lock_count == 1);
+		if (unlink(index->copy_lock_path) < 0) {
+			mail_index_file_set_syscall_error(index,
+							  index->copy_lock_path,
+							  "unlink()");
+		}
 		i_free(index->copy_lock_path);
 		index->copy_lock_path = NULL;
 
-		/* go back to old index */
-		(void)mail_index_reopen(index, -1);
-
-		index->lock_type = F_UNLCK;
+		index->lock_type = old_lock_type;
 		index->excl_lock_count = 0;
-		index->shared_lock_count = 0;
 		return -1;
 	}
 
-        i_assert(index->excl_lock_count == 1);
 	return 0;
 }
 
@@ -347,10 +367,14 @@
 {
 	if ((lock_id & 1) == 0) {
 		/* shared lock */
-		if (mail_index_is_locked(index, lock_id)) {
-			i_assert(index->shared_lock_count > 0);
-			index->shared_lock_count--;
+		if (!mail_index_is_locked(index, lock_id)) {
+			/* unlocking some older generation of the index file.
+			   we've already closed the file so just ignore this. */
+			return;
 		}
+
+		i_assert(index->shared_lock_count > 0);
+		index->shared_lock_count--;
 	} else {
 		/* exclusive lock */
 		i_assert(lock_id == index->lock_id + 1);

Index: mail-index-private.h
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/mail-index-private.h,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -d -r1.4 -r1.5
--- mail-index-private.h	28 Apr 2004 02:39:03 -0000	1.4
+++ mail-index-private.h	28 Apr 2004 18:52:06 -0000	1.5
@@ -49,6 +49,7 @@
 	uoff_t log_file_offset;
 
 	struct mail_index_header hdr_copy;
+
 	unsigned int write_to_disk:1;
 };
 
@@ -69,7 +70,7 @@
 	uint32_t indexid;
 
 	int lock_type, shared_lock_count, excl_lock_count;
-	unsigned int lock_id, copy_lock_id;
+	unsigned int lock_id, opening_lock_id;
 	char *copy_lock_path;
 	struct dotlock dotlock;
 
@@ -91,6 +92,7 @@
 			    const struct mail_index_header *hdr);
 
 int mail_index_create(struct mail_index *index, struct mail_index_header *hdr);
+int mail_index_try_open_only(struct mail_index *index);
 int mail_index_try_open(struct mail_index *index, unsigned int *lock_id_r);
 int mail_index_create_tmp_file(struct mail_index *index, const char **path_r);
 

Index: mail-index-sync.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/mail-index-sync.c,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- mail-index-sync.c	28 Apr 2004 02:39:03 -0000	1.3
+++ mail-index-sync.c	28 Apr 2004 18:52:06 -0000	1.4
@@ -102,6 +102,7 @@
 					 int external)
 {
         enum mail_transaction_type flag;
+	size_t size;
 	int ret;
 
 	flag = external ? MAIL_TRANSACTION_EXTERNAL : 0;
@@ -112,6 +113,11 @@
 			mail_index_sync_sort_transaction(ctx);
 	}
 
+	ctx->expunges = buffer_get_data(ctx->expunges_buf, &size);
+	ctx->expunges_count = size / sizeof(*ctx->expunges);
+	ctx->updates = buffer_get_data(ctx->updates_buf, &size);
+	ctx->updates_count = size / sizeof(*ctx->updates);
+
 	return ret;
 }
 
@@ -136,7 +142,6 @@
 	struct mail_index_sync_ctx *ctx;
 	uint32_t seq;
 	uoff_t offset;
-	size_t size;
 	unsigned int lock_id;
 
 	if (mail_transaction_log_sync_lock(index->log, &seq, &offset) < 0)
@@ -188,11 +193,6 @@
 		return -1;
 	}
 
-	ctx->expunges = buffer_get_data(ctx->expunges_buf, &size);
-	ctx->expunges_count = size / sizeof(*ctx->expunges);
-	ctx->updates = buffer_get_data(ctx->updates_buf, &size);
-	ctx->updates_count = size / sizeof(*ctx->updates);
-
 	*ctx_r = ctx;
 	*view_r = ctx->view;
 	return 1;

Index: mail-index.c
===================================================================
RCS file: /home/cvs/dovecot/src/lib-index/mail-index.c,v
retrieving revision 1.109
retrieving revision 1.110
diff -u -d -r1.109 -r1.110
--- mail-index.c	28 Apr 2004 02:00:39 -0000	1.109
+++ mail-index.c	28 Apr 2004 18:52:06 -0000	1.110
@@ -14,8 +14,6 @@
 #include <time.h>
 #include <sys/stat.h>
 
-static int mail_index_try_open_only(struct mail_index *index);
-
 struct mail_index *mail_index_alloc(const char *dir, const char *prefix)
 {
 	struct mail_index *index;
@@ -465,7 +463,7 @@
 	return 1;
 }
 
-static int mail_index_try_open_only(struct mail_index *index)
+int mail_index_try_open_only(struct mail_index *index)
 {
 	int i;
 
@@ -530,10 +528,10 @@
 mail_index_open2(struct mail_index *index, enum mail_index_open_flags flags)
 {
 	struct mail_index_header hdr;
-	unsigned int lock_id = 0;
 	int ret;
 
-	ret = mail_index_try_open(index, &lock_id);
+	index->opening_lock_id = 0;
+	ret = mail_index_try_open(index, &index->opening_lock_id);
 	if (ret > 0)
 		hdr = *index->hdr;
 	else if (ret == 0) {
@@ -551,8 +549,10 @@
 	if (index->log == NULL)
 		return -1;
 
-	if (lock_id != 0)
-		mail_index_unlock(index, lock_id);
+	if (index->opening_lock_id != 0) {
+		mail_index_unlock(index, index->opening_lock_id);
+                index->opening_lock_id = 0;
+	}
 	return index->fd != -1 ? 1 : mail_index_create(index, &hdr);
 }
 



More information about the dovecot-cvs mailing list