[gegl/swap-backend] buffer: merged TileBackendSwap and TileBackendFile



commit 90373c18016d57229291f97c951563edba95dbb3
Author: Ville Sokk <ville sokk gmail com>
Date:   Fri Feb 15 21:06:54 2013 +0200

    buffer: merged TileBackendSwap and TileBackendFile

 gegl/buffer/Makefile.am              |    4 +-
 gegl/buffer/gegl-buffer.c            |    6 +-
 gegl/buffer/gegl-tile-backend-file.c | 1427 +++++++++++++++++-----------------
 gegl/buffer/gegl-tile-backend-file.h |   54 +-
 gegl/buffer/gegl-tile-backend-swap.c | 1003 ------------------------
 gegl/buffer/gegl-tile-backend-swap.h |   65 --
 gegl/gegl-init.c                     |    4 +-
 7 files changed, 748 insertions(+), 1815 deletions(-)
---
diff --git a/gegl/buffer/Makefile.am b/gegl/buffer/Makefile.am
index 6ced32e..a4d75c2 100644
--- a/gegl/buffer/Makefile.am
+++ b/gegl/buffer/Makefile.am
@@ -16,7 +16,7 @@ noinst_LTLIBRARIES = libbuffer.la
 libbuffer_la_SOURCES = \
        gegl-aio-file.c \
        gegl-buffer-load.c      \
-       gegl-tile-backend-swap.c \
+       gegl-tile-backend-file.c \
     gegl-buffer-access.c       \
     gegl-buffer-cl-cache.c     \
     gegl-buffer-cl-iterator.c  \
@@ -49,7 +49,6 @@ libbuffer_la_SOURCES = \
     gegl-tile.c                        \
     \
        gegl-aio-file.h \
-       gegl-tile-backend-swap.h \
     gegl-buffer-cl-cache.h     \
     gegl-buffer-cl-iterator.h  \
     gegl-buffer-iterator.h     \
@@ -81,7 +80,6 @@ libbuffer_la_SOURCES = \
     gegl-tile-storage.h                \
     gegl-tile.h
 
-libbuffer_la_SOURCES += gegl-tile-backend-file-async.c
 #if HAVE_64_BIT 
 #libbuffer_la_SOURCES += gegl-tile-backend-file-mapped.c
 #else
diff --git a/gegl/buffer/gegl-buffer.c b/gegl/buffer/gegl-buffer.c
index 1ed43c8..32e06a5 100644
--- a/gegl/buffer/gegl-buffer.c
+++ b/gegl/buffer/gegl-buffer.c
@@ -53,7 +53,6 @@
 #include "gegl-tile-storage.h"
 #include "gegl-tile-backend.h"
 #include "gegl-tile-backend-file.h"
-#include "gegl-tile-backend-swap.h"
 #include "gegl-tile-backend-tiledir.h"
 #include "gegl-tile-backend-ram.h"
 #include "gegl-tile.h"
@@ -432,8 +431,7 @@ gegl_buffer_dispose (GObject *object)
       /* only flush non-internal backends,. */
       if (!(GEGL_IS_TILE_BACKEND_FILE (backend) ||
             GEGL_IS_TILE_BACKEND_RAM (backend) ||
-            GEGL_IS_TILE_BACKEND_TILE_DIR (backend) ||
-            GEGL_IS_TILE_BACKEND_SWAP (backend)))
+            GEGL_IS_TILE_BACKEND_TILE_DIR (backend)))
         gegl_buffer_flush (buffer);
 
       gegl_tile_source_reinit (GEGL_TILE_SOURCE (handler->source));
