changeset 20954:faf4cd7e1009

lib: With FILE_LOCK_SLOW_WARNING_MSECS log a warning also when keeping lock for long. This can help catching which process kept the lock for too long.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 24 Oct 2016 15:59:07 +0300
parents 8a09a379627e
children 1ca4607b0a0d
files src/lib/file-lock.c
diffstat 1 files changed, 39 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib/file-lock.c	Mon Oct 24 16:57:49 2016 +0300
+++ b/src/lib/file-lock.c	Mon Oct 24 15:59:07 2016 +0300
@@ -15,6 +15,7 @@
 	int fd;
 	char *path;
 
+	struct timeval locked_time;
 	int lock_type;
 	enum file_lock_method lock_method;
 };
@@ -23,6 +24,8 @@
 static uint64_t file_lock_wait_usecs = 0;
 static long long file_lock_slow_warning_usecs = -1;
 
+static void file_lock_log_warning_if_slow(struct file_lock *lock);
+
 bool file_lock_method_parse(const char *name, enum file_lock_method *method_r)
 {
 	if (strcasecmp(name, "fcntl") == 0)
@@ -317,6 +320,8 @@
 	lock->path = i_strdup(path);
 	lock->lock_type = lock_type;
 	lock->lock_method = lock_method;
+	if (gettimeofday(&lock->locked_time, NULL) < 0)
+		i_fatal("gettimeofday() failed: %m");
 	*lock_r = lock;
 	return 1;
 }
@@ -324,9 +329,15 @@
 int file_lock_try_update(struct file_lock *lock, int lock_type)
 {
 	const char *error;
+	int ret;
 
-	return file_lock_do(lock->fd, lock->path, lock_type,
-			    lock->lock_method, 0, &error);
+	ret = file_lock_do(lock->fd, lock->path, lock_type,
+			   lock->lock_method, 0, &error);
+	if (ret <= 0)
+		return ret;
+	file_lock_log_warning_if_slow(lock);
+	lock->lock_type = lock_type;
+	return 1;
 }
 
 void file_unlock(struct file_lock **_lock)
@@ -351,6 +362,7 @@
 
 	*_lock = NULL;
 
+	file_lock_log_warning_if_slow(lock);
 	i_free(lock->path);
 	i_free(lock);
 }
@@ -382,6 +394,31 @@
 	}
 }
 
+static void file_lock_log_warning_if_slow(struct file_lock *lock)
+{
+	if (file_lock_slow_warning_usecs < 0)
+		file_lock_wait_init_warning();
+	if (file_lock_slow_warning_usecs == LLONG_MAX) {
+		/* slowness checking is disabled */
+		return;
+	}
+	if (lock->lock_type != F_WRLCK) {
+		/* some shared locks can legitimately be kept for a long time.
+		   don't warn about them. */
+		return;
+	}
+
+	struct timeval now;
+	if (gettimeofday(&now, NULL) < 0)
+		i_fatal("gettimeofday() failed: %m");
+
+	int diff = timeval_diff_msecs(&now, &lock->locked_time);
+	if (diff > file_lock_slow_warning_usecs/1000) {
+		i_warning("Lock %s kept for %d.%03d secs", lock->path,
+			  diff / 1000, diff % 1000);
+	}
+}
+
 void file_lock_wait_end(const char *lock_name)
 {
 	struct timeval now;