[dconf/shm] Add "shm" region to avoid constantly rebuilding
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [dconf/shm] Add "shm" region to avoid constantly rebuilding
- Date: Fri, 14 May 2010 17:25:17 +0000 (UTC)
commit 7f8dbae8ff1c0c9ff486d05e6bf9a54098b1c1da
Author: Ryan Lortie <desrt desrt ca>
Date: Fri May 14 19:22:45 2010 +0200
Add "shm" region to avoid constantly rebuilding
gsettings/Makefile.am | 4 +-
gsettings/dconfdatabase.c | 70 +++++++++-
gsettings/dconfsettingsbackend.c | 1 +
gsettings/dconfshmreader.c | 217 ++++++++++++++++++++++++++++++
gsettings/dconfshmreader.h | 15 ++
service/Makefile.am | 7 +-
service/dconf-rebuilder.c | 2 +-
service/dconfchangeset.c | 78 +++++++++++
service/dconfchangeset.h | 11 ++
service/dconfjournal.c | 268 ++++++++++++++++++++++++++++++++++++++
service/dconfjournal.h | 9 ++
service/dconfshmwriter.c | 234 +++++++++++++++++++++++++++++++++
service/dconfshmwriter.h | 19 +++
service/service.c | 99 ++++++++++++++-
14 files changed, 1021 insertions(+), 13 deletions(-)
---
diff --git a/gsettings/Makefile.am b/gsettings/Makefile.am
index 41b7cc2..a5831a1 100644
--- a/gsettings/Makefile.am
+++ b/gsettings/Makefile.am
@@ -1,10 +1,12 @@
-AM_CFLAGS = $(gio_CFLAGS) -I$(top_srcdir)/gvdb
+AM_CFLAGS = $(gio_CFLAGS) -I$(top_srcdir)/gvdb -Wall -Wmissing-prototypes -g
giomodules_LTLIBRARIES = libdconfsettings.la
libdconfsettings_la_LIBADD = $(gio_LIBS)
libdconfsettings_la_SOURCES = \
../gvdb/gvdb-reader.c \
+ dconfshmreader.h \
+ dconfshmreader.c \
dconfdatabase.h \
dconfdatabase.c \
dconfsettingsbackend.c
diff --git a/gsettings/dconfdatabase.c b/gsettings/dconfdatabase.c
index 01f983b..8173c7a 100644
--- a/gsettings/dconfdatabase.c
+++ b/gsettings/dconfdatabase.c
@@ -19,13 +19,16 @@
* Author: Ryan Lortie <desrt desrt ca>
*/
+#include "dconfdatabase.h"
+
#define G_SETTINGS_ENABLE_BACKEND
#include <gio/gsettingsbackend.h>
-#include "dconfdatabase.h"
#include <gio/gio.h>
#include <string.h>
+
+#include "dconfshmreader.h"
#include "gvdb-reader.h"
typedef struct _Outstanding Outstanding;
@@ -34,10 +37,15 @@ struct _DConfDatabase
{
GStaticMutex lock;
+ gchar *filename;
+ gchar *logfile;
GDBusConnection *bus;
+ DConfShmReader *shm;
GvdbTable *value_table;
GSList *backends;
const gchar *context;
+ const gchar *log;
+ gsize log_size;
Outstanding *outstanding;
guint64 anti_expose;
@@ -246,6 +254,9 @@ dconf_database_read (DConfDatabase *database,
if (dconf_database_scan_outstanding (database, key, &value))
return value;
+ if (dconf_shm_reader_lookup (database->shm, key, &value))
+ return value;
+
if (database->value_table == NULL)
return NULL;
@@ -311,6 +322,23 @@ dconf_database_filter_function (GDBusConnection *connection,
}
}
+static void
+dconf_database_ensure_bus (DConfDatabase *database)
+{
+ if (database->bus != NULL)
+ return;
+
+ g_static_mutex_lock (&database->lock);
+ if (database->bus == NULL)
+ {
+ database->bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_dbus_connection_add_filter (database->bus,
+ dconf_database_filter_function,
+ database, NULL);
+ }
+ g_static_mutex_unlock (&database->lock);
+}
+
void
dconf_database_write_tree (DConfDatabase *database,
GTree *tree,
@@ -321,6 +349,8 @@ dconf_database_write_tree (DConfDatabase *database,
GVariant **values;
gchar *path;
+ dconf_database_ensure_bus (database);
+
serial = dconf_database_new_outstanding (database, NULL, NULL, NULL, tree);
g_settings_backend_flatten_tree (tree, &path, &keys, &values);
@@ -401,6 +431,8 @@ dconf_database_write (DConfDatabase *database,
volatile guint32 *serial;
GDBusMessage *message;
+ dconf_database_ensure_bus (database);
+
serial = dconf_database_new_outstanding (database, NULL,
path_or_key, value,
NULL);
@@ -427,6 +459,8 @@ send_match_rule (DConfDatabase *database,
GDBusMessage *message;
gchar *rule;
+ dconf_database_ensure_bus (database);
+
rule = g_strdup_printf ("interface='ca.desrt.dconf.Writer',"
"arg1path='%s'", name);
message = g_dbus_message_new_method_call ("org.freedesktop.DBus", "/",
@@ -434,6 +468,13 @@ send_match_rule (DConfDatabase *database,
g_dbus_message_set_body (message, g_variant_new ("(s)", (rule)));
g_dbus_message_set_flags (message, G_DBUS_MESSAGE_FLAGS_NO_REPLY_EXPECTED);
g_dbus_connection_send_message (database->bus, message, NULL, NULL);
+ /* FIXME: there's a small race here.
+ * if an event occurs between the time that we send this message
+ * and it gets to the bus then we could miss a change
+ *
+ * we could fix that by blocking for the reply here, but maybe
+ * it is not worth it.
+ */
g_object_unref (message);
g_free (rule);
}
@@ -452,6 +493,23 @@ dconf_database_unsubscribe (DConfDatabase *database,
send_match_rule (database, "RemoveMatch", name);
}
+static void
+dconf_database_ping (DConfDatabase *database)
+{
+ GDBusMessage *message, *reply;
+
+ dconf_database_ensure_bus (database);
+
+ message = g_dbus_message_new_method_call ("ca.desrt.dconf", "/",
+ "ca.desrt.dconf.Writer", "Ping");
+ reply = g_dbus_connection_send_message_with_reply_sync (database->bus,
+ message, -1, NULL,
+ NULL, NULL);
+ /* XXX: what if it failed? */
+ g_object_unref (message);
+ g_object_unref (reply);
+}
+
DConfDatabase *
dconf_database_get_for_backend (gpointer backend)
{
@@ -465,11 +523,11 @@ dconf_database_get_for_backend (gpointer backend)
g_static_mutex_init (&database->lock);
database->outstanding = NULL;
database->backends = g_slist_prepend (database->backends, backend);
- database->bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
- g_dbus_connection_add_filter (database->bus,
- dconf_database_filter_function,
- database, NULL);
- dconf_database_reopen_file (database);
+ database->filename = g_build_filename (g_get_user_config_dir (), "dconf", NULL);
+ database->shm = dconf_shm_reader_new ("");
+
+ if (!dconf_shm_reader_service_online (database->shm))
+ dconf_database_ping (database);
g_once_init_leave (&instance, (gsize) database);
}
diff --git a/gsettings/dconfsettingsbackend.c b/gsettings/dconfsettingsbackend.c
index 7a7d69c..96ebc28 100644
--- a/gsettings/dconfsettingsbackend.c
+++ b/gsettings/dconfsettingsbackend.c
@@ -34,6 +34,7 @@ typedef struct
DConfDatabase *database;
} DConfSettingsBackend;
+static GType dconf_settings_backend_get_type (void);
G_DEFINE_TYPE (DConfSettingsBackend,
dconf_settings_backend,
G_TYPE_SETTINGS_BACKEND)
diff --git a/gsettings/dconfshmreader.c b/gsettings/dconfshmreader.c
new file mode 100644
index 0000000..cb3a534
--- /dev/null
+++ b/gsettings/dconfshmreader.c
@@ -0,0 +1,217 @@
+#include "dconfshmreader.h"
+
+#include <gio/gio.h>
+
+#include <sys/mman.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define SHM_SIZE (4 * 1024 * 1024)
+
+struct shm_header
+{
+ guint32 prev_offset;
+ guint32 hashval;
+ guint32 datalen;
+ guint32 strlen;
+};
+
+
+struct _DConfShmReader
+{
+ gpointer data;
+ gchar *name;
+};
+
+static const gchar *
+dconf_shm_reader_get_directory (void)
+{
+ static gsize initialised;
+ static gchar *path;
+
+ if (g_once_init_enter (&initialised))
+ {
+ const gchar *envdir;
+
+ envdir = getenv ("XDG_SESSION_TMPDIR");
+
+ if (envdir == NULL)
+ {
+ GDBusMessage *message, *reply;
+ GDBusConnection *connection;
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ message = g_dbus_message_new_method_call ("ca.desrt.dconf", "/",
+ "ca.desrt.dconf.Service",
+ "GetShmDirectory");
+ reply = g_dbus_connection_send_message_with_reply_sync (connection,
+ message, -1,
+ NULL, NULL,
+ NULL);
+ g_object_unref (message);
+
+ if (reply != NULL)
+ {
+ g_variant_get (g_dbus_message_get_body (reply), "(s)", &path);
+ g_object_unref (reply);
+ }
+ }
+
+ else
+ path = g_build_filename (envdir, "dconf", NULL);
+
+ g_once_init_leave (&initialised, 1);
+ }
+
+ return path;
+}
+
+gboolean
+dconf_shm_reader_service_online (DConfShmReader *shm)
+{
+ return shm->data && !!*(guint32 *) shm->data;
+}
+
+static void
+dconf_shm_reader_map (DConfShmReader *shm)
+{
+ gchar *filename;
+ gint fd;
+
+ shm->data = NULL;
+
+ filename = g_build_filename (dconf_shm_reader_get_directory (), "shm", NULL);
+ fd = open (filename, O_RDWR | O_CREAT);
+ g_free (filename);
+
+ if (fd < 0)
+ return;
+
+ /* XXX stat first? */
+ if (posix_fallocate (fd, 0, 8) == 0)
+ {
+ shm->data = mmap (NULL, SHM_SIZE, PROT_READ, MAP_SHARED, fd, 0);
+
+ if (shm->data == MAP_FAILED)
+ shm->data = NULL;
+ }
+
+ close (fd);
+}
+
+static guint32
+dconf_shm_reader_start (DConfShmReader *shm)
+{
+ guint32 offset;
+
+ if (shm->data != NULL)
+ {
+ offset = *(guint32 *) shm->data;
+
+ if (offset < SHM_SIZE)
+ {
+ if (offset == 1)
+ offset = 0;
+
+ return offset;
+ }
+
+ munmap (shm->data, SHM_SIZE);
+ }
+
+ dconf_shm_reader_map (shm);
+
+ if (shm->data == NULL)
+ return 0;
+
+ offset = *(guint32 *) shm->data;
+
+ if (offset < SHM_SIZE)
+ {
+ if (offset == 1)
+ offset = 0;
+
+ return offset;
+ }
+
+ return 0;
+}
+
+gboolean
+dconf_shm_reader_lookup (DConfShmReader *shm,
+ const gchar *key,
+ GVariant **value)
+{
+ guint32 hashval;
+ guint32 offset;
+ gsize keylen;
+
+ offset = dconf_shm_reader_start (shm);
+
+ hashval = g_str_hash (key);
+ keylen = strlen (key) + 1;
+
+ if (hashval == 0)
+ hashval = 1;
+
+ while (offset)
+ {
+ struct shm_header *header;
+ const guchar *this_value;
+ const guchar *this_key;
+ const guchar *data;
+
+ data = shm->data;
+ data += offset;
+
+ header = (struct shm_header *) data;
+ this_value = data + sizeof *header;
+ this_key = this_value + header->datalen;
+
+ if (header->hashval == hashval && header->strlen == keylen &&
+ memcmp (this_key, key, keylen) == 0)
+ {
+ if (header->datalen)
+ {
+ GVariant *variant;
+ gpointer mydata;
+
+ mydata = g_memdup (this_value, header->datalen);
+ variant = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT,
+ mydata, header->datalen,
+ TRUE, g_free, mydata);
+ *value = g_variant_get_variant (variant);
+ g_variant_unref (variant);
+ }
+ else
+ *value = NULL;
+
+ return TRUE;
+ }
+
+ else if (header->hashval == 0 && header->strlen < keylen &&
+ memcmp (this_key, key, header->strlen) == 0)
+ {
+ *value = NULL;
+ return TRUE;
+ }
+
+ offset = header->prev_offset;
+ }
+
+ return FALSE;
+}
+
+DConfShmReader *
+dconf_shm_reader_new (const gchar *name)
+{
+ DConfShmReader *shm;
+
+ shm = g_slice_new (DConfShmReader);
+ shm->name = g_strdup (name);
+ dconf_shm_reader_map (shm);
+
+ return shm;
+}
diff --git a/gsettings/dconfshmreader.h b/gsettings/dconfshmreader.h
new file mode 100644
index 0000000..52cf3a8
--- /dev/null
+++ b/gsettings/dconfshmreader.h
@@ -0,0 +1,15 @@
+#ifndef _dconfshmreader_h_
+#define _dconfshmreader_h_
+
+#include <glib.h>
+
+typedef struct _DConfShmReader DConfShmReader;
+
+DConfShmReader * dconf_shm_reader_new (const gchar *name);
+gboolean dconf_shm_reader_lookup (DConfShmReader *shm,
+ const gchar *key,
+ GVariant **value);
+gboolean
+dconf_shm_reader_service_online (DConfShmReader *shm);
+
+#endif /* _dconfshmreader_h_ */
diff --git a/service/Makefile.am b/service/Makefile.am
index dbd509e..e8f7330 100644
--- a/service/Makefile.am
+++ b/service/Makefile.am
@@ -1,4 +1,4 @@
-AM_CFLAGS = $(gio_CFLAGS) -I$(top_srcdir)/gvdb
+AM_CFLAGS = $(gio_CFLAGS) -I$(top_srcdir)/gvdb -Wall -Wmissing-prototypes -g
libexec_PROGRAMS = dconf-service
@@ -9,7 +9,10 @@ dconf_service_LDADD = $(gio_LIBS)
dconf_service_SOURCES = \
../gvdb/gvdb-builder.c \
../gvdb/gvdb-reader.c \
- dconf-rebuilder.h \
+ dconfshmwriter.h \
+ dconfshmwriter.c \
+ dconfchangeset.h \
+ dconfchangeset.c \
dconf-rebuilder.h \
dconf-rebuilder.c \
service.c
diff --git a/service/dconf-rebuilder.c b/service/dconf-rebuilder.c
index e9a8923..93ea3f6 100644
--- a/service/dconf-rebuilder.c
+++ b/service/dconf-rebuilder.c
@@ -23,8 +23,8 @@
#include <string.h>
-#include "gvdb-reader.h"
#include "gvdb-builder.h"
+#include "gvdb-reader.h"
typedef struct
{
diff --git a/service/dconfchangeset.c b/service/dconfchangeset.c
new file mode 100644
index 0000000..5750def
--- /dev/null
+++ b/service/dconfchangeset.c
@@ -0,0 +1,78 @@
+#include "dconfchangeset.h"
+
+#include <string.h>
+
+struct _DConfChangeset
+{
+ GTree *blocks;
+ GTree *items;
+};
+
+static gint
+prefixcmp (gconstpointer a,
+ gconstpointer b)
+{
+ if (g_str_has_prefix (a, b))
+ return 0;
+
+ return strcmp (a, b);
+}
+
+DConfChangeset *
+dconf_changeset_new (void)
+{
+ DConfChangeset *set;
+
+ set = g_slice_new (DConfChangeset);
+ set->blocks = g_tree_new_full ((GCompareDataFunc) prefixcmp,
+ NULL, g_free, NULL);
+ set->items = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
+ g_free, (GDestroyNotify) g_variant_unref);
+
+ return set;
+}
+
+void
+dconf_changeset_write (DConfChangeset *set,
+ const gchar *key,
+ GVariant *value)
+{
+ if (g_tree_lookup (set->blocks, key))
+ return;
+
+ if (g_str_has_suffix (key, "/"))
+ {
+ g_assert (value == NULL);
+
+ g_tree_insert (set->blocks, g_strdup (key), (void *) 1);
+
+ return;
+ }
+
+ if (g_tree_lookup_extended (set->items, key, NULL, NULL))
+ return;
+
+ if (value != NULL)
+ g_variant_ref_sink (value);
+
+ g_tree_insert (set->items, g_strdup (key), value);
+}
+
+void
+dconf_changeset_merge (DConfChangeset *set,
+ const gchar *path,
+ GVariant *array)
+{
+ GVariantIter iter;
+ const gchar *rel;
+ GVariant *value;
+
+ g_variant_iter_init (&iter, array);
+
+ while (g_variant_iter_next (&iter, "{&sm v}", &rel, &value))
+ {
+ gchar *key = g_strconcat (path, rel, NULL);
+ dconf_changeset_write (set, key, value);
+ g_free (key);
+ }
+}
diff --git a/service/dconfchangeset.h b/service/dconfchangeset.h
new file mode 100644
index 0000000..fbfce56
--- /dev/null
+++ b/service/dconfchangeset.h
@@ -0,0 +1,11 @@
+#include <glib.h>
+
+typedef struct _DConfChangeset DConfChangeset;
+
+DConfChangeset * dconf_changeset_new (void);
+void dconf_changeset_write (DConfChangeset *set,
+ const gchar *key,
+ GVariant *value);
+void dconf_changeset_merge (DConfChangeset *set,
+ const gchar *path,
+ GVariant *items);
diff --git a/service/dconfjournal.c b/service/dconfjournal.c
new file mode 100644
index 0000000..387e736
--- /dev/null
+++ b/service/dconfjournal.c
@@ -0,0 +1,268 @@
+#include "dconfjournal.h"
+
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+struct _DConfJournal
+{
+ GString *buffer;
+ gchar *filename;
+ gboolean locked;
+ gint fd;
+};
+
+static gboolean
+dconf_journal_fcntl_lock (DConfJournal *journal,
+ gint cmd,
+ gshort type,
+ gshort whence,
+ goffset start,
+ goffset length)
+{
+ struct flock lock;
+ gint s;
+
+ lock.l_type = type;
+ lock.l_whence = whence;
+ lock.l_start = start;
+ lock.l_len = length;
+
+ do
+ s = fcntl (journal->fd, cmd, &lock);
+ while (s == -1 && errno == EINTR);
+
+ return s == 0;
+}
+
+void
+dconf_journal_lock (DConfJournal *journal)
+{
+ g_assert (!journal->locked);
+ dconf_journal_fcntl_lock (journal, F_SETLKW, F_WRLCK, SEEK_SET, 0, 0);
+ journal->locked = TRUE;
+}
+
+void
+dconf_journal_unlock (DConfJournal *journal)
+{
+ g_assert (journal->locked);
+
+ fdatasync (journal->fd);
+ dconf_journal_fcntl_lock (journal, F_SETLKW, F_UNLCK, SEEK_SET, 0, 0);
+ journal->locked = FALSE;
+}
+
+void
+dconf_journal_unlink (DConfJournal *journal)
+{
+ g_assert (journal->locked);
+
+ fdatasync (journal->fd);
+ unlink (journal->filename);
+ close (journal->fd);
+ journal->fd = -1;
+
+ journal->locked = FALSE;
+}
+
+static gboolean
+dconf_journal_read_into_string (DConfJournal *journal,
+ GString *string)
+{
+ gsize length = string->len;
+ gssize s;
+
+ g_string_set_size (string, length + 4096);
+
+ do
+ s = read (journal->fd, string->str + length, 4096);
+ while (s == -1 && errno == EINTR);
+
+ if (s < 1)
+ {
+ g_string_set_size (string, length);
+ return FALSE;
+ }
+
+ g_string_set_size (string, length + s);
+
+ return TRUE;
+}
+
+static gboolean
+dconf_journal_incoming_line (DConfJournal *journal,
+ const gchar *start,
+ const gchar *end)
+{
+ gsize length = end - start;
+ gboolean handled = FALSE;
+ gsize i;
+
+ if (length == 7 && memcmp (start, "rebuild", 7) == 0)
+ return TRUE;
+
+ for (i = 0; i < length; i++)
+ if (start[i] == ' ')
+ break;
+
+ if (i == length)
+ i = 0;
+
+ if (i == 5 && memcmp (start, "write", 5) == 0)
+ {
+ const gchar *endptr;
+ GVariant *tuple;
+
+ tuple = g_variant_parse (G_VARIANT_TYPE ("(smv)"),
+ start + i, end, &endptr, NULL);
+
+ if (tuple != NULL)
+ {
+ if (endptr == end)
+ {
+ g_print ("got a write\n");
+ handled = TRUE;
+ }
+
+ g_variant_unref (tuple);
+ }
+ }
+
+ else if (i == 5 && memcmp (start, "merge", 5) == 0)
+ {
+ const gchar *endptr;
+ GVariant *tuple;
+
+ tuple = g_variant_parse (G_VARIANT_TYPE ("(sa{smv})"),
+ start + i, end, &endptr, NULL);
+
+ if (tuple != NULL)
+ {
+ if (endptr == end)
+ {
+ g_print ("got a merge\n");
+ handled = TRUE;
+ }
+
+ g_variant_unref (tuple);
+ }
+ }
+
+ g_warning ("unhandled journal entry (corrupt journal?)");
+
+ return FALSE;
+}
+
+void
+dconf_journal_read (DConfJournal *journal)
+{
+ GString *buffer;
+ gsize scanned;
+ gsize start;
+
+ buffer = g_string_new (NULL);
+ start = scanned = 0;
+
+ while (dconf_journal_read_into_string (journal, buffer))
+ {
+ while (scanned < buffer->len)
+ {
+ if (buffer->str[scanned] == '\n')
+ {
+ gboolean rebuilt;
+
+ if (dconf_journal_incoming_line (journal,
+ buffer->str + start,
+ buffer->str + scanned))
+ {
+ g_print ("replaced by seq\n");
+ return;
+ }
+
+ start = scanned + 1;
+ }
+
+ scanned++;
+ }
+ }
+
+ g_string_free (buffer, TRUE);
+}
+
+void
+dconf_journal_append (DConfJournal *journal,
+ const gchar *text)
+{
+ g_assert (journal->locked);
+
+ lseek (journal->fd, 0, SEEK_END);
+ write (journal->fd, text, strlen (text));
+}
+
+static gboolean
+dconf_journal_is_on_nfs (DConfJournal *journal)
+{
+ return FALSE;
+}
+
+DConfJournal *
+dconf_journal_new (const gchar *filename)
+{
+ DConfJournal *journal;
+
+ journal = g_slice_new (DConfJournal);
+ journal->fd = open (filename, O_RDWR | O_CREAT, 0600);
+ journal->filename = g_strdup (filename);
+
+ return journal;
+}
+
+static void
+dconf_journal_do_append (DConfJournal *journal,
+ GString *string)
+{
+ gssize s;
+
+ do
+ s = write (journal->fd, string->str, string->len);
+ while (s == -1 && errno == EINTR);
+
+ g_assert (s == string->len);
+
+ g_string_free (string, TRUE);
+}
+
+gboolean
+dconf_journal_add_write (DConfJournal *journal,
+ GVariant *tuple)
+{
+ GString *record;
+
+ record = g_string_new ("write ");
+ g_variant_print_string (tuple, record, FALSE);
+ g_string_append_c (record, '\n');
+
+ dconf_journal_do_append (journal, record);
+}
+
+gboolean
+dconf_journal_add_merge (DConfJournal *journal,
+ GVariant *tuple)
+{
+ GString *record;
+
+ record = g_string_new ("merge ");
+ g_variant_print_string (tuple, record, FALSE);
+ g_string_append_c (record, '\n');
+
+ dconf_journal_do_append (journal, record);
+}
+
+gboolean
+dconf_journal_rebuild (DConfJournal *journal)
+{
+}
+
+
diff --git a/service/dconfjournal.h b/service/dconfjournal.h
new file mode 100644
index 0000000..b384360
--- /dev/null
+++ b/service/dconfjournal.h
@@ -0,0 +1,9 @@
+#ifndef _dconfjournal_h_
+#define _dconfjournal_h_
+
+#include <glib.h>
+
+typedef struct _DConfJournal DConfJournal;
+
+
+#endif /* _dconfjournal_h_ */
diff --git a/service/dconfshmwriter.c b/service/dconfshmwriter.c
new file mode 100644
index 0000000..3da05f8
--- /dev/null
+++ b/service/dconfshmwriter.c
@@ -0,0 +1,234 @@
+#define SHM_SIZE (4 * 1024 * 1024)
+#define SHM_ITEMS 1024
+
+#define _BSD_SOURCE
+#define _XOPEN_SOURCE 600
+
+#include "dconfshmwriter.h"
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+struct shm_header
+{
+ guint32 prev_offset;
+ guint32 hashval;
+ guint32 datalen;
+ guint32 strlen;
+};
+
+struct _DConfShmWriter
+{
+ guint32 last_ptr;
+ guint32 offset;
+ gpointer data;
+ gint n_items;
+ gint fd;
+};
+
+static inline gsize
+fill_iov (struct iovec *vector,
+ gconstpointer data,
+ gsize size)
+{
+ vector->iov_base = (void *) data;
+ vector->iov_len = size;
+
+ return size;
+}
+
+gboolean
+dconf_shm_writer_append (DConfShmWriter *shm,
+ const gchar *name,
+ GVariant *value)
+{
+ static const gchar zero[7];
+ struct shm_header header;
+ struct iovec iov[4];
+ gconstpointer data;
+ gssize result;
+ gsize total;
+
+ if (shm->n_items == SHM_ITEMS)
+ return FALSE;
+
+ header.prev_offset = shm->last_ptr;
+ header.strlen = strlen (name) + 1;
+
+ if (name[header.strlen - 1] != '/')
+ {
+ header.hashval = g_str_hash (name);
+ if (header.hashval == 0)
+ header.hashval = 1;
+ }
+ else
+ header.hashval = 0;
+
+ if (value != NULL)
+ {
+ header.datalen = g_variant_get_size (value);
+ data = g_variant_get_data (value);
+ }
+ else
+ {
+ header.datalen = 0;
+ data = NULL;
+ }
+
+ total = fill_iov (iov + 0, &header, sizeof header);
+ total += fill_iov (iov + 1, data, header.datalen);
+ total += fill_iov (iov + 2, name, header.strlen);
+ total += fill_iov (iov + 3, zero, (-total) & 7u);
+
+ if (shm->offset + total > SHM_SIZE)
+ return FALSE;
+
+ shm->last_ptr = shm->offset;
+ shm->offset += total;
+
+ result = writev (shm->fd, iov, 4);
+ g_assert (result == total);
+ shm->n_items++;
+
+ return TRUE;
+}
+
+void
+dconf_shm_writer_sync (DConfShmWriter *shm)
+{
+ guint32 output;
+ gssize result;
+
+ if (shm->last_ptr)
+ output = shm->last_ptr;
+ else
+ output = 1;
+
+ result = pwrite (shm->fd, &output, sizeof output, 0);
+ g_assert (result == sizeof output);
+}
+
+const gchar *
+dconf_shm_writer_get_path (void)
+{
+ static gsize initialised;
+ static gchar *path;
+
+ if (g_once_init_enter (&initialised))
+ {
+ gchar template[] = "/tmp/dconf-session.XXXXXX";
+ const gchar *envdir;
+
+ envdir = getenv ("XDG_SESSION_TMPDIR");
+
+ if (envdir != NULL)
+ {
+ path = g_build_filename (envdir, "dconf", NULL);
+ mkdir (path, 0700);
+ }
+ else
+ path = g_strdup (mkdtemp (template));
+
+ g_once_init_leave (&initialised, 1);
+ }
+
+ return path;
+}
+
+static gint
+prefixcmp (gconstpointer a,
+ gconstpointer b)
+{
+ if (g_str_has_prefix (a, b))
+ return 0;
+
+ return strcmp (a, b);
+}
+
+GTree *
+dconf_shm_writer_get_changes (DConfShmWriter *shm)
+{
+ guint32 offset;
+ GTree *blocks;
+ GTree *items;
+
+ items = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
+ NULL, (GDestroyNotify) g_variant_unref);
+ blocks = g_tree_new (prefixcmp);
+ offset = shm->last_ptr;
+
+ while (offset)
+ {
+ struct shm_header *header;
+ const guchar *this_value;
+ const guchar *this_key;
+ const guchar *data;
+
+ data = shm->data;
+ data += offset;
+
+ header = (struct shm_header *) data;
+ this_value = data + sizeof *header;
+ this_key = this_value + header->datalen;
+
+ if (!g_tree_lookup (blocks, this_key))
+ {
+ if (this_key[header->strlen - 2] == '/')
+ g_tree_insert (blocks, (gpointer) this_key, (gpointer) this_key);
+
+ else if (!g_tree_lookup (items, this_key))
+ {
+ GVariant *value;
+
+ value = g_variant_new_from_data (G_VARIANT_TYPE_VARIANT,
+ this_value, header->datalen,
+ TRUE, NULL, NULL);
+ g_tree_insert (items, (gpointer) this_key,
+ g_variant_ref_sink (value));
+ }
+ }
+
+ offset = header->prev_offset;
+ }
+
+ g_tree_unref (blocks);
+
+ return items;
+}
+
+DConfShmWriter *
+dconf_shm_writer_new (void)
+{
+ DConfShmWriter *shm;
+ gchar *filename;
+ gint fd;
+
+ filename = g_build_filename (dconf_shm_writer_get_path (), "shm", NULL);
+ /* NB: need O_RDWR here for emulated posix_fallocated()
+ */
+ fd = open (filename, O_RDWR | O_CREAT, 0600);
+ if (fd >= 0 && posix_fallocate (fd, 0, 8) != 0)
+ {
+ close (fd);
+ fd = -1;
+ }
+ lseek (fd, 8, SEEK_SET);
+ g_free (filename);
+
+ if (fd < 0)
+ return NULL;
+
+ shm = g_slice_new (DConfShmWriter);
+ shm->last_ptr = 0;
+ shm->offset = 8;
+ shm->data = mmap (NULL, SHM_SIZE, PROT_READ, MAP_SHARED, fd, 0);
+ shm->n_items = 0;
+ shm->fd = fd;
+
+ return shm;
+}
diff --git a/service/dconfshmwriter.h b/service/dconfshmwriter.h
new file mode 100644
index 0000000..3c225f2
--- /dev/null
+++ b/service/dconfshmwriter.h
@@ -0,0 +1,19 @@
+#ifndef _dconfshmwriter_h_
+#define _dconfshmwriter_h_
+
+#include <glib.h>
+
+typedef struct _DConfShmWriter DConfShmWriter;
+
+const gchar * dconf_shm_writer_get_path (void);
+DConfShmWriter * dconf_shm_writer_new (void);
+gboolean
+dconf_shm_writer_append (DConfShmWriter *shm,
+ const gchar *name,
+ GVariant *value);
+void dconf_shm_writer_sync (DConfShmWriter *shm);
+GTree *
+dconf_shm_writer_get_changes (DConfShmWriter *shm)
+ ;
+
+#endif /* _dconfshmwriter_h_ */
diff --git a/service/service.c b/service/service.c
index fe1fde2..3d59e72 100644
--- a/service/service.c
+++ b/service/service.c
@@ -24,6 +24,8 @@
#include <stdio.h>
#include "dconf-rebuilder.h"
+#include "dconfchangeset.h"
+#include "dconfshmwriter.h"
static const GDBusArgInfo name_arg = { -1, "name", "s" };
static const GDBusArgInfo names_arg = { -1, "names", "as" };
@@ -58,11 +60,63 @@ static const GDBusInterfaceInfo writer_interface = {
typedef struct
{
+ DConfShmWriter *shm;
GMainLoop *loop;
guint64 serial;
+ gchar *sessiondir;
gchar *path;
+ guint rebuild_timeout_id;
} DConfWriter;
+static gboolean
+do_rebuild (DConfWriter *writer,
+ DConfChangeset *changeset,
+ GError **error)
+{
+ return TRUE;
+}
+
+static gboolean
+rebuild_in_timeout (gpointer data)
+{
+ DConfWriter *writer = data;
+
+ writer->rebuild_timeout_id = 0;
+ do_rebuild (writer, NULL, NULL);
+ /* what do we do if it fails? */
+
+ return FALSE;
+}
+
+static void
+cancel_scheduled_rebuild (DConfWriter *writer)
+{
+ if (writer->rebuild_timeout_id)
+ {
+ g_source_remove (writer->rebuild_timeout_id);
+ writer->rebuild_timeout_id = 0;
+ }
+}
+
+static void
+schedule_rebuild (DConfWriter *writer)
+{
+ cancel_scheduled_rebuild (writer);
+
+ writer->rebuild_timeout_id =
+ g_timeout_add (60000, rebuild_in_timeout, writer);
+}
+
+static gboolean
+rebuild (DConfWriter *writer,
+ DConfChangeset *changeset,
+ GError **error)
+{
+ cancel_scheduled_rebuild (writer);
+
+ return do_rebuild (writer, changeset, error);
+}
+
static void
emit_notify_signal (GDBusConnection *connection,
guint64 serial,
@@ -145,7 +199,7 @@ method_call (GDBusConnection *connection,
guint64 serial;
GVariant *none;
- g_variant_get (parameters, "(@smv)", &keyvalue, &value);
+ g_variant_get (parameters, "(@sm v)", &keyvalue, &value);
key = g_variant_get_string (keyvalue, &key_length);
g_variant_unref (keyvalue);
@@ -169,6 +223,34 @@ method_call (GDBusConnection *connection,
return;
}
+ if (!dconf_shm_writer_append (writer->shm, key, value))
+ {
+ DConfChangeset *changeset;
+
+ /* the shm area is full. we need to do a rebuild immediately.
+ */
+
+ changeset = dconf_changeset_new ();
+ dconf_changeset_write (changeset, key, value);
+
+ if (!rebuild (writer, changeset, &error))
+ {
+ /* ok. that's bad news.
+ * we have no choice now.
+ */
+
+ g_variant_unref (value);
+
+ g_dbus_method_invocation_return_gerror (invocation, error);
+ g_error_free (error);
+ }
+ }
+
+ /* XXX record the journal entry here */
+
+ dconf_shm_writer_sync (writer->shm);
+ schedule_rebuild (writer);
+/*
if (!dconf_rebuilder_rebuild (writer->path, "", &key,
&value, 1, &error))
{
@@ -176,7 +258,7 @@ method_call (GDBusConnection *connection,
g_error_free (error);
return;
}
-
+*/
serial = writer->serial++;
g_dbus_method_invocation_return_value (invocation,
g_variant_new ("(t)", serial));
@@ -249,6 +331,13 @@ method_call (GDBusConnection *connection,
g_free (values);
g_free (keys);
}
+ else if (strcmp (method_name, "GetSessionDir") == 0)
+ {
+ GVariant *result;
+
+ result = g_variant_new ("(s)", writer->sessiondir);
+ g_dbus_method_invocation_return_value (invocation, result);
+ }
else
g_assert_not_reached ();
}
@@ -270,8 +359,10 @@ fake_method_call (GDBusConnection *connection,
if (strcmp (method_name, "Merge") == 0)
type = G_VARIANT_TYPE ("(sa{smv})");
- else
+ else if (strcmp (method_name, "Write") == 0)
type = G_VARIANT_TYPE ("(smv)");
+ else
+ type = G_VARIANT_TYPE ("()");
g_variant_get (parameters, "(&s)", &printed);
real_parameters = g_variant_parse (type, printed, NULL, NULL, &error);
@@ -338,6 +429,8 @@ main (void)
writer.loop = g_main_loop_new (NULL, FALSE);
writer.path = g_build_filename (g_get_user_config_dir (), "dconf", NULL);
+ writer.shm = dconf_shm_writer_new ();
+ dconf_shm_writer_sync (writer.shm);
g_bus_own_name (G_BUS_TYPE_SESSION, "ca.desrt.dconf", 0,
bus_acquired, name_acquired, name_lost, &writer, NULL);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]