@@ -1280,7 +1278,7 @@ gegl_tile_storage_new_cached (gint tile_width, gint tile_height,
           GeglTileBackend *backend;
           item->ram = FALSE;
 
-          backend = g_object_new (GEGL_TYPE_TILE_BACKEND_SWAP,
+          backend = g_object_new (GEGL_TYPE_TILE_BACKEND_FILE,
                                   "tile-width", tile_width,
                                   "tile-height", tile_height,
                                   "format", babl_fmt,
diff --git a/gegl/buffer/gegl-tile-backend-file.c b/gegl/buffer/gegl-tile-backend-file.c
index 40bf5b0..4cdc435 100644
--- a/gegl/buffer/gegl-tile-backend-file.c
+++ b/gegl/buffer/gegl-tile-backend-file.c
@@ -1,4 +1,4 @@
-/* This file is part of GEGL.
+ /* This file is part of GEGL.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -14,29 +14,36 @@
  * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
  *
  * Copyright 2006, 2007, 2008 Ãyvind KolÃs <pippin gimp org>
+ *           2012 Ville Sokk <ville sokk gmail com>
  */
 
 #include "config.h"
 
-#include <gio/gio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
 #include <fcntl.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include <string.h>
 #include <errno.h>
 
+#ifdef G_OS_WIN32
+#include <process.h>
+#define getpid() _getpid()
+#endif
+
 #include <glib-object.h>
 #include <glib/gprintf.h>
 #include <glib/gstdio.h>
+#include <gio/gio.h>
 
 #include "gegl.h"
 #include "gegl-buffer-backend.h"
+#include "gegl-buffer-types.h"
 #include "gegl-tile-backend.h"
 #include "gegl-tile-backend-file.h"
-#include "gegl-buffer-index.h"
-#include "gegl-buffer-types.h"
+#include "gegl-aio-file.h"
 #include "gegl-debug.h"
+#include "gegl-config.h"
 
 
 #ifndef HAVE_FSYNC
@@ -48,386 +55,338 @@
 #endif
 
 
-struct _GeglTileBackendFile
-{
-  GeglTileBackend  parent_instance;
+G_DEFINE_TYPE (GeglTileBackendFile, gegl_tile_backend_file, GEGL_TYPE_TILE_BACKEND)
 
-  /* the path to our buffer */
-  gchar           *path;
+static GObjectClass * parent_class = NULL;
 
-  /* the file exist (and we've thus been able to initialize i and o,
-   * the utility call ensure_exist() should be called before any code
-   * using i and o)
-   */
-  gboolean         exist;
 
-  /* total size of file */
-  guint            total;
+typedef struct
+{
+  guint64 offset;
+  gint    rev;
+  gint    x;
+  gint    y;
+  gint    z;
+} FileEntry;
+
+typedef struct
+{
+  guint64 start;
+  guint64 end;
+} FileGap;
 
-  /* hashtable containing all entries of buffer, the index is written
-   * to the swapfile conforming to the structures laid out in
-   * gegl-buffer-index.h
-   */
-  GHashTable      *index;
+enum
+{
+  PROP_0,
+  PROP_PATH
+};
 
-  /* list of offsets to tiles that are free */
-  GSList          *free_list;
 
-  /* offset to next pre allocated tile slot */
-  guint            next_pre_alloc;
+void                     gegl_tile_backend_file_stats         (void);
+static void              gegl_tile_backend_file_dbg_alloc     (gint                      size);
+static void              gegl_tile_backend_file_dbg_dealloc   (gint                      size);
+static inline FileEntry *gegl_tile_backend_file_entry_create  (gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static guint64           gegl_tile_backend_file_find_offset   (GeglTileBackendFile      *self,
+                                                               gint                      size);
+static inline FileGap *  gegl_tile_backend_file_gap_new       (guint64                   start,
+                                                               guint64                   end);
+static void              gegl_tile_backend_file_entry_destroy (GeglTileBackendFile      *self,
+                                                               FileEntry                *entry);
+static inline FileEntry *gegl_tile_backend_file_lookup_entry  (GeglTileBackendFile      *self,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static GeglTile *        gegl_tile_backend_file_get_tile      (GeglTileSource           *self,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static gpointer          gegl_tile_backend_file_set_tile      (GeglTileSource           *self,
+                                                               GeglTile                 *tile,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static gpointer          gegl_tile_backend_file_void_tile     (GeglTileSource           *self,
+                                                               GeglTile                 *tile,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static GeglBufferHeader  gegl_tile_backend_file_read_header   (GeglTileBackendFile      *self);
+static void              gegl_tile_backend_file_write_header  (GeglTileBackendFile      *self);
+static GList*            gegl_tile_backend_file_read_index    (GeglTileBackendFile      *self);
+static void              gegl_tile_backend_file_load_index    (GeglTileBackendFile      *self);
+static gpointer          gegl_tile_backend_file_flush         (GeglTileSource           *source);
+static gpointer          gegl_tile_backend_file_exist_tile    (GeglTileSource           *self,
+                                                               GeglTile                 *tile,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static gpointer          gegl_tile_backend_file_command       (GeglTileSource           *self,
+                                                               GeglTileCommand           command,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z,
+                                                               gpointer                  data);
+static void              gegl_tile_backend_file_file_changed  (GFileMonitor             *monitor,
+                                                               GFile                    *file,
+                                                               GFile                    *other_file,
+                                                               GFileMonitorEvent         event_type,
+                                                               GeglTileBackendFile      *self);
+static guint             gegl_tile_backend_file_hashfunc      (gconstpointer             key);
+static gboolean          gegl_tile_backend_file_equalfunc     (gconstpointer             a,
+                                                               gconstpointer             b);
+static GObject *         gegl_tile_backend_file_constructor   (GType                     type,
+                                                               guint                     n_params,
+                                                               GObjectConstructParam    *params);
+static void              gegl_tile_backend_file_finalize      (GObject                  *object);
+static void              gegl_tile_backend_file_set_property  (GObject                  *object,
+                                                               guint                     property_id,
+                                                               const GValue             *value,
+                                                               GParamSpec               *pspec);
+static void              gegl_tile_backend_file_get_property  (GObject                  *object,
+                                                               guint                     property_id,
+                                                               GValue                   *value,
+                                                               GParamSpec               *pspec);
+static void              gegl_tile_backend_file_class_init    (GeglTileBackendFileClass *klass);
+static void              gegl_tile_backend_file_init          (GeglTileBackendFile      *self);
+void                     gegl_tile_backend_file_cleanup       (void);
+
+
+GeglAIOFile *swap_file     = NULL;
+GList       *swap_gap_list = NULL;
 
-  /* revision of last index sync, for cooperated sharing of a buffer
-   * file
-   */
-  guint32          rev;
 
-  /* a local copy of the header that will be written to the file, in a
-   * multiple user per buffer scenario, the flags in the header might
-   * be used for locking/signalling
-   */
-  GeglBufferHeader header;
+/* this debugging is across all buffers */
+static gint allocs         = 0;
+static gint file_size      = 0;
+static gint peak_allocs    = 0;
+static gint peak_file_size = 0;
 
-  gint            foffset;
+void
+gegl_tile_backend_file_stats (void)
+{
+  g_warning ("leaked: %i chunks (%f mb)  peak: %i (%i bytes %fmb))",
+             allocs, file_size / 1024 / 1024.0,
+             peak_allocs, peak_file_size, peak_file_size / 1024 / 1024.0);
+}
 
-  /* current offset, used when writing the index */
-  gint             offset;
+static void
+gegl_tile_backend_file_dbg_alloc (gint size)
+{
+  allocs++;
+  file_size += size;
+  if (allocs > peak_allocs)
+    peak_allocs = allocs;
+  if (file_size > peak_file_size)
+    peak_file_size = file_size;
+}
 
-  /* when writing buffer blocks the writer keeps one block unwritten
-   * at all times to be able to keep track of the ->next offsets in
-   * the blocks.
-   */
-  GeglBufferBlock *in_holding;
+static void
+gegl_tile_backend_file_dbg_dealloc (gint size)
+{
+  allocs--;
+  file_size -= size;
+}
 
-  /* loading buffer */
-  GList           *tiles;
+static inline FileEntry *
+gegl_tile_backend_file_entry_create (gint x,
+                                     gint y,
+                                     gint z)
+{
+  FileEntry *entry = g_slice_new0 (FileEntry);
 
-  /* GFile refering to our buffer */
-  GFile           *file;
+  entry->x = x;
+  entry->y = y;
+  entry->z = z;
 
-  GFileMonitor    *monitor;
-  /* for writing */
-  int              o;
+  return entry;
+}
 
-  /* for reading */
-  int              i;
-};
+static guint64
+gegl_tile_backend_file_find_offset (GeglTileBackendFile *self,
+                                    gint                 size)
+{
+  FileGap *gap;
+  guint64  offset;
 
+  if (*self->gap_list)
+    {
+      GList *link = *self->gap_list;
 
-static void     gegl_tile_backend_file_ensure_exist (GeglTileBackendFile *self);
-static gboolean gegl_tile_backend_file_write_block  (GeglTileBackendFile *self,
-                                                     GeglBufferBlock     *block);
-static void     gegl_tile_backend_file_dbg_alloc    (int                  size);
-static void     gegl_tile_backend_file_dbg_dealloc  (int                  size);
+      while (link)
+        {
+          gap = link->data;
 
+          if ((gap->end - gap->start) >= size)
+            {
+              guint64 offset = gap->start;
 
-static inline void
-gegl_tile_backend_file_file_entry_read (GeglTileBackendFile *self,
-                                        GeglBufferTile      *entry,
-                                        guchar              *dest)
-{
-  gint     to_be_read;
-  gboolean success;
-  gint     tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
-  goffset  offset = entry->offset;
-  guchar  *tdest = dest;
+              gap->start += size;
 
-  gegl_tile_backend_file_ensure_exist (self);
+              if (gap->start == gap->end)
+                {
+                  g_slice_free (FileGap, gap);
+                  *self->gap_list = g_list_remove_link (*self->gap_list, link);
+                  g_list_free (link);
+                }
 
-  if (self->foffset != offset)
-    {
-      success = (lseek (self->i, offset, SEEK_SET) >= 0);
+              return offset;
+            }
 
-      if (success == FALSE)
-        {
-          g_warning ("unable to seek to tile in buffer: %s", g_strerror (errno));
-          return;
+          link = link->next;
         }
-      self->foffset = offset;
     }
-  to_be_read = tile_size;
 
-  while (to_be_read > 0)
-    {
-      GError *error = NULL;
-      gint byte_read;
+  offset = self->file->total;
 
-      byte_read = read (self->i, tdest + tile_size - to_be_read, to_be_read);
-      if (byte_read <= 0)
-        {
-          g_message ("unable to read tile data from self: "
-                     "%s (%d/%d bytes read) %s",
-                     g_strerror (errno), byte_read, to_be_read, error?error->message:"--");
-          return;
-        }
-      to_be_read -= byte_read;
-      self->foffset += byte_read;
-    }
+  gegl_aio_file_resize (self->file, self->file->total + size + 32 * 4 * 4 * 128 * 64);
 
-  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "read entry %i,%i,%i at %i", entry->x, entry->y, entry->z, 
(gint)offset);
+  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "pushed resize to %i", (gint)self->file->total);
+
+  return offset;
 }
 
-static inline void
-gegl_tile_backend_file_file_entry_write (GeglTileBackendFile *self,
-                                         GeglBufferTile      *entry,
-                                         guchar              *source)
+static inline FileGap *
+gegl_tile_backend_file_gap_new (guint64 start,
+                                guint64 end)
 {
-  gint     to_be_written;
-  gboolean success;
-  goffset  offset = entry->offset;
-  gint     tile_size;
-
-  gegl_tile_backend_file_ensure_exist (self);
+  FileGap *gap = g_slice_new (FileGap);
 
-  if (self->foffset != offset)
-    {
-      success = (lseek (self->o, offset, SEEK_SET) >= 0);
-      if (success == FALSE)
-        {
-          g_warning ("unable to seek to tile in buffer: %s", g_strerror (errno));
-          return;
-        }
-      self->foffset = offset;
-    }
+  gap->start = start;
+  gap->end   = end;
 
-  tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
-
-  to_be_written = tile_size;
-
-  while (to_be_written > 0)
-    {
-      gint wrote;
-      wrote = write (self->o,
-                     source + tile_size - to_be_written,
-                     to_be_written);
-      if (wrote <= 0)
-        {
-          g_message ("unable to write tile data to self: "
-                     "%s (%d/%d bytes written)",
-                     g_strerror (errno), wrote, to_be_written);
-          return;
-        }
-      to_be_written -= wrote;
-      self->foffset += wrote;
-    }
-  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "wrote entry %i,%i,%i at %i", entry->x, entry->y, entry->z, 
(gint)offset);
+  return gap;
 }
 
-static inline GeglBufferTile *
-gegl_tile_backend_file_file_entry_new (GeglTileBackendFile *self)
+static void
+gegl_tile_backend_file_entry_destroy (GeglTileBackendFile *self,
+                                      FileEntry           *entry)
 {
-  GeglBufferTile *entry = gegl_tile_entry_new (0,0,0);
-
-  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "Creating new entry");
+  guint64  start, end;
+  gint     tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
+  GList   *link, *link2;
+  FileGap *gap, *gap2;
 
-  gegl_tile_backend_file_ensure_exist (self);
+  start = entry->offset;
+  end = start + tile_size;
 
-  if (self->free_list)
-    {
-      /* XXX: losing precision ?
-       * the free list seems to operate with fixed size datums and
-       * only keep track of offsets.
-       */
-      gint offset = GPOINTER_TO_INT (self->free_list->data);
-      entry->offset = offset;
-      self->free_list = g_slist_remove (self->free_list, self->free_list->data);
-
-      GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "  set offset %i from free list", ((gint)entry->offset));
-    }
-  else
+  link = *self->gap_list;
+  while (link)
     {
-      gint tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
-
-      entry->offset = self->next_pre_alloc;
-      GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "  set offset %i (next allocation)", (gint)entry->offset);
-      self->next_pre_alloc += tile_size;
+      gap = link->data;
 
-      if (self->next_pre_alloc >= self->total) /* automatic growing ensuring that
-                                                  we have room for next allocation..
-                                                */
+      if (end == gap->start)
         {
-          self->total = self->total + 32 * tile_size;
+          gap->start = start;
 
-          GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "growing file to %i bytes", (gint)self->total);
+          if (link->prev)
+            {
+              gap2 = link->prev->data;
 
-          ftruncate (self->o, self->total);
-          self->foffset = -1;
+              if (gap->start == gap2->end)
+                {
+                  gap2->end = gap->end;
+                  g_slice_free (FileGap, gap);
+                  *self->gap_list = g_list_remove_link (*self->gap_list, link);
+                  g_list_free (link);
+                }
+            }
+          break;
         }
-    }
-  gegl_tile_backend_file_dbg_alloc (gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self)));
-  return entry;
-}
-
-static inline void
-gegl_tile_backend_file_file_entry_destroy (GeglBufferTile      *entry,
-                                           GeglTileBackendFile *self)
-{
-  /* XXX: EEEk, throwing away bits */
-  guint offset = entry->offset;
-  self->free_list = g_slist_prepend (self->free_list,
-                                     GUINT_TO_POINTER (offset));
-  g_hash_table_remove (self->index, entry);
-
-  gegl_tile_backend_file_dbg_dealloc (gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self)));
-  g_free (entry);
-}
+      else if (start == gap->end)
+        {
+          gap->end = end;
 
-static gboolean
-gegl_tile_backend_file_write_header (GeglTileBackendFile *self)
-{
-  gboolean success;
+          if (link->next)
+            {
+              gap2 = link->next->data;
 
-  gegl_tile_backend_file_ensure_exist (self);
+              if (gap->end == gap2->start)
+                {
+                  gap2->start = gap->start;
+                  g_slice_free (FileGap, gap);
+                  *self->gap_list =
+                    g_list_remove_link (*self->gap_list, link);
+                  g_list_free (link);
+                }
+            }
+          break;
+        }
+      else if (end < gap->start)
+        {
+          gap = gegl_tile_backend_file_gap_new (start, end);
+
+          link2 = g_list_alloc ();
+          link2->data = gap;
+          link2->next = link;
+          link2->prev = link->prev;
+          if (link->prev)
+            link->prev->next = link2;
+          link->prev = link2;
+
+          if (link == *self->gap_list)
+            *self->gap_list = link2;
+          break;
+        }
+      else if (!link->next)
+        {
+          gap = gegl_tile_backend_file_gap_new (start, end);
+          link->next = g_list_alloc ();
+          link->next->data = gap;
+          link->next->prev = link;
+          break;
+        }
 
-  success = (lseek (self->o, 0, SEEK_SET) != -1);
-  if (success == FALSE)
-    {
-      g_warning ("unable to seek in buffer");
-      return FALSE;
+      link = link->next;
     }
-  write (self->o, &(self->header), 256);
-  self->foffset = 256;
-  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "Wrote header, next=%i", (gint)self->header.next);
-  return TRUE;
-}
 
