[dconf] keyfile: add advisory locking



commit bdd4dc68f54f9700a9b7c13cf035b0deba03612c
Author: Ryan Lortie <desrt desrt ca>
Date:   Fri Jan 11 19:23:18 2013 -0500

    keyfile: add advisory locking
    
    Use fcntl() on a lockfile when accessing a keyfile.
    
    Now reading the keyfile, notifying local processes of changes in it,
    applying changes from local request and rewriting it is all done under a
    single acquire of the lock.  This effectively means that concurrent
    changes made to the database across several machines sharing a home
    directory over NFS will be seen by all machines as having occurred in
    the same order (decided by who won the race to the lock).

 service/dconf-keyfile-writer.c |   64 ++++++++++++++++++++++++++++++++++++++-
 1 files changed, 62 insertions(+), 2 deletions(-)
---
diff --git a/service/dconf-keyfile-writer.c b/service/dconf-keyfile-writer.c
index 2845c65..e41d0fe 100644
--- a/service/dconf-keyfile-writer.c
+++ b/service/dconf-keyfile-writer.c
@@ -23,6 +23,9 @@
 #include "dconf-writer.h"
 
 #include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
 
 typedef DConfWriterClass DConfKeyfileWriterClass;
 
@@ -30,6 +33,8 @@ typedef struct
 {
   DConfWriter   parent_instance;
   gchar        *filename;
+  gchar        *lock_filename;
+  gint          lock_fd;
   GFileMonitor *monitor;
   guint         scheduled_update;
   gchar        *contents;
@@ -159,6 +164,7 @@ dconf_keyfile_writer_begin (DConfWriter  *writer,
 
       filename_base = g_build_filename (g_get_user_config_dir (), "dconf", dconf_writer_get_name (writer), NULL);
       kfw->filename = g_strconcat (filename_base, ".txt", NULL);
+      kfw->lock_filename = g_strconcat (kfw->filename, "-lock", NULL);
       g_free (filename_base);
 
       file = g_file_new_for_path (kfw->filename);
@@ -170,6 +176,55 @@ dconf_keyfile_writer_begin (DConfWriter  *writer,
 
   g_clear_pointer (&kfw->contents, g_free);
 
+  kfw->lock_fd = open (kfw->lock_filename, O_RDWR | O_CREAT, 0666);
+  if (kfw->lock_fd == -1)
+    {
+      gchar *dirname;
+
+      /* Maybe it failed because the directory doesn't exist.  Try
+       * again, after mkdir().
+       */
+      dirname = g_path_get_dirname (kfw->lock_filename);
+      g_mkdir_with_parents (dirname, 0777);
+      g_free (dirname);
+
+      kfw->lock_fd = open (kfw->lock_filename, O_RDWR | O_CREAT, 0666);
+      if (kfw->lock_fd == -1)
+        {
+          gint saved_errno = errno;
+
+          g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
+                       "%s: %s", kfw->lock_filename, g_strerror (saved_errno));
+          return FALSE;
+        }
+    }
+
+  while (TRUE)
+    {
+      struct flock lock;
+
+      lock.l_type = F_WRLCK;
+      lock.l_whence = 0;
+      lock.l_start = 0;
+      lock.l_len = 0; /* lock all bytes */
+
+      if (fcntl (kfw->lock_fd, F_SETLKW, &lock) == 0)
+        break;
+
+      if (errno != EINTR)
+        {
+          gint saved_errno = errno;
+
+          g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno),
+                       "%s: unable to fcntl(F_SETLKW): %s", kfw->lock_filename, g_strerror (saved_errno));
+          close (kfw->lock_fd);
+          kfw->lock_fd = -1;
+          return FALSE;
+        }
+
+      /* it was EINTR.  loop again. */
+    }
+
   if (!g_file_get_contents (kfw->filename, &kfw->contents, NULL, &local_error))
     {
       if (!g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
@@ -394,6 +449,8 @@ dconf_keyfile_writer_end (DConfWriter *writer)
 
   g_clear_pointer (&kfw->keyfile, g_key_file_free);
   g_clear_pointer (&kfw->contents, g_free);
+  close (kfw->lock_fd);
+  kfw->lock_fd = -1;
 }
 
 static gboolean
@@ -421,15 +478,18 @@ dconf_keyfile_writer_finalize (GObject *object)
     g_source_remove (kfw->scheduled_update);
 
   g_clear_object (&kfw->monitor);
+  g_free (kfw->lock_filename);
   g_free (kfw->filename);
 
   G_OBJECT_CLASS (dconf_keyfile_writer_parent_class)->finalize (object);
 }
 
 static void
-dconf_keyfile_writer_init (DConfKeyfileWriter *writer)
+dconf_keyfile_writer_init (DConfKeyfileWriter *kfw)
 {
-  dconf_writer_set_basepath (DCONF_WRITER (writer), "keyfile");
+  dconf_writer_set_basepath (DCONF_WRITER (kfw), "keyfile");
+
+  kfw->lock_fd = -1;
 }
 
 static void



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