[dconf] regenerate database file when it fills
- From: Ryan Lortie <ryanl src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [dconf] regenerate database file when it fills
- Date: Tue, 18 Aug 2009 04:43:49 +0000 (UTC)
commit b129c5024c4e14ce44ad6f54e2cd27fb62a49654
Author: Ryan Lortie <desrt desrt ca>
Date: Tue Aug 18 00:43:22 2009 -0400
regenerate database file when it fills
writer/dconf-dbus-writer.c | 13 ++--
writer/dconf-writer-file.c | 169 +++++++++++++++++++++++++----------------
writer/dconf-writer-flatten.c | 121 +++++++++++++++++++++++++++++
writer/dconf-writer-merge.c | 7 +-
writer/dconf-writer-private.h | 10 +++
writer/dconf-writer.c | 7 +-
writer/dconf-writer.h | 11 ++-
7 files changed, 252 insertions(+), 86 deletions(-)
---
diff --git a/writer/dconf-dbus-writer.c b/writer/dconf-dbus-writer.c
index defdafb..2eb33c9 100644
--- a/writer/dconf-dbus-writer.c
+++ b/writer/dconf-dbus-writer.c
@@ -251,8 +251,8 @@ dconf_dbus_writer_handle_message (DConfDBusWriter *writer)
value = dconf_dbus_variant_to_gv (&iter);
dbus_message_iter_next (&iter);
- status = dconf_writer_set (writer->writer,
- key, value, &error);
+ dconf_writer_set (writer->writer, key, value);
+ status = dconf_writer_sync (writer->writer, &error);
g_variant_unref (value);
if (status == TRUE)
@@ -298,10 +298,11 @@ dconf_dbus_writer_handle_message (DConfDBusWriter *writer)
g_assert (names->len == values->len);
- status = dconf_writer_merge (writer->writer, prefix,
- (const gchar **) names->pdata,
- (GVariant **) values->pdata,
- names->len, &error);
+ dconf_writer_merge (writer->writer, prefix,
+ (const gchar **) names->pdata,
+ (GVariant **) values->pdata,
+ names->len);
+ status = dconf_writer_sync (writer->writer, &error);
for (i = 0; i < values->len; i++)
g_variant_unref (values->pdata[i]);
diff --git a/writer/dconf-writer-file.c b/writer/dconf-writer-file.c
index 423e032..7824896 100644
--- a/writer/dconf-writer-file.c
+++ b/writer/dconf-writer-file.c
@@ -47,17 +47,17 @@ dconf_writer_create_directory (const gchar *filename,
static gpointer
dconf_writer_create_temp_file (const gchar *filename,
+ gint *fd,
gint bytes,
gchar **tmpname,
GError **error)
{
gpointer contents;
gint saved_errno;
- gint fd;
*tmpname = g_strdup_printf ("%s.XXXXXX", filename);
- if ((fd = g_mkstemp (*tmpname)) < 0)
+ if ((*fd = g_mkstemp (*tmpname)) < 0)
{
saved_errno = errno;
@@ -70,7 +70,7 @@ dconf_writer_create_temp_file (const gchar *filename,
return NULL;
}
- if ((saved_errno = posix_fallocate (fd, 0, bytes)))
+ if ((saved_errno = posix_fallocate (*fd, 0, bytes)))
{
g_set_error (error, G_FILE_ERROR,
g_file_error_from_errno (saved_errno),
@@ -78,13 +78,13 @@ dconf_writer_create_temp_file (const gchar *filename,
bytes, *tmpname, g_strerror (saved_errno));
unlink (*tmpname);
g_free (*tmpname);
- close (fd);
+ close (*fd);
return NULL;
}
if ((contents = mmap (NULL, bytes, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0)) == MAP_FAILED)
+ MAP_SHARED, *fd, 0)) == MAP_FAILED)
{
saved_errno = errno;
@@ -94,14 +94,13 @@ dconf_writer_create_temp_file (const gchar *filename,
*tmpname, g_strerror (saved_errno));
unlink (*tmpname);
g_free (*tmpname);
- close (fd);
+ close (*fd);
return NULL;
}
/* surely nobody's mmap is really this evil... */
g_assert (contents != NULL);
- close (fd);
return contents;
}
@@ -129,17 +128,13 @@ dconf_writer_rename_temp (gchar *tmpname,
return TRUE;
}
-static guint
-dconf_writer_calculate_size (DConfWriter *writer)
-{
- return 4;
-}
-
gboolean
dconf_writer_create (DConfWriter *writer,
GError **error)
{
- DConfWriter new_writer;
+ struct superblock *previous_super = NULL;
+ GTree *previous_contents = NULL;
+ gsize previous_size;
gint blocks, bytes;
gpointer contents;
gchar *tmpname;
@@ -147,65 +142,88 @@ dconf_writer_create (DConfWriter *writer,
if (!dconf_writer_create_directory (writer->filename, error))
return FALSE;
- blocks = dconf_writer_calculate_size (writer);
- bytes = blocks * sizeof (struct chunk_header) * 8;
- bytes = (bytes + 4095) & ~4095;
+ /* if there was a database already open... */
+ if (writer->data.super != NULL)
+ {
+ /* store the information we need */
+ previous_contents = dconf_writer_flatten (writer);
+ previous_super = writer->data.super;
+ previous_size = (gchar *) writer->end - (gchar *) previous_super;
- if ((contents = dconf_writer_create_temp_file (writer->filename, bytes,
- &tmpname, error)) == NULL)
- return FALSE;
+ /* clear/reset the rest */
+ g_ptr_array_foreach (writer->extras, (GFunc) g_free, NULL);
+ g_ptr_array_set_size (writer->extras, 0);
+ writer->changed_pointer = NULL;
+ writer->changed_value = 0;
+ writer->data.super = NULL;
+ writer->end = NULL;
+ close (writer->fd);
+ writer->fd = -1;
+
+ /* treat an empty db as if there were no db at all */
+ if (g_tree_nnodes (previous_contents) == 0)
+ {
+ g_tree_unref (previous_contents);
+ previous_contents = NULL;
+ }
+ else
+ blocks = dconf_writer_measure_tree (previous_contents);
+ }
- new_writer.data.super = contents;
- new_writer.end = new_writer.data.blocks +
- (bytes / sizeof (struct chunk_header));
+ if (previous_contents == NULL)
+ blocks = sizeof (struct superblock) / 8;
- new_writer.data.super->signature[0] = DCONF_SIGNATURE_0;
- new_writer.data.super->signature[1] = DCONF_SIGNATURE_1;
- new_writer.data.super->next = sizeof (struct superblock) /
- sizeof (struct chunk_header);
- new_writer.data.super->root_index = 0;
+ bytes = blocks * sizeof (struct chunk_header);
+ bytes += 100;
- /* the copy process should never create extras.
- set this to NULL so that attempting to do so will crash
- */
- new_writer.extras = NULL;
- new_writer.changed_pointer = NULL;
- new_writer.changed_value = 0;
+ if ((contents = dconf_writer_create_temp_file (writer->filename,
+ &writer->fd, bytes,
+ &tmpname, error)) == NULL)
+ {
+ munmap (previous_super, previous_size);
+ return FALSE;
+ }
- if (writer->data.super)
+ writer->data.super = contents;
+ writer->end = ((gchar *) contents) + bytes;
+ writer->data.super->signature[0] = DCONF_SIGNATURE_0;
+ writer->data.super->signature[1] = DCONF_SIGNATURE_1;
+ writer->data.super->next = sizeof (struct superblock) /
+ sizeof (struct chunk_header);
+ writer->data.super->root_index = 0;
+
+ if (previous_contents != NULL)
{
- g_assert_not_reached ();
- /* copy stuff */
+ const gchar **names;
+ GVariant **values;
+ gint num;
+
+ dconf_writer_unzip_tree (previous_contents, &names, &values, &num);
+ dconf_writer_merge (writer, "", names, values, num);
+ g_tree_unref (previous_contents);
+ g_free (values);
+ g_free (names);
+
+ g_assert (writer->extras->len == 0);
+ dconf_writer_sync (writer, NULL);
}
if (!dconf_writer_rename_temp (tmpname, writer->filename, error))
{
+ munmap (previous_super, previous_size);
munmap (contents, bytes);
-
return FALSE;
}
- if (writer->data.super)
+ if (previous_super != NULL)
{
- gint i;
-
- for (i = 0; i < writer->extras->len; i++)
- g_free (g_ptr_array_index (writer->extras, i));
-
- g_ptr_array_set_size (writer->extras, 0);
-
- writer->data.super->flags |= DCONF_FLAG_STALE;
- munmap (writer->data.super,
- (gchar *) writer->end - (gchar *) writer->data.super);
+ previous_super->flags |= DCONF_FLAG_STALE;
+ munmap (previous_super, previous_size);
}
- g_assert (writer->extras->len == 0);
- new_writer.extras = writer->extras;
-
- g_assert (new_writer.changed_pointer == NULL);
- g_assert (new_writer.changed_value == 0);
-
- *writer = new_writer;
+ if G_UNLIKELY (writer->data.super->next != blocks)
+ g_critical ("New file should have been be %d blocks, but it was %d",
+ blocks, writer->data.super->next);
return TRUE;
}
@@ -216,9 +234,8 @@ dconf_writer_open (DConfWriter *writer,
{
struct superblock *super;
struct stat buf;
- gint fd;
- if ((fd = open (writer->filename, O_RDWR)) < 0)
+ if ((writer->fd = open (writer->filename, O_RDWR)) < 0)
{
gint saved_errno = errno;
@@ -230,7 +247,7 @@ dconf_writer_open (DConfWriter *writer,
return FALSE;
}
- if (fstat (fd, &buf))
+ if (fstat (writer->fd, &buf))
{
gint saved_errno = errno;
@@ -238,7 +255,7 @@ dconf_writer_open (DConfWriter *writer,
g_file_error_from_errno (saved_errno),
"failed to fstat existing dconf database %s: %s",
writer->filename, g_strerror (saved_errno));
- close (fd);
+ close (writer->fd);
return FALSE;
}
@@ -248,7 +265,7 @@ dconf_writer_open (DConfWriter *writer,
g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
"existing dconf database file %s is too small "
"(%ld < 32 bytes)", writer->filename, buf.st_size);
- close (fd);
+ close (writer->fd);
return FALSE;
}
@@ -259,14 +276,14 @@ dconf_writer_open (DConfWriter *writer,
"existing dconf database file %s must be a multiple of "
"8 bytes in size (is %ld bytes)",
writer->filename, buf.st_size);
- close (fd);
+ close (writer->fd);
return FALSE;
}
if ((super = mmap (NULL, buf.st_size,
PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0)) == MAP_FAILED)
+ MAP_SHARED, writer->fd, 0)) == MAP_FAILED)
{
gint saved_errno = errno;
@@ -274,13 +291,11 @@ dconf_writer_open (DConfWriter *writer,
g_file_error_from_errno (saved_errno),
"failed to memory-map existing dconf database file %s: %s",
writer->filename, g_strerror (saved_errno));
- close (fd);
+ close (writer->fd);
return FALSE;
}
- close (fd);
-
if (super->signature[0] != DCONF_SIGNATURE_0 ||
super->signature[1] != DCONF_SIGNATURE_1)
{
@@ -341,3 +356,25 @@ dconf_writer_new (const gchar *filename,
return writer;
}
+
+gboolean
+dconf_writer_sync (DConfWriter *writer,
+ GError **error)
+{
+ if (writer->extras->len > 0)
+ return dconf_writer_create (writer, error);
+
+ fdatasync (writer->fd);
+
+ if (writer->changed_pointer != NULL)
+ {
+ *writer->changed_pointer = writer->changed_value;
+ writer->changed_pointer = NULL;
+ writer->changed_value = 0;
+
+ fdatasync (writer->fd);
+
+ }
+
+ return TRUE;
+}
diff --git a/writer/dconf-writer-flatten.c b/writer/dconf-writer-flatten.c
index 86d2e0b..8af52bf 100644
--- a/writer/dconf-writer-flatten.c
+++ b/writer/dconf-writer-flatten.c
@@ -111,14 +111,135 @@ dconf_writer_dump_entry (gpointer key,
return FALSE;
}
+struct measure_tree_state
+{
+ const gchar *last;
+ gint blocks;
+};
+
+static gboolean
+dconf_writer_measure_entry (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ struct measure_tree_state *mts = user_data;
+ const gchar *this = key;
+ gint part = 0;
+ gint i;
+
+ /* find the common part of the path */
+ if (mts->last)
+ for (i = 0; mts->last[i] == this[i]; i++);
+ else
+ i = 0;
+
+ /* for each new directory component (ie: ending in '/'):
+ * - we need a chunk header for that directory (1 block)
+ * - we need a directory entry to point at that chunk (6 blocks)
+ * - if the name is long, we need to store it separately
+ */
+ while (this[i])
+ {
+ if (this[i++] == '/')
+ {
+ mts->blocks += sizeof (struct chunk_header) / 8;
+ mts->blocks += sizeof (struct dir_entry) / 8;
+
+ if (i - part > 36)
+ {
+ /* long filename */
+ mts->blocks += sizeof (struct chunk_header) / 8;
+ mts->blocks += (i - part + 7) / 8;
+ }
+
+ part = i;
+ }
+ }
+
+ /* for each value:
+ * - we need a chunk to store the value if it is non-atomic
+ * - we need a directory entry in any case (6 blocks)
+ * - if the name is long, we need to store it separately
+ */
+ if (! (/*atomic*/0))
+ {
+ mts->blocks += sizeof (struct chunk_header) / 8;
+ mts->blocks += (g_variant_get_size (value) + 7) / 8;
+ }
+
+ mts->blocks += sizeof (struct dir_entry) / 8;
+
+ if (i - part > 36)
+ {
+ /* long filename */
+ mts->blocks += sizeof (struct chunk_header) / 8;
+ mts->blocks += (i - part + 7) / 8;
+ }
+
+ /* next entry probably shares a lot in common with this one so save
+ * our path so that we can determine the amount of overlap.
+ */
+ mts->last = this;
+
+ return FALSE;
+}
+
+gint
+dconf_writer_measure_tree (GTree *tree)
+{
+ struct measure_tree_state state;
+
+ state.last = NULL;
+ state.blocks = sizeof (struct superblock) / 8;
+ state.blocks += sizeof (struct chunk_header) / 8;
+
+ g_tree_foreach (tree, dconf_writer_measure_entry, &state);
+
+ return state.blocks;
+}
+
void
dconf_writer_dump (DConfWriter *writer)
{
GTree *tree;
+
tree = dconf_writer_flatten (writer);
g_message ("dumping contents of dconf database");
g_tree_foreach (tree, dconf_writer_dump_entry, NULL);
g_message ("end of output");
+
+ g_message ("tree has %d blocks", dconf_writer_measure_tree (tree));
+
g_tree_unref (tree);
}
+
+static gboolean
+dconf_writer_unzip_entry (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ gpointer *(*args)[2] = user_data;
+
+ *((*args)[0]++) = key;
+ *((*args)[1]++) = value;
+
+ return FALSE;
+}
+
+void
+dconf_writer_unzip_tree (GTree *tree,
+ const gchar ***names,
+ GVariant ***values,
+ gint *num)
+{
+ gpointer args[2];
+
+ *num = g_tree_nnodes (tree);
+ *names = g_new (const gchar *, *num);
+ *values = g_new (GVariant *, *num);
+
+ args[0] = *names;
+ args[1] = *values;
+ g_tree_foreach (tree, dconf_writer_unzip_entry, args);
+}
diff --git a/writer/dconf-writer-merge.c b/writer/dconf-writer-merge.c
index b200261..41df246 100644
--- a/writer/dconf-writer-merge.c
+++ b/writer/dconf-writer-merge.c
@@ -664,13 +664,12 @@ dconf_writer_merge_index (DConfWriter *writer,
merge_state_assert_done (&state);
}
-gboolean
+void
dconf_writer_merge (DConfWriter *writer,
const gchar *prefix,
const gchar **names,
GVariant **values,
- gint n_items,
- GError **error)
+ gint n_items)
{
volatile struct superblock *super = writer->data.super;
guint32 index;
@@ -687,6 +686,4 @@ dconf_writer_merge (DConfWriter *writer,
}
else
g_assert (n_items == 1);
-
- return TRUE;
}
diff --git a/writer/dconf-writer-private.h b/writer/dconf-writer-private.h
index bbdfb60..e29b4b0 100644
--- a/writer/dconf-writer-private.h
+++ b/writer/dconf-writer-private.h
@@ -19,6 +19,7 @@
struct OPAQUE_TYPE__DConfWriter
{
gchar *filename;
+ gint fd;
union
{
@@ -90,4 +91,13 @@ dconf_writer_get_entry_value (DConfWriter *writer,
GTree *
dconf_writer_flatten (DConfWriter *writer);
+void
+dconf_writer_unzip_tree (GTree *tree,
+ const gchar ***names,
+ GVariant ***values,
+ gint *num);
+
+gint
+dconf_writer_measure_tree (GTree *tree);
+
#endif /* _dconf_writer_private_h_ */
diff --git a/writer/dconf-writer.c b/writer/dconf-writer.c
index 022ff63..503621e 100644
--- a/writer/dconf-writer.c
+++ b/writer/dconf-writer.c
@@ -239,15 +239,14 @@ dconf_writer_set_entry_name (DConfWriter *writer,
}
}
-gboolean
+void
dconf_writer_set (DConfWriter *writer,
const gchar *key,
- GVariant *value,
- GError **error)
+ GVariant *value)
{
const gchar *empty_string = "";
- return dconf_writer_merge (writer, key, &empty_string, &value, 1, error);
+ return dconf_writer_merge (writer, key, &empty_string, &value, 1);
}
GVariant *
diff --git a/writer/dconf-writer.h b/writer/dconf-writer.h
index a59b54b..0d4ee66 100644
--- a/writer/dconf-writer.h
+++ b/writer/dconf-writer.h
@@ -20,10 +20,9 @@ typedef struct OPAQUE_TYPE__DConfWriter DConfWriter;
DConfWriter * dconf_writer_new (const gchar *filename,
GError **error);
-gboolean dconf_writer_set (DConfWriter *writer,
+void dconf_writer_set (DConfWriter *writer,
const gchar *key,
- GVariant *value,
- GError **error);
+ GVariant *value);
gboolean dconf_writer_unset (DConfWriter *writer,
const gchar *key,
@@ -34,11 +33,13 @@ gboolean dconf_writer_set_locked (DConfWr
gboolean locked,
GError **error);
-gboolean dconf_writer_merge (DConfWriter *writer,
+void dconf_writer_merge (DConfWriter *writer,
const gchar *prefix,
const gchar **names,
GVariant **values,
- gint n_items,
+ gint n_items);
+
+gboolean dconf_writer_sync (DConfWriter *writer,
GError **error);
void dconf_writer_dump (DConfWriter *writer);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]