-static gboolean
-gegl_tile_backend_file_write_block (GeglTileBackendFile *self,
-                                    GeglBufferBlock     *block)
-{
-  gegl_tile_backend_file_ensure_exist (self);
-  if (self->in_holding)
+  if (!*self->gap_list)
     {
-      guint64 next_allocation = self->offset + self->in_holding->length;
+      gap = gegl_tile_backend_file_gap_new (start, end);
+      *self->gap_list = g_list_append (*self->gap_list, gap);
+    }
 
+  link = g_list_last (*self->gap_list);
+  gap = link->data;
 
-      /* update the next offset pointer in the previous block */
-      if (block == NULL)
-          /* the previous block was the last block */
-          self->in_holding->next = 0;
-      else
-          self->in_holding->next = next_allocation;
-
-      if (self->foffset != self->offset)
-      {
-        if(lseek (self->o, self->offset, G_SEEK_SET) == -1)
-          goto fail;
-
-        self->foffset = self->offset;
-      }
-
-      /* XXX: should promiscuosuly try to compress here as well,. if revisions
-              are not matching..
-       */
-
-      GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "Wrote block: length:%i flags:%i next:%i at offset %i",
-                 self->in_holding->length,
-                 self->in_holding->flags,
-                 (gint)self->in_holding->next,
-                 (gint)self->offset);
-      {
-        ssize_t written = write (self->o, self->in_holding,
-                                 self->in_holding->length);
-        if(written != -1)
-          {
-            self->offset += written;
-            self->foffset += written;
-          }
-      }
-
-      g_assert (next_allocation == self->offset); /* true as long as
-                                                     the simple allocation
-                                                     scheme is used */
-
-      self->offset = next_allocation;
-    }
-  else
+  if (gap->end == self->file->total)
     {
-      /* we're setting up for the first write */
-
-      self->offset = self->next_pre_alloc; /* start writing header at end
-                                            * of file, worry about writing
-                                            * header inside free list later
-                                            */
-      if (self->foffset != self->offset)
-      {
-        if(lseek (self->o, self->offset, G_SEEK_SET) == -1)
-          goto fail;
-
-        self->foffset = self->offset;
-      }
+      gegl_aio_file_resize (self->file, gap->start);
+      g_slice_free (FileGap, gap);
+      *self->gap_list = g_list_remove_link (*self->gap_list, link);
+      g_list_free (link);
     }
-  self->in_holding = block;
-
-  return TRUE;
-fail:
-  g_warning ("gegl buffer index writing problems for %s",
-             self->path);
-  return FALSE;
-}
-
-G_DEFINE_TYPE (GeglTileBackendFile, gegl_tile_backend_file, GEGL_TYPE_TILE_BACKEND)
-static GObjectClass * parent_class = NULL;
-
-/* this debugging is across all buffers */
-
-static gint allocs         = 0;
-static gint file_size      = 0;
-static gint peak_allocs    = 0;
-static gint peak_file_size = 0;
-
-void
-gegl_tile_backend_file_stats (void)
-{
-  g_warning ("leaked: %i chunks (%f mb)  peak: %i (%i bytes %fmb))",
-             allocs, file_size / 1024 / 1024.0,
-             peak_allocs, peak_file_size, peak_file_size / 1024 / 1024.0);
-}
-
-static void
-gegl_tile_backend_file_dbg_alloc (gint size)
-{
-  allocs++;
-  file_size += size;
-  if (allocs > peak_allocs)
-    peak_allocs = allocs;
-  if (file_size > peak_file_size)
-    peak_file_size = file_size;
-}
 
-static void
-gegl_tile_backend_file_dbg_dealloc (gint size)
-{
-  allocs--;
-  file_size -= size;
+  g_hash_table_remove (self->index, entry);
+  g_slice_free (FileEntry, entry);
+  gegl_tile_backend_file_dbg_dealloc (gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self)));
 }
 
-static inline GeglBufferTile *
+static inline FileEntry *
 gegl_tile_backend_file_lookup_entry (GeglTileBackendFile *self,
-              gint                 x,
-              gint                 y,
-              gint                 z)
+                                     gint                 x,
+                                     gint                 y,
+                                     gint                 z)
 {
-  GeglBufferTile *ret;
-  GeglBufferTile *key = gegl_tile_entry_new (x,y,z);
+  FileEntry *ret = NULL;
+  FileEntry *key = gegl_tile_backend_file_entry_create (x, y, z);
+
   ret = g_hash_table_lookup (self->index, key);
-  gegl_tile_entry_destroy (key);
+  g_slice_free (FileEntry, key);
+
   return ret;
 }
 
-/* this is the only place that actually should
- * instantiate tiles, when the cache is large enough
- * that should make sure we don't hit this function
- * too often.
- */
 static GeglTile *
 gegl_tile_backend_file_get_tile (GeglTileSource *self,
-          gint            x,
-          gint            y,
-          gint            z)
+                                 gint            x,
+                                 gint            y,
+                                 gint            z)
 {
   GeglTileBackend     *backend;
   GeglTileBackendFile *tile_backend_file;
-  GeglBufferTile      *entry;
+  FileEntry           *entry;
   GeglTile            *tile = NULL;
   gint                 tile_size;
 
@@ -443,7 +402,11 @@ gegl_tile_backend_file_get_tile (GeglTileSource *self,
   gegl_tile_set_rev (tile, entry->rev);
   gegl_tile_mark_as_stored (tile);
 
-  gegl_tile_backend_file_file_entry_read (tile_backend_file, entry, gegl_tile_get_data (tile));
+  gegl_aio_file_read (tile_backend_file->file, entry->offset, tile_size,
+                      gegl_tile_get_data (tile));
+
+  GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "read entry %i, %i, %i from %i", entry->x, entry->y, entry->z, 
(gint)entry->offset);
+
   return tile;
 }
 
@@ -456,24 +419,30 @@ gegl_tile_backend_file_set_tile (GeglTileSource *self,
 {
   GeglTileBackend     *backend;
   GeglTileBackendFile *tile_backend_file;
-  GeglBufferTile      *entry;
+  FileEntry           *entry;
+  gint                 tile_size;
 
   backend           = GEGL_TILE_BACKEND (self);
   tile_backend_file = GEGL_TILE_BACKEND_FILE (backend);
+  tile_size         = gegl_tile_backend_get_tile_size (backend);
   entry             = gegl_tile_backend_file_lookup_entry (tile_backend_file, x, y, z);
 
   if (entry == NULL)
     {
-      entry    = gegl_tile_backend_file_file_entry_new (tile_backend_file);
-      entry->x = x;
-      entry->y = y;
-      entry->z = z;
+      entry          = gegl_tile_backend_file_entry_create (x, y, z);
+      entry->offset  = gegl_tile_backend_file_find_offset (tile_backend_file, tile_size);
       g_hash_table_insert (tile_backend_file->index, entry, entry);
+      gegl_tile_backend_file_dbg_alloc (tile_size);
     }
-  entry->rev = gegl_tile_get_rev (tile);
 
-  gegl_tile_backend_file_file_entry_write (tile_backend_file, entry, gegl_tile_get_data (tile));
+  gegl_aio_file_write (tile_backend_file->file, entry->offset,
+                       tile_size,
+                       gegl_tile_get_data (tile));
+
   gegl_tile_mark_as_stored (tile);
+
+  GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "pushed write of entry %i, %i, %i at %i", entry->x, entry->y, entry->z, 
(gint)entry->offset);
+
   return NULL;
 }
 
@@ -486,7 +455,7 @@ gegl_tile_backend_file_void_tile (GeglTileSource *self,
 {
   GeglTileBackend     *backend;
   GeglTileBackendFile *tile_backend_file;
-  GeglBufferTile      *entry;
+  FileEntry           *entry;
 
   backend           = GEGL_TILE_BACKEND (self);
   tile_backend_file = GEGL_TILE_BACKEND_FILE (backend);
@@ -494,87 +463,270 @@ gegl_tile_backend_file_void_tile (GeglTileSource *self,
 
   if (entry != NULL)
     {
-      gegl_tile_backend_file_file_entry_destroy (entry, tile_backend_file);
+      GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "void tile %i, %i, %i", x, y, z);
+
+      gegl_tile_backend_file_entry_destroy (tile_backend_file, entry);
     }
 
   return NULL;
 }
 
-static gpointer
-gegl_tile_backend_file_exist_tile (GeglTileSource *self,
-                                   GeglTile       *tile,
-                                   gint            x,
-                                   gint            y,
-                                   gint            z)
+static GeglBufferHeader
+gegl_tile_backend_file_read_header (GeglTileBackendFile *self)
 {
-  GeglTileBackend     *backend;
-  GeglTileBackendFile *tile_backend_file;
-  GeglBufferTile      *entry;
+  GeglBufferHeader ret;
 
-  backend           = GEGL_TILE_BACKEND (self);
-  tile_backend_file = GEGL_TILE_BACKEND_FILE (backend);
-  entry             = gegl_tile_backend_file_lookup_entry (tile_backend_file, x, y, z);
+  gegl_aio_file_read(self->file, 0, sizeof (GeglBufferHeader), (guchar*)&ret);
 
-  return entry!=NULL?((gpointer)0x1):NULL;
+  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "read header: tile-width: %i tile-height: %i next:%i  %ix%i\n",
+             ret.tile_width,
+             ret.tile_height,
+             (guint)ret.next,
+             ret.width,
+             ret.height);
+
+  if (!(ret.magic[0]=='G' &&
+       ret.magic[1]=='E' &&
+       ret.magic[2]=='G' &&
+       ret.magic[3]=='L'))
+    {
+      g_warning ("Magic is wrong! %s", ret.magic);
+    }
+
+  return ret;
 }
 
+static void
+gegl_tile_backend_file_write_header (GeglTileBackendFile *self)
+{
+  gegl_aio_file_write (self->file, 0, sizeof (GeglBufferHeader), (guchar*)&self->header);
+}
 
-static gpointer
-gegl_tile_backend_file_flush (GeglTileSource *source,
-                              GeglTile       *tile,
-                              gint            x,
-                              gint            y,
-                              gint            z)
+static GList *
+gegl_tile_backend_file_read_index (GeglTileBackendFile *self)
 {
-  GeglTileBackend     *backend;
-  GeglTileBackendFile *self;
-  GList               *tiles;
+  GList          *ret    = NULL;
+  guint64         offset = self->header.next;
+  GeglBufferTile *item;
 
-  backend  = GEGL_TILE_BACKEND (source);
-  self     = GEGL_TILE_BACKEND_FILE (backend);
+  while (offset) /* ->next of the last block is 0 */
+    {
+      item = g_malloc (sizeof (GeglBufferTile));
+      gegl_aio_file_read (self->file, offset, sizeof (GeglBufferTile), (guchar*)item);
+      offset = item->block.next;
+      ret = g_list_prepend (ret, item);
+    }
 
-  gegl_tile_backend_file_ensure_exist (self);
+  ret = g_list_reverse (ret);
 
-  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "flushing %s", self->path);
+  return ret;
+}
 
+static void
+gegl_tile_backend_file_load_index (GeglTileBackendFile *self)
+{
+  GeglBufferHeader  new_header;
+  GList            *tiles, *iter;
+  GeglTileBackend  *backend = GEGL_TILE_BACKEND (self);
+  guint64           max     = 0;
+  gint              tile_size;
 
-  self->header.rev ++;
-  self->header.next = self->next_pre_alloc; /* this is the offset
-                                               we start handing
-                                               out headers from*/
-  tiles = g_hash_table_get_keys (self->index);
+  new_header = gegl_tile_backend_file_read_header (self);
 
-  if (tiles == NULL)
-    self->header.next = 0;
-  else
+  while (new_header.flags & GEGL_FLAG_LOCKED)
     {
-      GList *iter;
-      for (iter = tiles; iter; iter = iter->next)
-        {
-          GeglBufferItem *item = iter->data;
+      g_usleep (50000);
+      new_header = gegl_tile_backend_file_read_header (self);
+    }
 
-          gegl_tile_backend_file_write_block (self, &item->block);
-        }
-      gegl_tile_backend_file_write_block (self, NULL); /* terminate the index */
-      g_list_free (tiles);
+  if (new_header.rev == self->header.rev)
+    {
+      GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "header not changed: %s", self->path);
+      return;
+    }
+  else
+    {
+      self->header = new_header;
+      GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "loading index: %s", self->path);
     }
 
-  gegl_tile_backend_file_write_header (self);
-  fsync (self->o);
+  tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
+  tiles = gegl_tile_backend_file_read_index (self);
 
-  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "flushed %s", self->path);
+  for (iter = tiles; iter; iter = iter->next)
+    {
+      GeglBufferTile *item           = iter->data;
+      FileEntry      *new, *existing =
+        gegl_tile_backend_file_lookup_entry (self, item->x, item->y, item->z);
 
-  return (gpointer)0xf0f;
-}
+      if (item->offset > max)
+        max = item->offset + tile_size;
 
-enum
-{
-  PROP_0,
-  PROP_PATH
-};
+      if (existing)
+        {
+          if (existing->rev == item->rev)
+            {
+              g_assert (existing->offset == item->offset);
+              g_free (item);
+              continue;
+            }
+          else
+            {
+              GeglTileStorage *storage =
+                (void*) gegl_tile_backend_peek_storage (backend);
+              GeglRectangle rect;
 
-static gpointer
-gegl_tile_backend_file_command (GeglTileSource  *self,
+              g_hash_table_remove (self->index, existing);
+              gegl_tile_source_refetch (GEGL_TILE_SOURCE (storage),
+                                        existing->x,
+                                        existing->y,
+                                        existing->z);
+
+              if (existing->z == 0)
+                {
+                  rect.width = self->header.tile_width;
+                  rect.height = self->header.tile_height;
+                  rect.x = existing->x * self->header.tile_width;
+                  rect.y = existing->y * self->header.tile_height;
+                }
+              g_free (existing);
+              g_signal_emit_by_name (storage, "changed", &rect, NULL);
+            }
+        }
+
+      /* we've found a new tile */
+      new = gegl_tile_backend_file_entry_create (item->x, item->y, item->z);
+      new->rev = item->rev;
+      new->offset = item->offset;
+      g_hash_table_insert (self->index, new, new);
+      g_free (item);
+    }
+
+  g_list_free (tiles);
+  g_list_free (*self->gap_list);
+  *self->gap_list   = NULL;
+  self->file->total = max;
+}
+
+static gpointer
+gegl_tile_backend_file_flush (GeglTileSource *source)
+{
+  GeglTileBackend     *backend;
+  GeglTileBackendFile *self;
+  GList               *tiles;
+
+  backend  = GEGL_TILE_BACKEND (source);
+  self     = GEGL_TILE_BACKEND_FILE (backend);
+
+  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "flushing %s", self->path);
+
+  self->header.rev++;
+  tiles = g_hash_table_get_keys (self->index);
+
+  if (tiles == NULL)
+    self->header.next = 0;
+  else
+    {
+      GList           *iter;
+      guchar          *index;
+      guint64          index_offset;
+      gint             index_len;
+      GeglBufferBlock *previous = NULL;
+
+      index_len    = g_list_length (tiles) * sizeof (GeglBufferTile);
+      index        = g_malloc (index_len);
+      index_offset = gegl_tile_backend_file_find_offset (self, index_len);
+      index_len    = 0;
+
+      for (iter = tiles; iter; iter = iter->next)
+        {
+          FileEntry      *item = iter->data;
+          GeglBufferTile  entry;
+
+          entry.x            = item->x;
+          entry.y            = item->y;
+          entry.z            = item->z;
+          entry.rev          = item->rev;
+          entry.offset       = item->offset;
+          entry.block.flags  = GEGL_FLAG_TILE;
+          entry.block.length = sizeof (GeglBufferTile);
+
+          memcpy (index + index_len, &entry, sizeof (GeglBufferTile));
+
+          if (previous)
+            previous->next = index_offset + index_len;
+
+          previous = (GeglBufferBlock *)(index + index_len);
+
+          index_len += sizeof (GeglBufferTile);
+        }
+
+      previous->next = 0; /* last block points to offset 0 */
+      self->header.next = index_offset;
+      gegl_aio_file_write (self->file, index_offset, index_len, index);
+      g_free (index);
+      g_list_free (tiles);
+    }
+
+  gegl_tile_backend_file_write_header (self);
+  gegl_aio_file_sync (self->file);
+
+  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "flushed %s", self->path);
+
+  return (gpointer)0xf0f;
+}
+
+static gpointer
+gegl_tile_backend_file_exist_tile (GeglTileSource *self,
+                                   GeglTile       *tile,
+                                   gint            x,
+                                   gint            y,
+                                   gint            z)
+{
+  GeglTileBackend     *backend;
+  GeglTileBackendFile *tile_backend_file;
+  FileEntry           *entry;
+
+  backend           = GEGL_TILE_BACKEND (self);
+  tile_backend_file = GEGL_TILE_BACKEND_FILE (backend);
+  entry             = gegl_tile_backend_file_lookup_entry (tile_backend_file, x, y, z);
+
+  return entry!=NULL?((gpointer)0x1):NULL;
+}
+
+gboolean
+gegl_tile_backend_file_try_lock (GeglTileBackendFile *self)
+{
+  GeglBufferHeader new_header = gegl_tile_backend_file_read_header (self);
+
+  if (new_header.flags & GEGL_FLAG_LOCKED)
+    return FALSE;
+
+  self->header.flags += GEGL_FLAG_LOCKED;
+  gegl_tile_backend_file_write_header (self);
+  gegl_aio_file_sync (self->file);
+
+  return TRUE;
+}
+
+gboolean
+gegl_tile_backend_file_unlock (GeglTileBackendFile *self)
+{
+  if (!(self->header.flags & GEGL_FLAG_LOCKED))
+    {
+      g_warning ("tried to unlock unlocked buffer");
+      return FALSE;
+    }
+
+  self->header.flags -= GEGL_FLAG_LOCKED;
+  gegl_tile_backend_file_write_header (self);
+  gegl_aio_file_sync (self->file);
+
+  return TRUE;
+}
+
+static gpointer
+gegl_tile_backend_file_command (GeglTileSource  *self,
                                 GeglTileCommand  command,
                                 gint             x,
                                 gint             y,
@@ -587,20 +739,14 @@ gegl_tile_backend_file_command (GeglTileSource  *self,
         return gegl_tile_backend_file_get_tile (self, x, y, z);
       case GEGL_TILE_SET:
         return gegl_tile_backend_file_set_tile (self, data, x, y, z);
-
       case GEGL_TILE_IDLE:
-        return NULL;       /* we could perhaps lazily be writing indexes
-                            * at some intervals, making it work as an
-                            * autosave for the buffer?
-                            */
-
+        return NULL;
       case GEGL_TILE_VOID:
         return gegl_tile_backend_file_void_tile (self, data, x, y, z);
-
       case GEGL_TILE_EXIST:
         return gegl_tile_backend_file_exist_tile (self, data, x, y, z);
       case GEGL_TILE_FLUSH:
-        return gegl_tile_backend_file_flush (self, data, x, y, z);
+        return gegl_tile_backend_file_flush (self);
 
       default:
         g_assert (command < GEGL_TILE_LAST_COMMAND &&
@@ -610,92 +756,25 @@ gegl_tile_backend_file_command (GeglTileSource  *self,
 }
 
 static void
-gegl_tile_backend_file_set_property (GObject      *object,
-                                     guint         property_id,
-                                     const GValue *value,
-                                     GParamSpec   *pspec)
-{
-  GeglTileBackendFile *self = GEGL_TILE_BACKEND_FILE (object);
-
-  switch (property_id)
-    {
-      case PROP_PATH:
-        if (self->path)
-          g_free (self->path);
-        self->path = g_value_dup_string (value);
-        break;
-
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-        break;
-    }
-}
-
-static void
-gegl_tile_backend_file_get_property (GObject    *object,
-                                     guint       property_id,
-                                     GValue     *value,
-                                     GParamSpec *pspec)
-{
-  GeglTileBackendFile *self = GEGL_TILE_BACKEND_FILE (object);
-
-  switch (property_id)
-    {
-      case PROP_PATH:
-        g_value_set_string (value, self->path);
-        break;
-
-      default:
-        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-        break;
-    }
-}
-
-static void
-gegl_tile_backend_file_finalize (GObject *object)
+gegl_tile_backend_file_file_changed (GFileMonitor        *monitor,
+                                     GFile               *file,
+                                     GFile               *other_file,
+                                     GFileMonitorEvent    event_type,
+                                     GeglTileBackendFile *self)
 {
-  GeglTileBackendFile *self = (GeglTileBackendFile *) object;
-
-  if (self->index)
-    g_hash_table_unref (self->index);
-
-  if (self->exist)
-    {
-      GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "finalizing buffer %s", self->path);
-
-      if (self->i != -1)
-        {
-          close (self->i);
-          self->i = -1;
-        }
-      if (self->o != -1)
-        {
-          close (self->o);
-          self->o = -1;
-        }
-    }
-
-  if (self->path)
-    g_free (self->path);
-
-  if (self->monitor)
-    g_object_unref (self->monitor);
-
-  if (self->file)
-    g_object_unref (self->file);
-
-  (*G_OBJECT_CLASS (parent_class)->finalize)(object);
+  if (event_type == G_FILE_MONITOR_EVENT_CHANGED)
+    gegl_tile_backend_file_load_index (self);
 }
 
 static guint
 gegl_tile_backend_file_hashfunc (gconstpointer key)
 {
-  const GeglBufferTile *e = key;
+  const FileEntry *entry = key;
   guint            hash;
   gint             i;
-  gint             srcA = e->x;
-  gint             srcB = e->y;
-  gint             srcC = e->z;
+  gint             srcA  = entry->x;
+  gint             srcB  = entry->y;
+  gint             srcC  = entry->z;
 
   /* interleave the 10 least significant bits of all coordinates,
    * this gives us Z-order / morton order of the space and should
@@ -715,10 +794,10 @@ gegl_tile_backend_file_hashfunc (gconstpointer key)
 
 static gboolean
 gegl_tile_backend_file_equalfunc (gconstpointer a,
-           gconstpointer b)
+                                  gconstpointer b)
 {
-  const GeglBufferTile *ea = a;
-  const GeglBufferTile *eb = b;
+  const FileEntry *ea = a;
+  const FileEntry *eb = b;
 
   if (ea->x == eb->x &&
       ea->y == eb->y &&
@@ -728,260 +807,217 @@ gegl_tile_backend_file_equalfunc (gconstpointer a,
   return FALSE;
 }
 
-
-static void
-gegl_tile_backend_file_load_index (GeglTileBackendFile *self,
-                                   gboolean             block)
+static GObject *
+gegl_tile_backend_file_constructor (GType                  type,
+                                    guint                  n_params,
+                                    GObjectConstructParam *params)
 {
-  GeglBufferHeader new_header;
-  GList           *iter;
-  GeglTileBackend *backend;
-  goffset offset = 0;
-  goffset max=0;
-  gint tile_size;
-
-  /* compute total from and next pre alloc by monitoring tiles as they
-   * are added here
-   */
-  /* reload header */
-  new_header = gegl_buffer_read_header (self->i, &offset, NULL)->header;
-  self->foffset = 256;
+  GObject             *object;
+  GeglTileBackendFile *self;
+  GeglTileBackend     *backend;
 
-  while (new_header.flags & GEGL_FLAG_LOCKED)
-    {
-      g_usleep (50000);
-      new_header = gegl_buffer_read_header (self->i, &offset, NULL)->header;
-      self->foffset = 256;
-    }
+  object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);
+  self   = GEGL_TILE_BACKEND_FILE (object);
+  backend = GEGL_TILE_BACKEND (self);
 
-  if (new_header.rev == self->header.rev)
-    {
-      GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "header not changed: %s", self->path);
-      return;
-    }
-  else
+  self->index = g_hash_table_new (gegl_tile_backend_file_hashfunc,
+                                  gegl_tile_backend_file_equalfunc);
+
+  if (self->path)
     {
-      self->header=new_header;
-      GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "loading index: %s", self->path);
-    }
+      /* if the path is specified, use a separate file for this buffer */
 
-  tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
-  offset      = self->header.next;
-  self->tiles = gegl_buffer_read_index (self->i, &offset, NULL);
-  self->foffset = -1;
-  backend     = GEGL_TILE_BACKEND (self);
+      self->file = g_object_new (GEGL_TYPE_AIO_FILE, "path", self->path, NULL);
+      self->gap_list = g_malloc (sizeof (void*));
+      *self->gap_list = NULL;
 
-  for (iter = self->tiles; iter; iter=iter->next)
+      if (g_access (self->path, F_OK) != -1)
+        {
+          /* if we can access the file, assume that it was created by another
+            process and read it */
+          self->gfile = g_file_new_for_commandline_arg (self->path);
+          self->monitor = g_file_monitor_file (self->gfile,
+                                               G_FILE_MONITOR_NONE,
+                                               NULL, NULL);
+          g_signal_connect (self->monitor, "changed",
+                            G_CALLBACK (gegl_tile_backend_file_file_changed),
+                            self);
+          self->header = gegl_tile_backend_file_read_header (self);
+          self->header.rev -= 1;
+
+          /* we are overriding all of the work of the actual constructor here,
+           * a really evil hack :d
+           */
+          backend->priv->tile_width = self->header.tile_width;
+          backend->priv->tile_height = self->header.tile_height;
+          backend->priv->format = babl_format (self->header.description);
+          backend->priv->px_size = babl_format_get_bytes_per_pixel (backend->priv->format);
+          backend->priv->tile_size = backend->priv->tile_width *
+            backend->priv->tile_height *
+            backend->priv->px_size;
+          backend->priv->shared = TRUE;
+
+          gegl_tile_backend_file_load_index (self);
+        }
+      else
+        {
+          gegl_buffer_header_init (&self->header,
+                                   backend->priv->tile_width,
+                                   backend->priv->tile_height,
+                                   backend->priv->px_size,
+                                   backend->priv->format);
+        }
+    }
+  else
     {
-      GeglBufferItem *item = iter->data;
+      /* use a file that's shared between multiple buffers */
+      if (swap_file == NULL)
+        {
+          gchar *filename, *path;
 
-      GeglBufferItem *existing = g_hash_table_lookup (self->index, item);
+          filename  = g_strdup_printf ("%i-common-swap-file.swap", getpid ());
+          path      = g_build_filename (gegl_config ()->swap, filename, NULL);
+          swap_file = g_object_new (GEGL_TYPE_AIO_FILE, "path", path, NULL);
+          g_free (filename);
+          g_free (path);
+        }
 
-      if (item->tile.offset > max)
-        max = item->tile.offset + tile_size;
+      self->gap_list = &swap_gap_list;
+      self->file = swap_file;
 
-      if (existing)
-        {
-          if (existing->tile.rev == item->tile.rev)
-            {
-              g_assert (existing->tile.offset == item->tile.offset);
-              existing->tile = item->tile;
-              g_free (item);
-              continue;
-            }
-          else
-            {
-              GeglTileStorage *storage =
-                (void*)gegl_tile_backend_peek_storage (backend);
-              GeglRectangle rect;
-              g_hash_table_remove (self->index, existing);
+      gegl_buffer_header_init (&self->header,
+                               backend->priv->tile_width,
+                               backend->priv->tile_height,
+                               backend->priv->px_size,
+                               backend->priv->format);
+    }
 
-              gegl_tile_source_refetch (GEGL_TILE_SOURCE (storage),
-                                        existing->tile.x,
-                                        existing->tile.y,
-                                        existing->tile.z);
+  backend->priv->header = &self->header;
 
-              if (existing->tile.z == 0)
-                {
-                  rect.width = self->header.tile_width;
-                  rect.height = self->header.tile_height;
-                  rect.x = existing->tile.x * self->header.tile_width;
-                  rect.y = existing->tile.y * self->header.tile_height;
-                }
-              g_free (existing);
+  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "constructing file backend");
 
-              g_signal_emit_by_name (storage, "changed", &rect, NULL);
-            }
-        }
-      g_hash_table_insert (self->index, iter->data, iter->data);
-    }
-  g_list_free (self->tiles);
-  g_slist_free (self->free_list);
-  self->free_list      = NULL;
-  self->next_pre_alloc = max; /* if bigger than own? */
-  self->total          = max;
-  self->tiles          = NULL;
+  return object;
 }
 
 static void
-gegl_tile_backend_file_file_changed (GFileMonitor        *monitor,
-              GFile               *file,
-              GFile               *other_file,
-              GFileMonitorEvent    event_type,
-              GeglTileBackendFile *self)
+gegl_tile_backend_file_finalize (GObject *object)
 {
-  if (event_type == G_FILE_MONITOR_EVENT_CHANGED /*G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT*/ )
+  GeglTileBackendFile *self = GEGL_TILE_BACKEND_FILE (object);
+
+  if (self->index)
     {
-      gegl_tile_backend_file_load_index (self, TRUE);
-      self->foffset = -1;
-    }
-}
+      GList *tiles = g_hash_table_get_keys (self->index);
 
-static GObject *
-gegl_tile_backend_file_constructor (GType                  type,
-                                    guint                  n_params,
-                                    GObjectConstructParam *params)
-{
-  GObject             *object;
-  GeglTileBackendFile *self;
-  GeglTileBackend     *backend;
+      if (tiles != NULL)
+        {
+          GList *iter;
 
-  object  = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);
-  self    = GEGL_TILE_BACKEND_FILE (object);
-  backend = GEGL_TILE_BACKEND (object);
+          for (iter = tiles; iter; iter = iter->next)
+            gegl_tile_backend_file_entry_destroy (self, iter->data);
+        }
 
-  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "constructing file backend: %s", self->path);
+      g_list_free (tiles);
 
-  self->file = g_file_new_for_commandline_arg (self->path);
-  self->i = self->o = -1;
-  self->index = g_hash_table_new (gegl_tile_backend_file_hashfunc, gegl_tile_backend_file_equalfunc);
+      g_hash_table_unref (self->index);
 
+      self->index = NULL;
+    }
 
-  /* If the file already exists open it, assuming it is a GeglBuffer. */
-  if (g_access (self->path, F_OK) != -1)
+  if (self->file && self->file != swap_file)
     {
-      goffset offset = 0;
-
-      /* Install a monitor for changes to the file in case other applications
-       * might be writing to the buffer
-       */
-      self->monitor = g_file_monitor_file (self->file, G_FILE_MONITOR_NONE,
-                                           NULL, NULL);
-      g_signal_connect (self->monitor, "changed",
-                        G_CALLBACK (gegl_tile_backend_file_file_changed),
-                        self);
-
-      self->o = g_open (self->path, O_RDWR|O_CREAT, 0770);
-      if (self->o == -1)
-        {
-          /* Try again but this time with only read access. This is
-           * a quick-fix for make distcheck, where img_cmp fails
-           * when it opens a GeglBuffer file in the source tree
-           * (which is read-only).
-           */
-          self->o = g_open (self->path, O_RDONLY, 0770);
+      g_object_unref (self->file);
+      self->file = NULL;
+    }
 
-          if (self->o == -1)
-            g_warning ("%s: Could not open '%s': %s", G_STRFUNC, self->path, g_strerror (errno));
-        }
-      self->i = dup (self->o);
-
-      self->header = gegl_buffer_read_header (self->i, &offset, NULL)->header;
-      self->header.rev = self->header.rev -1;
-
-      /* we are overriding all of the work of the actual constructor here,
-       * a really evil hack :d
-       */
-      backend->priv->tile_width = self->header.tile_width;
-      backend->priv->tile_height = self->header.tile_height;
-      backend->priv->format = babl_format (self->header.description);
-      backend->priv->px_size = babl_format_get_bytes_per_pixel (backend->priv->format);
-      backend->priv->tile_size = backend->priv->tile_width *
-                                    backend->priv->tile_height *
-                                    backend->priv->px_size;
-
-      /* insert each of the entries into the hash table */
-      gegl_tile_backend_file_load_index (self, TRUE);
-      self->exist = TRUE;
-      g_assert (self->i != -1);
-      g_assert (self->o != -1);
-
-      /* to autoflush gegl_buffer_set */
-
-      /* XXX: poking at internals, icky */
-      backend->priv->shared = TRUE;
+  if (self->gfile)
+    {
+      g_object_unref (self->gfile);
+      self->gfile = NULL;
     }
-  else
+
+  if (self->monitor)
     {
-      self->exist = FALSE; /* this is also the default, the file will be created on demand */
+      g_object_unref (self->monitor);
+      self->monitor = NULL;
     }
 
-  g_assert (self->file);
+  if (self->path)
+    {
+      g_free (self->path);
+      self->path = NULL;
+    }
 
-  backend->priv->header = &self->header;
+  if (*self->gap_list && *self->gap_list != swap_gap_list)
+    {
+      GList *iter;
 
-  return object;
+      for (iter = *self->gap_list; iter; iter = iter->next)
+        g_slice_free (FileGap, iter->data);
+
+      g_list_free (*self->gap_list);
+      *self->gap_list = NULL;
+    }
+
+  (*G_OBJECT_CLASS (parent_class)->finalize)(object);
 }
 
 static void
-gegl_tile_backend_file_ensure_exist (GeglTileBackendFile *self)
+gegl_tile_backend_file_set_property (GObject      *object,
+                                     guint         property_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
 {
-  if (!self->exist)
-    {
-      GeglTileBackend *backend;
-      self->exist = TRUE;
-
-      backend = GEGL_TILE_BACKEND (self);
+  GeglTileBackendFile *self = GEGL_TILE_BACKEND_FILE (object);
 
-      GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "creating swapfile  %s", self->path);
+  switch (property_id)
+    {
+    case PROP_PATH:
+      self->path = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+    }
+}
 
-      self->o = g_open (self->path, O_RDWR|O_CREAT, 0770);
-      if (self->o == -1)
-        g_warning ("%s: Could not open '%s': %s", G_STRFUNC, self->path, g_strerror (errno));
+static void
+gegl_tile_backend_file_get_property (GObject    *object,
+                                     guint       property_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  GeglTileBackendFile *self = GEGL_TILE_BACKEND_FILE (object);
 
-      self->next_pre_alloc = 256;  /* reserved space for header */
-      self->total          = 256;  /* reserved space for header */
-      self->foffset        = 0;
-      gegl_buffer_header_init (&self->header,
-                               backend->priv->tile_width,
-                               backend->priv->tile_height,
-                               backend->priv->px_size,
-                               backend->priv->format
-                               );
-      gegl_tile_backend_file_write_header (self);
-      self->foffset       = 256;
-      fsync (self->o);
-      self->i = dup (self->o);
-
-      /*self->i = G_INPUT_STREAM (g_file_read (self->file, NULL, NULL));*/
-      self->next_pre_alloc = 256;  /* reserved space for header */
-      self->total          = 256;  /* reserved space for header */
-      g_assert (self->i != -1);
-      g_assert (self->o != -1);
+  switch (property_id)
+    {
+    case PROP_PATH:
+      g_value_set_string (value, self->path);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
     }
 }
 
+
 static void
 gegl_tile_backend_file_class_init (GeglTileBackendFileClass *klass)
 {
-  GObjectClass    *gobject_class     = G_OBJECT_CLASS (klass);
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
   parent_class = g_type_class_peek_parent (klass);
 
-  gobject_class->get_property = gegl_tile_backend_file_get_property;
-  gobject_class->set_property = gegl_tile_backend_file_set_property;
   gobject_class->constructor  = gegl_tile_backend_file_constructor;
   gobject_class->finalize     = gegl_tile_backend_file_finalize;
-
-
-  GEGL_BUFFER_STRUCT_CHECK_PADDING;
+  gobject_class->set_property = gegl_tile_backend_file_set_property;
+  gobject_class->get_property = gegl_tile_backend_file_get_property;
 
   g_object_class_install_property (gobject_class, PROP_PATH,
                                    g_param_spec_string ("path",
                                                         "path",
-                                                        "The base path for this backing file for a buffer",
+                                                        "Path of the backing file",
                                                         NULL,
-                                                        G_PARAM_CONSTRUCT |
+                                                        G_PARAM_CONSTRUCT_ONLY |
                                                         G_PARAM_READWRITE));
 }
 
@@ -989,41 +1025,28 @@ static void
 gegl_tile_backend_file_init (GeglTileBackendFile *self)
 {
   ((GeglTileSource*)self)->command = gegl_tile_backend_file_command;
-  self->path           = NULL;
-  self->file           = NULL;
-  self->i              = -1;
-  self->o              = -1;
-  self->index          = NULL;
-  self->free_list      = NULL;
-  self->next_pre_alloc = 256;  /* reserved space for header */
-  self->total          = 256;  /* reserved space for header */
-}
 
-gboolean
-gegl_tile_backend_file_try_lock (GeglTileBackendFile *self)
-{
-  GeglBufferHeader new_header;
-  new_header = gegl_buffer_read_header (self->i, NULL, NULL)->header;
-  if (new_header.flags & GEGL_FLAG_LOCKED)
-    {
-      return FALSE;
-    }
-  self->header.flags += GEGL_FLAG_LOCKED;
-  gegl_tile_backend_file_write_header (self);
-  fsync (self->o);
-  return TRUE;
+  self->path     = NULL;
+  self->file     = NULL;
+  self->index    = NULL;
+  self->gap_list = NULL;
+  self->gfile    = NULL;
+  self->monitor  = NULL;
 }
 
-gboolean
-gegl_tile_backend_file_unlock (GeglTileBackendFile *self)
+void
+gegl_tile_backend_file_cleanup (void)
 {
-  if (!(self->header.flags & GEGL_FLAG_LOCKED))
+  if (swap_file)
+    g_object_unref (swap_file);
+
+  if (swap_gap_list)
     {
-      g_warning ("tried to unlock unlocked buffer");
-      return FALSE;
+      GList *iter;
+
+      for (iter = swap_gap_list; iter; iter = iter->next)
+        g_slice_free (FileGap, iter->data);
+
+      g_list_free (swap_gap_list);
     }
-  self->header.flags -= GEGL_FLAG_LOCKED;
-  gegl_tile_backend_file_write_header (self);
-  fsync (self->o);
-  return TRUE;
 }
diff --git a/gegl/buffer/gegl-tile-backend-file.h b/gegl/buffer/gegl-tile-backend-file.h
index 00ba5fc..c2426ce 100644
--- a/gegl/buffer/gegl-tile-backend-file.h
+++ b/gegl/buffer/gegl-tile-backend-file.h
@@ -13,18 +13,17 @@
  * You should have received a copy of the GNU Lesser General Public
  * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
  *
- * Copyright 2006 Ãyvind KolÃs <pippin gimp org>
+ * Copyright 2012 Ville Sokk <ville sokk gmail com>
  */
 
 #ifndef __GEGL_TILE_BACKEND_FILE_H__
 #define __GEGL_TILE_BACKEND_FILE_H__
 
+#include <glib.h>
+#include <gio/gio.h>
 #include "gegl-tile-backend.h"
 #include "gegl-buffer-index.h"
-
-/***
- * GeglTileBackendFile is a GeglTileBackend that store tiles in a unique file.
- */
+#include "gegl-aio-file.h"
 
 G_BEGIN_DECLS
 
@@ -39,45 +38,28 @@ G_BEGIN_DECLS
 typedef struct _GeglTileBackendFile      GeglTileBackendFile;
 typedef struct _GeglTileBackendFileClass GeglTileBackendFileClass;
 
-typedef enum
-{
-  OP_WRITE,
-  OP_WRITE_BLOCK,
-  OP_TRUNCATE,
-  OP_SYNC
-} GeglFileBackendThreadOp;
-
-typedef struct
-{
-  GeglBufferTile *tile;
-  /* reference to the writer queue links of this entry when writing
-     tile data or a GeglBufferBlock*/
-  GList          *tile_link;
-  GList          *block_link;
-} GeglFileBackendEntry;
-
-typedef struct
-{
-  gint                     length;    /* length of data if writing tile or
-                                         length of file if truncating */
-  guchar                  *source;
-  goffset                  offset;
-  GeglTileBackendFile     *file;      /* the file we are operating on */
-  GeglFileBackendThreadOp  operation; /* type of file operation, see above */
-  GeglFileBackendEntry    *entry;
-} GeglFileBackendThreadParams;
-
 struct _GeglTileBackendFileClass
 {
   GeglTileBackendClass parent_class;
 };
 
+struct _GeglTileBackendFile
+{
+  GeglTileBackend    parent_instance;
+  gchar             *path;
+  GeglAIOFile       *file;
+  GHashTable        *index;
+  GList            **gap_list;
+  GeglBufferHeader   header;
+  GFile             *gfile;
+  GFileMonitor      *monitor;
+};
+
 GType gegl_tile_backend_file_get_type (void) G_GNUC_CONST;
 
-void  gegl_tile_backend_file_stats    (void);
+gboolean gegl_tile_backend_file_try_lock (GeglTileBackendFile *self);
 
-gboolean gegl_tile_backend_file_try_lock (GeglTileBackendFile *file);
-gboolean gegl_tile_backend_file_unlock   (GeglTileBackendFile *file);
+gboolean gegl_tile_backend_file_unlock (GeglTileBackendFile *self);
 
 G_END_DECLS
 
diff --git a/gegl/gegl-init.c b/gegl/gegl-init.c
index 924ba47..05cb8bb 100644
--- a/gegl/gegl-init.c
+++ b/gegl/gegl-init.c
@@ -349,7 +349,7 @@ static void swap_clean (void)
 
 void gegl_tile_storage_cache_cleanup (void);
 void gegl_aio_file_cleanup (void);
-void gegl_tile_backend_swap_cleanup (void);
+void gegl_tile_backend_file_cleanup (void);
 
 void
 gegl_exit (void)
@@ -364,7 +364,7 @@ gegl_exit (void)
 
   timing = gegl_ticks ();
 
-  gegl_tile_backend_swap_cleanup ();
+  gegl_tile_backend_file_cleanup ();
   gegl_aio_file_cleanup ();
   gegl_tile_storage_cache_cleanup ();
   gegl_tile_cache_destroy ();


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