[gegl/swap-backend: 3/4] buffer: moved IO code from swap backend to new class GeglAIOFile. Changed swap backend's queue limit



commit 39c5c65aa5b2d0f1cc0c0a70705793012789b122
Author: Ville Sokk <ville sokk gmail com>
Date:   Tue Jan 29 20:44:52 2013 +0200

    buffer: moved IO code from swap backend to new class GeglAIOFile. Changed swap backend's queue limit to 
used memory instead of number of entries. Removed unnecessary code from gegl-buffer-load.c

 gegl/buffer/Makefile.am                    |   74 ++-
 gegl/buffer/gegl-aio-file.c                |  618 ++++++++++++++++++
 gegl/buffer/gegl-aio-file.h                |   73 +++
 gegl/buffer/gegl-buffer-index.h            |    6 +-
 gegl/buffer/gegl-buffer-load.c             |  122 ++---
 gegl/buffer/gegl-tile-backend-file-async.c |   14 +-
 gegl/buffer/gegl-tile-backend-swap.c       |  966 ++++++++++++++++------------
 gegl/buffer/gegl-tile-backend-swap.h       |   16 +-
 gegl/gegl-config.c                         |   24 +-
 gegl/gegl-config.h                         |    2 +-
 gegl/gegl-init.c                           |   18 +-
 11 files changed, 1351 insertions(+), 582 deletions(-)
---
diff --git a/gegl/buffer/Makefile.am b/gegl/buffer/Makefile.am
index 22a5d8d..43d9a5d 100644
--- a/gegl/buffer/Makefile.am
+++ b/gegl/buffer/Makefile.am
@@ -14,72 +14,74 @@ AM_CFLAGS = $(DEP_CFLAGS) $(BABL_CFLAGS)
 noinst_LTLIBRARIES = libbuffer.la
 
 libbuffer_la_SOURCES = \
-    gegl-tile-backend-tiledir.c        \
-    gegl-buffer.c              \
+       gegl-aio-file.c \
+       gegl-buffer-load.c      \
+       gegl-tile-backend-swap.c \
     gegl-buffer-access.c       \
-    gegl-buffer-share.c                \
+    gegl-buffer-cl-cache.c     \
+    gegl-buffer-cl-iterator.c  \
     gegl-buffer-index.h                \
     gegl-buffer-iterator.c     \
-    gegl-buffer-cl-iterator.c  \
-    gegl-buffer-cl-cache.c     \
     gegl-buffer-linear.c       \
-       gegl-buffer-load.c      \
     gegl-buffer-save.c         \
+    gegl-buffer-share.c                \
+    gegl-buffer.c              \
     gegl-cache.c               \
-    gegl-sampler.c             \
+    gegl-id-pool.c             \
+    gegl-region-generic.c      \
     gegl-sampler-cubic.c       \
     gegl-sampler-linear.c      \
-    gegl-sampler-nearest.c     \
-    gegl-sampler-nohalo.c       \
     gegl-sampler-lohalo.c       \
-    gegl-region-generic.c      \
-    gegl-tile.c                        \
-    gegl-tile-source.c         \
-    gegl-tile-storage.c                \
-    gegl-tile-backend.c                \
+    gegl-sampler-nearest.c     \
+       gegl-sampler-nohalo.c \
+    gegl-sampler.c             \
     gegl-tile-backend-ram.c    \
-       gegl-tile-backend-swap.c \
-    gegl-tile-handler.c                \
-    gegl-tile-handler-private.h        \
+    gegl-tile-backend-tiledir.c        \
+    gegl-tile-backend.c                \
     gegl-tile-handler-cache.c  \
     gegl-tile-handler-chain.c  \
     gegl-tile-handler-empty.c  \
     gegl-tile-handler-log.c    \
+    gegl-tile-handler-private.h        \
     gegl-tile-handler-zoom.c   \
-    gegl-id-pool.c             \
+    gegl-tile-handler.c                \
+    gegl-tile-source.c         \
+    gegl-tile-storage.c                \
+    gegl-tile.c                        \
     \
-    gegl-buffer.h              \
-    gegl-buffer-private.h      \
-    gegl-buffer-iterator.h     \
-    gegl-buffer-cl-iterator.h  \
+       gegl-aio-file.h \
+       gegl-tile-backend-swap.h \
     gegl-buffer-cl-cache.h     \
+    gegl-buffer-cl-iterator.h  \
+    gegl-buffer-iterator.h     \
     gegl-buffer-load.h         \
+    gegl-buffer-private.h      \
     gegl-buffer-save.h         \
     gegl-buffer-types.h                \
+    gegl-buffer.h              \
     gegl-cache.h               \
-    gegl-sampler.h             \
+    gegl-id-pool.h \
+    gegl-region-generic.h      \
+    gegl-region.h              \
     gegl-sampler-cubic.h       \
     gegl-sampler-linear.h      \
-    gegl-sampler-nearest.h     \
-    gegl-sampler-nohalo.h       \
     gegl-sampler-lohalo.h       \
-    gegl-region.h              \
-    gegl-region-generic.h      \
-    gegl-tile.h                        \
-    gegl-tile-source.h         \
-    gegl-tile-storage.h                \
-    gegl-tile-backend.h                \
+    gegl-sampler-nearest.h     \
+       gegl-sampler-nohalo.h \
+    gegl-sampler.h             \
     gegl-tile-backend-file.h   \
-       gegl-tile-backend-swap.h \
-    gegl-tile-backend-tiledir.h        \
     gegl-tile-backend-ram.h    \
-    gegl-tile-handler.h                \
-    gegl-tile-handler-chain.h  \
+    gegl-tile-backend-tiledir.h        \
+    gegl-tile-backend.h                \
     gegl-tile-handler-cache.h  \
+    gegl-tile-handler-chain.h  \
     gegl-tile-handler-empty.h  \
     gegl-tile-handler-log.h    \
     gegl-tile-handler-zoom.h   \
-    gegl-id-pool.h
+    gegl-tile-handler.h                \
+    gegl-tile-source.h         \
+    gegl-tile-storage.h                \
+    gegl-tile.h
 
 libbuffer_la_SOURCES += gegl-tile-backend-file-async.c
 #if HAVE_64_BIT 
diff --git a/gegl/buffer/gegl-aio-file.c b/gegl/buffer/gegl-aio-file.c
new file mode 100644
index 0000000..9a05c53
--- /dev/null
+++ b/gegl/buffer/gegl-aio-file.c
@@ -0,0 +1,618 @@
+/* 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
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * 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, 2007, 2008 Ãyvind KolÃs <pippin gimp org>
+ *           2012 Ville Sokk <ville sokk gmail com>
+ */
+
+
+#include "config.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 "gegl.h"
+#include "gegl-buffer-backend.h"
+#include "gegl-aio-file.h"
+#include "gegl-debug.h"
+#include "gegl-config.h"
+
+#ifndef HAVE_FSYNC
+
+#ifdef G_OS_WIN32
+#define fsync _commit
+#endif
+
+#endif
+
+
+G_DEFINE_TYPE (GeglAIOFile, gegl_aio_file, G_TYPE_OBJECT);
+
+static GObjectClass * parent_class = NULL;
+
+
+typedef enum
+{
+  OP_WRITE,
+  OP_TRUNCATE,
+  OP_SYNC
+} ThreadOp;
+
+typedef struct
+{
+  guint64  offset;
+  guint    length;
+  GList   *link;
+} FileEntry;
+
+typedef struct
+{
+  GeglAIOFile *file;
+  FileEntry   *entry;
+  guchar      *source;
+  ThreadOp     operation;
+} ThreadParams;
+
+enum
+{
+  PROP_0,
+  PROP_PATH
+};
+
+
+static void       gegl_aio_file_push_queue   (ThreadParams          *params);
+static void       gegl_aio_file_thread_write (ThreadParams          *params);
+static gpointer   gegl_aio_file_thread       (gpointer               ignored);
+static FileEntry *gegl_aio_file_entry_create (guint64                offset,
+                                              guint                  length);
+static FileEntry *gegl_aio_file_lookup_entry (GeglAIOFile           *self,
+                                              guint64                offset);
+static guint      gegl_aio_file_hashfunc     (gconstpointer          key);
+static gboolean   gegl_aio_file_equalfunc    (gconstpointer          a,
+                                              gconstpointer          b);
+static GObject   *gegl_aio_file_constructor  (GType                  type,
+                                              guint                  n_params,
+                                              GObjectConstructParam *params);
+static void       gegl_aio_file_init         (GeglAIOFile           *self);
+static void       gegl_aio_file_ensure_exist (GeglAIOFile           *self);
+static void       gegl_aio_file_class_init   (GeglAIOFileClass      *klass);
+static void       gegl_aio_file_finalize     (GObject               *object);
+static void       gegl_aio_file_get_property (GObject               *gobject,
+                                              guint                  property_id,
+                                              GValue                *value,
+                                              GParamSpec            *pspec);
+static void       gegl_aio_file_set_property (GObject               *object,
+                                              guint                  property_id,
+                                              const GValue          *value,
+                                              GParamSpec            *pspec);
+void              gegl_aio_file_cleanup      (void);
+
+
+static GThread      *writer_thread = NULL;
+static GQueue       *queue         = NULL;
+static ThreadParams *in_progress   = NULL;
+static gboolean      exit_thread   = FALSE;
+static guint64       queue_size    = 0;
+static GCond         empty_cond;
+static GCond         max_cond;
+static GMutex        mutex;
+
+
+static gpointer
+gegl_aio_file_thread (gpointer ignored)
+{
+  while (TRUE)
+    {
+      ThreadParams *params;
+
+      g_mutex_lock (&mutex);
+
+      while (g_queue_is_empty (queue) && !exit_thread)
+        g_cond_wait (&empty_cond, &mutex);
+
+      if (exit_thread)
+        {
+          g_mutex_unlock (&mutex);
+          /*GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "exiting writer thread");*/
+          return NULL;
+        }
+
+      params = (ThreadParams *)g_queue_pop_head (queue);
+      if (params->operation == OP_WRITE)
+        {
+          g_hash_table_remove (params->file->index, params->entry);
+          in_progress = params;
+          params->entry->link = NULL;
+        }
+
+      g_mutex_unlock (&mutex);
+
+      switch (params->operation)
+        {
+        case OP_WRITE:
+          gegl_aio_file_thread_write (params);
+          break;
+        case OP_TRUNCATE:
+          ftruncate (params->file->out_fd, params->file->total);
+          break;
+        case OP_SYNC:
+          fsync (params->file->out_fd);
+          break;
+        }
+
+      g_mutex_lock (&mutex);
+
+      in_progress = NULL;
+
+      if (params->operation == OP_WRITE)
+        {
+          queue_size -= params->entry->length + sizeof (GList) +
+            sizeof (ThreadParams);
+          g_free (params->source);
+          g_slice_free (FileEntry, params->entry);
+
+          /* unblock the main thread if the queue had gotten too big */
+          if (queue_size <= gegl_config ()->queue_size)
+            g_cond_signal (&max_cond);
+        }
+
+      g_slice_free (ThreadParams, params);
+
+      g_mutex_unlock (&mutex);
+    }
+
+  return NULL;
+}
+
+static void
+gegl_aio_file_push_queue (ThreadParams *params)
+{
+  g_mutex_lock (&mutex);
+
+  /* block if the queue has gotten too big */
+  while (queue_size > gegl_config ()->queue_size)
+    g_cond_wait (&max_cond, &mutex);
+
+  g_queue_push_tail (queue, params);
+
+  if (params->operation == OP_WRITE)
+    {
+      params->entry->link = g_queue_peek_tail_link (queue);
+      queue_size += params->entry->length + sizeof (GList) + sizeof (ThreadParams);
+    }
+
+  /* wake up the writer thread */
+  g_cond_signal (&empty_cond);
+
+  g_mutex_unlock (&mutex);
+}
+
+static void
+gegl_aio_file_thread_write (ThreadParams *params)
+{
+  GeglAIOFile *file          = params->file;
+  guint        to_be_written = params->entry->length;
+  guint64      offset        = params->entry->offset;
+
+  if (file->out_offset != offset)
+    {
+      if (lseek (file->out_fd, offset, SEEK_SET) < 0)
+        {
+          g_warning ("unable to seek to tile in buffer: %s", g_strerror (errno));
+          return;
+        }
+      file->out_offset = offset;
+    }
+
+  while (to_be_written > 0)
+    {
+      gint wrote;
+      wrote = write (file->out_fd,
+                     params->source + params->entry->length - 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);
+          break;
+        }
+
+      to_be_written    -= wrote;
+      file->out_offset += wrote;
+    }
+
+  /*GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "writer thread wrote at %i", (gint)offset);*/
+}
+
+static FileEntry *
+gegl_aio_file_entry_create (guint64 offset,
+                            guint   length)
+{
+  FileEntry *entry = g_slice_new (FileEntry);
+
+  entry->offset = offset;
+  entry->length = length;
+  entry->link   = NULL;
+
+  return entry;
+}
+
+static FileEntry *
+gegl_aio_file_lookup_entry (GeglAIOFile *self,
+                            guint64      offset)
+{
+  FileEntry *ret = NULL;
+  FileEntry *key = gegl_aio_file_entry_create (offset, 0);
+
+  ret = g_hash_table_lookup (self->index, key);
+  g_slice_free (FileEntry, key);
+
+  return ret;
+}
+
+void
+gegl_aio_file_resize (GeglAIOFile *self,
+                      guint64      size)
+{
+  ThreadParams *params;
+
+  self->total       = size;
+  params            = g_slice_new0 (ThreadParams);
+  params->file      = self;
+  params->operation = OP_TRUNCATE;
+
+  gegl_aio_file_push_queue (params);
+}
+
+void
+gegl_aio_file_read (GeglAIOFile *self,
+                    guint64      offset,
+                    guint        length,
+                    guchar      *dest)
+{
+  FileEntry    *entry;
+  guint         to_be_read = length;
+  ThreadParams *params     = NULL;
+
+  gegl_aio_file_ensure_exist (self);
+
+  g_mutex_lock (&mutex);
+
+  entry = gegl_aio_file_lookup_entry (self, offset);
+
+  if (entry && entry->link)
+    params = entry->link->data;
+  else if (in_progress && in_progress->entry->offset == offset &&
+           in_progress->operation == OP_WRITE)
+    params = in_progress;
+
+  if (params)
+    {
+      if (params->entry->length >= length)
+        {
+          memcpy (dest, params->source, length);
+          g_mutex_unlock (&mutex);
+          return;
+        }
+      else
+        {
+          guint len = params->entry->length;
+
+          memcpy (dest, params->source, len);
+          offset += len;
+          to_be_read -= len;
+        }
+    }
+
+  g_mutex_unlock (&mutex);
+
+  if (self->in_offset != offset)
+    {
+      if (lseek (self->in_fd, offset, SEEK_SET) < 0)
+        {
+          g_warning ("unable to seek to tile in buffer: %s", g_strerror (errno));
+          return;
+        }
+      self->in_offset = offset;
+    }
+
+  while (to_be_read > 0)
+    {
+      GError *error = NULL;
+      gint    bytes_read;
+
+      bytes_read = read (self->in_fd, dest + length - to_be_read, to_be_read);
+      if (bytes_read <= 0)
+        {
+          g_message ("unable to read tile data from file %s: "
+                     "%s (%d/%d bytes read) %s",
+                     self->path, g_strerror (errno), bytes_read, to_be_read,
+                     error?error->message:"--");
+          return;
+        }
+      to_be_read      -= bytes_read;
+      self->in_offset += bytes_read;
+    }
+}
+
+void
+gegl_aio_file_write (GeglAIOFile *self,
+                     guint64      offset,
+                     guint        length,
+                     guchar      *source)
+{
+  FileEntry    *entry = NULL;
+  ThreadParams *params;
+  guchar       *new_source;
+
+  gegl_aio_file_ensure_exist (self);
+
+  g_mutex_lock (&mutex);
+
+  entry = gegl_aio_file_lookup_entry (self, offset);
+
+  if (entry && entry->link)
+    {
+      params = entry->link->data;
+
+      if (entry->length != length)
+        {
+          if (entry->length < length)
+            params->source = g_realloc (params->source, length);
+
+          entry->length = length;
+        }
+
+      memcpy (params->source, source, length);
+      g_mutex_unlock (&mutex);
+      return;
+    }
+
+  entry = gegl_aio_file_entry_create (offset, length);
+  g_hash_table_insert (self->index, entry, entry);
+
+  g_mutex_unlock (&mutex);
+
+  new_source = g_malloc (length);
+  memcpy (new_source, source, length);
+
+  params            = g_slice_new0 (ThreadParams);
+  params->operation = OP_WRITE;
+  params->source    = new_source;
+  params->entry     = entry;
+  params->file      = self;
+
+  gegl_aio_file_push_queue (params);
+}
+
+void
+gegl_aio_file_sync (GeglAIOFile *self)
+{
+  ThreadParams *params = g_slice_new0 (ThreadParams);
+  params->operation = OP_SYNC;
+  params->file = self;
+
+  gegl_aio_file_push_queue (params);
+}
+
+static guint
+gegl_aio_file_hashfunc (gconstpointer key)
+{
+  return ((const FileEntry *) key)->offset;
+}
+
+static gboolean
+gegl_aio_file_equalfunc (gconstpointer a,
+                         gconstpointer b)
+{
+  const FileEntry *ea = a;
+  const FileEntry *eb = b;
+
+  if (ea->offset == eb->offset)
+    return TRUE;
+
+  return FALSE;
+}
+
+static void
+gegl_aio_file_init (GeglAIOFile *self)
+{
+  self->index     = NULL;
+  self->path      = NULL;
+  self->in_fd     = self->out_fd = -1;
+  self->in_offset = self->out_offset = 0;
+  self->total     = 0;
+}
+
+static GObject *
+gegl_aio_file_constructor (GType                  type,
+                           guint                  n_params,
+                           GObjectConstructParam *params)
+{
+  GObject     *object;
+  GeglAIOFile *self;
+
+  object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);
+  self   = GEGL_AIO_FILE (object);
+
+  self->index = g_hash_table_new (gegl_aio_file_hashfunc,
+                                  gegl_aio_file_equalfunc);
+
+  /*GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "constructing swap backend");*/
+
+  return object;
+}
+
+static void
+gegl_aio_file_ensure_exist (GeglAIOFile *self)
+{
+  if (self->in_fd == -1 || self->out_fd == -1)
+    {
+      /*GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "creating swapfile %s", self->path);*/
+
+      self->out_fd = g_open (self->path, O_RDWR|O_CREAT, 0770);
+      self->in_fd = g_open (self->path, O_RDONLY);
+
+      if (self->out_fd == -1 || self->in_fd == -1)
+        g_warning ("Could not open file '%s': %s", self->path, g_strerror (errno));
+    }
+}
+
+static void
+gegl_aio_file_class_init (GeglAIOFileClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->constructor  = gegl_aio_file_constructor;
+  gobject_class->finalize     = gegl_aio_file_finalize;
+  gobject_class->get_property = gegl_aio_file_get_property;
+  gobject_class->set_property = gegl_aio_file_set_property;
+
+  queue = g_queue_new ();
+
+  writer_thread = g_thread_create_full (gegl_aio_file_thread,
+                                        NULL, 0, TRUE, TRUE,
+                                        G_THREAD_PRIORITY_NORMAL, NULL);
+
+  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",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_READWRITE));
+}
+
+static void
+gegl_aio_file_finalize (GObject *object)
+{
+  GeglAIOFile *self = GEGL_AIO_FILE (object);
+
+  if (self->index)
+    {
+      if (g_hash_table_size (self->index) != 0)
+        {
+          GList *entries, *iter;
+
+          g_mutex_lock (&mutex);
+
+          entries = g_hash_table_get_keys (self->index);
+
+          for (iter = entries; iter; iter = iter->next)
+            {
+              FileEntry *entry = iter->data;
+
+              if (entry->link)
+                {
+                  g_slice_free (ThreadParams, entry->link->data);
+                  g_queue_delete_link (queue, entry->link);
+                }
+
+              g_slice_free (FileEntry, entry);
+            }
+
+          g_list_free (entries);
+
+          g_mutex_unlock (&mutex);
+        }
+
+      g_hash_table_unref (self->index);
+      self->index = NULL;
+
+      g_free (self->path);
+      self->path = NULL;
+
+      close (self->in_fd);
+      self->in_fd = -1;
+
+      close (self->out_fd);
+      self->out_fd = -1;
+    }
+
+  (*G_OBJECT_CLASS (parent_class)->finalize)(object);
+}
+
+static void
+gegl_aio_file_get_property (GObject    *gobject,
+                            guint       property_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+  GeglAIOFile *self = GEGL_AIO_FILE (gobject);
+
+  switch (property_id)
+    {
+    case PROP_PATH:
+      g_value_set_string (value, self->path);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
+      break;
+    }
+}
+
+static void
+gegl_aio_file_set_property (GObject      *object,
+                            guint         property_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+  GeglAIOFile *self = GEGL_AIO_FILE (object);
+
+  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;
+    }
+}
+
+void
+gegl_aio_file_cleanup (void)
+{
+  /* check if this class has been used at all before cleaning */
+  if (queue)
+    {
+      exit_thread = TRUE;
+      g_cond_signal (&empty_cond);
+      g_thread_join (writer_thread);
+
+      if (g_queue_get_length (queue) != 0)
+        g_warning ("writer thread queue wasn't empty before freeing\n");
+
+      g_queue_free (queue);
+    }
+}
diff --git a/gegl/buffer/gegl-aio-file.h b/gegl/buffer/gegl-aio-file.h
new file mode 100644
index 0000000..1193d73
--- /dev/null
+++ b/gegl/buffer/gegl-aio-file.h
@@ -0,0 +1,73 @@
+/* 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
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * 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 2012 Ville Sokk <ville sokk gmail com>
+ */
+
+#ifndef __GEGL_AIO_FILE_H__
+#define __GEGL_AIO_FILE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define GEGL_TYPE_AIO_FILE            (gegl_aio_file_get_type ())
+#define GEGL_AIO_FILE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GEGL_TYPE_AIO_FILE, GeglAIOFile))
+#define GEGL_AIO_FILE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GEGL_TYPE_AIO_FILE, 
GeglAIOFileClass))
+#define GEGL_IS_AIO_FILE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GEGL_TYPE_AIO_FILE))
+#define GEGL_IS_AIO_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GEGL_TYPE_AIO_FILE))
+#define GEGL_AIO_FILE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GEGL_TYPE_AIO_FILE, 
GeglAIOFileClass))
+
+
+typedef struct _GeglAIOFile      GeglAIOFile;
+typedef struct _GeglAIOFileClass GeglAIOFileClass;
+
+struct _GeglAIOFileClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GeglAIOFile
+{
+  GObject     parent_instance;
+  GHashTable *index;
+  gchar      *path;
+  gint        in_fd;
+  gint        out_fd;
+  guint64     in_offset;
+  guint64     out_offset;
+  guint64     total;
+};
+
+GType gegl_aio_file_get_type (void) G_GNUC_CONST;
+
+void gegl_aio_file_resize (GeglAIOFile *self,
+                           guint64      size);
+
+void gegl_aio_file_read (GeglAIOFile *self,
+                         guint64      offset,
+                         guint        length,
+                         guchar      *dest);
+
+void gegl_aio_file_write (GeglAIOFile *self,
+                          guint64      offset,
+                          guint        length,
+                          guchar      *source);
+
+void gegl_aio_file_sync (GeglAIOFile *self);
+
+G_END_DECLS
+
+#endif
diff --git a/gegl/buffer/gegl-buffer-index.h b/gegl/buffer/gegl-buffer-index.h
index ff0bda0..10116c4 100644
--- a/gegl/buffer/gegl-buffer-index.h
+++ b/gegl/buffer/gegl-buffer-index.h
@@ -125,11 +125,9 @@ void gegl_buffer_header_init (GeglBufferHeader *header,
 void gegl_tile_entry_destroy (GeglBufferTile *entry);
 
 GeglBufferItem *gegl_buffer_read_header(int      i,
-                                        goffset *offset,
-                                        gchar   *map);
+                                        goffset *offset);
 GList          *gegl_buffer_read_index (int      i,
-                                        goffset *offset,
-                                        gchar   *map);
+                                        goffset *offset);
 
 #define struct_check_padding(type, size) \
   if (sizeof (type) != size) \
diff --git a/gegl/buffer/gegl-buffer-load.c b/gegl/buffer/gegl-buffer-load.c
index 8857318..2613453 100644
--- a/gegl/buffer/gegl-buffer-load.c
+++ b/gegl/buffer/gegl-buffer-load.c
@@ -94,37 +94,25 @@ load_info_destroy (LoadInfo *info)
   g_slice_free (LoadInfo, info);
 }
 
-/* Reads buffer header from the file descriptor (first argument) or
- * memory mapped file if map argument is not NULL
- */
 GeglBufferItem *
 gegl_buffer_read_header (int      i,
-                         goffset *offset,
-                         gchar   *map)
+                         goffset *offset)
 {
   goffset         placeholder;
   GeglBufferItem *ret;
-
   if (offset==0)
     offset = &placeholder;
 
-  if (map == NULL)
-    if(lseek(i, 0, SEEK_SET) == -1)
-      g_warning ("failed seeking to %i", 0);
+  if(lseek(i, 0, SEEK_SET) == -1)
+    g_warning ("failed seeking to %i", 0);
   *offset = 0;
 
   ret = g_malloc (sizeof (GeglBufferHeader));
-  if (map)
-    {
-      memcpy (ret, map, sizeof (GeglBufferHeader));
-      *offset += sizeof (GeglBufferHeader);
-    }
-  else
-    {
-      ssize_t sz_read = read(i, ret, sizeof(GeglBufferHeader));
-      if (sz_read != -1)
-        *offset += sz_read;
-    }
+  {
+    ssize_t sz_read = read(i, ret, sizeof(GeglBufferHeader));
+    if (sz_read != -1)
+      *offset += sz_read;
+  }
 
   GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD, "read header: tile-width: %i tile-height: %i next:%i  %ix%i\n",
                    ret->header.tile_width,
@@ -145,41 +133,30 @@ gegl_buffer_read_header (int      i,
 }
 
 /* reads a block of information from a geglbuffer that resides in an GInputStream,
- * if offset is NULL it is read from the current offset of the stream. If offset
+ * if offset is NULL it is read from the current offsetition of the stream. If offset
  * is passed in the offset stored at the location is used as the initial seeking
  * point and will be updated with the offset after the read is completed.
- *
- * If the map argument is not NULL then the block is memcpyd from the passed in
- * memory mapped file.
  */
 static GeglBufferItem *read_block (int      i,
-                                   goffset *offset,
-                                   gchar   *map)
+                                   goffset *offset)
 {
-  GeglBufferBlock  block;
-  GeglBufferItem  *ret;
-  gsize            byte_read  = 0;
-  gint             own_size   = 0;
-  gsize            block_size = sizeof (GeglBufferBlock);
+  GeglBufferBlock block;
+  GeglBufferItem *ret;
+  gsize           byte_read = 0;
+  gint            own_size=0;
 
   if (*offset==0)
     return NULL;
 
-  if (offset && map == NULL)
+  if (offset)
     if(lseek(i, *offset, SEEK_SET) == -1)
       g_warning ("failed seeking to %i", (gint)*offset);
 
-  if (map)
-    {
-      memcpy (&block, map + *offset, block_size);
-      byte_read += block_size;
-    }
-  else
-    {
-      ssize_t sz_read = read (i, &block,  block_size);
-      if(sz_read != -1)
-        byte_read += sz_read;
-    }
+  {
+       ssize_t sz_read = read (i, &block,  sizeof (GeglBufferBlock));
+    if(sz_read != -1)
+      byte_read += sz_read;
+  }
   GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD, "read block: length:%i next:%i",
                           block.length, (guint)block.next);
 
@@ -207,41 +184,25 @@ static GeglBufferItem *read_block (int      i,
        * versions
        */
       ret = g_malloc (own_size);
-      memcpy (ret, &block, block_size);
-
-      if (map)
-        {
-          memcpy (((gchar*)ret) + block_size, map + *offset + byte_read,
-                  own_size - block_size);
-          byte_read += own_size - block_size;
-        }
-      else
-        {
-          ssize_t sz_read = read (i, ((gchar*)ret) + block_size,
-                                  own_size - block_size);
-          if(sz_read != -1)
-            byte_read += sz_read;
-        }
+      memcpy (ret, &block, sizeof (GeglBufferBlock));
+      {
+        ssize_t sz_read = read (i, ((gchar*)ret) + sizeof(GeglBufferBlock),
+                                own_size - sizeof(GeglBufferBlock));
+        if(sz_read != -1)
+          byte_read += sz_read;
+      }
       ret->block.length = own_size;
     }
   else if (block.length < own_size)
     {
       ret = g_malloc (own_size);
-      memcpy (ret, &block, block_size);
-
-      if (map)
-        {
-          memcpy (((gchar*)ret) + block_size, map + *offset + byte_read,
-                  block.length - block_size);
-          byte_read += block.length - block_size;
-        }
-      else
-        {
-          ssize_t sz_read = read (i, ((gchar*)ret) + block_size,
-                                  block.length - block_size);
-          if(sz_read != -1)
-            byte_read += sz_read;
-        }
+      memcpy (ret, &block, sizeof (GeglBufferBlock));
+      {
+        ssize_t sz_read = read (i, ret + sizeof(GeglBufferBlock),
+                                                               block.length - sizeof (GeglBufferBlock));
+               if(sz_read != -1)
+                 byte_read += sz_read;
+      }
       ret->block.length = own_size;
     }
   else
@@ -254,21 +215,15 @@ static GeglBufferItem *read_block (int      i,
   return ret;
 }
 
-/* Reads buffer index from the file descriptor (first argument) or
- * memory mapped file if map argument is not NULL
- */
 GList *
 gegl_buffer_read_index (int      i,
-                        goffset *offset,
-                        gchar   *map)
+                        goffset *offset)
 /* load the index */
 {
   GList          *ret = NULL;
   GeglBufferItem *item;
 
-  for (item = read_block (i, offset, map);
-       item;
-       item = read_block (i, offset, map))
+  for (item = read_block (i, offset); item; item = read_block (i, offset))
     {
       g_assert (item);
       GEGL_NOTE (GEGL_DEBUG_BUFFER_LOAD,"loaded item: %i, %i, %i offset:%i next:%i", item->tile.x,
@@ -283,7 +238,6 @@ gegl_buffer_read_index (int      i,
   return ret;
 }
 
-
 static void sanity(void) { GEGL_BUFFER_SANITY; }
 
 
@@ -312,7 +266,7 @@ gegl_buffer_load (const gchar *path)
     }
 
   {
-    GeglBufferItem *header = gegl_buffer_read_header (info->i, &info->offset, NULL);
+    GeglBufferItem *header = gegl_buffer_read_header (info->i, &info->offset);
     g_assert (header);
     /*memcpy (&(info->header), header, sizeof (GeglBufferHeader));*/
     info->header = *(&header->header);
@@ -340,7 +294,7 @@ gegl_buffer_load (const gchar *path)
   */
   g_assert (babl_format_get_bytes_per_pixel (info->format) == info->header.bytes_per_pixel);
 
-  info->tiles = gegl_buffer_read_index (info->i, &info->offset, NULL);
+  info->tiles = gegl_buffer_read_index (info->i, &info->offset);
 
   /* load each tile */
   {
diff --git a/gegl/buffer/gegl-tile-backend-file-async.c b/gegl/buffer/gegl-tile-backend-file-async.c
index 9686f9a..246c4b2 100644
--- a/gegl/buffer/gegl-tile-backend-file-async.c
+++ b/gegl/buffer/gegl-tile-backend-file-async.c
@@ -180,7 +180,7 @@ gegl_tile_backend_file_push_queue (GeglFileBackendThreadParams *params)
   length = g_queue_get_length (&queue);
 
   /* block if the queue has gotten too big */
-  if (length > gegl_config ()->queue_limit)
+  if (length > gegl_config ()->queue_size)
     g_cond_wait (max_cond, mutex);
 
   params->file->pending_ops += 1;
@@ -286,7 +286,7 @@ gegl_tile_backend_file_writer_thread (gpointer ignored)
         g_cond_signal (params->file->cond);
 
       /* unblock the main thread if the queue had gotten too big */
-      if (g_queue_get_length (&queue) < gegl_config ()->queue_limit)
+      if (g_queue_get_length (&queue) < gegl_config ()->queue_size)
         g_cond_signal (max_cond);
 
       if (params->source)
@@ -1028,12 +1028,12 @@ gegl_tile_backend_file_load_index (GeglTileBackendFile *self,
    * are added here
    */
   /* reload header */
-  new_header = gegl_buffer_read_header (self->i, &offset, NULL)->header;
+  new_header = gegl_buffer_read_header (self->i, &offset)->header;
 
   while (new_header.flags & GEGL_FLAG_LOCKED)
     {
       g_usleep (50000);
-      new_header = gegl_buffer_read_header (self->i, &offset, NULL)->header;
+      new_header = gegl_buffer_read_header (self->i, &offset)->header;
     }
 
   if (new_header.rev == self->header.rev)
@@ -1049,7 +1049,7 @@ gegl_tile_backend_file_load_index (GeglTileBackendFile *self,
 
   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->tiles     = gegl_buffer_read_index (self->i, &offset);
   self->in_offset = self->out_offset = -1;
   backend         = GEGL_TILE_BACKEND (self);
 
@@ -1173,7 +1173,7 @@ gegl_tile_backend_file_constructor (GType                  type,
         }
       self->i = g_open (self->path, O_RDONLY, 0);
 
-      self->header     = gegl_buffer_read_header (self->i, &offset, NULL)->header;
+      self->header     = gegl_buffer_read_header (self->i, &offset)->header;
       self->header.rev = self->header.rev -1;
 
       /* we are overriding all of the work of the actual constructor here,
@@ -1292,7 +1292,7 @@ gegl_tile_backend_file_try_lock (GeglTileBackendFile *self)
 {
   GeglBufferHeader new_header;
 
-  new_header = gegl_buffer_read_header (self->i, NULL, NULL)->header;
+  new_header = gegl_buffer_read_header (self->i, NULL)->header;
   if (new_header.flags & GEGL_FLAG_LOCKED)
     {
       return FALSE;
diff --git a/gegl/buffer/gegl-tile-backend-swap.c b/gegl/buffer/gegl-tile-backend-swap.c
index e1fbef1..ad91d0d 100644
--- a/gegl/buffer/gegl-tile-backend-swap.c
+++ b/gegl/buffer/gegl-tile-backend-swap.c
@@ -34,11 +34,14 @@
 #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-swap.h"
+#include "gegl-aio-file.h"
 #include "gegl-debug.h"
 #include "gegl-config.h"
 
@@ -57,376 +60,140 @@ G_DEFINE_TYPE (GeglTileBackendSwap, gegl_tile_backend_swap, GEGL_TYPE_TILE_BACKE
 static GObjectClass * parent_class = NULL;
 
 
-typedef enum
-{
-  OP_WRITE,
-  OP_TRUNCATE,
-} ThreadOp;
-
 typedef struct
 {
-  guint64  offset;
-  GList   *link;
-  gint     x;
-  gint     y;
-  gint     z;
+  guint64 offset;
+  gint    rev;
+  gint    x;
+  gint    y;
+  gint    z;
 } SwapEntry;
 
 typedef struct
 {
-  SwapEntry *entry;
-  gint       length;
-  guchar    *source;
-  ThreadOp   operation;
-} ThreadParams;
-
-typedef struct
-{
   guint64 start;
   guint64 end;
 } SwapGap;
 
-
-static void        gegl_tile_backend_swap_push_queue    (ThreadParams *params);
-static void        gegl_tile_backend_swap_write         (ThreadParams *params);
-static gpointer    gegl_tile_backend_swap_writer_thread (gpointer ignored);
-static void        gegl_tile_backend_swap_entry_read    (GeglTileBackendSwap   *self,
-                                                         SwapEntry             *entry,
-                                                         guchar                *dest);
-static void        gegl_tile_backend_swap_entry_write   (GeglTileBackendSwap   *self,
-                                                         SwapEntry             *entry,
-                                                         guchar                *source);
-static SwapEntry * gegl_tile_backend_swap_entry_create  (gint                   x,
-                                                         gint                   y,
-                                                         gint                   z);
-static guint64     gegl_tile_backend_swap_find_offset   (gint                   tile_size);
-static SwapGap *   gegl_tile_backend_swap_gap_new       (guint64                start,
-                                                         guint64                end);
-static void        gegl_tile_backend_swap_entry_destroy (GeglTileBackendSwap   *self,
-                                                         SwapEntry             *entry);
-static void        gegl_tile_backend_swap_resize        (guint64 size);
-static SwapEntry * gegl_tile_backend_swap_lookup_entry  (GeglTileBackendSwap   *self,
-                                                         gint                   x,
-                                                         gint                   y,
-                                                         gint                   z);
-static GeglTile *  gegl_tile_backend_swap_get_tile      (GeglTileSource        *self,
-                                                         gint                   x,
-                                                         gint                   y,
-                                                         gint                   z);
-static gpointer    gegl_tile_backend_swap_set_tile      (GeglTileSource        *self,
-                                                         GeglTile              *tile,
-                                                         gint                   x,
-                                                         gint                   y,
-                                                         gint                   z);
-static gpointer    gegl_tile_backend_swap_void_tile     (GeglTileSource        *self,
-                                                         GeglTile              *tile,
-                                                         gint                   x,
-                                                         gint                   y,
-                                                         gint                   z);
-static gpointer    gegl_tile_backend_swap_exist_tile    (GeglTileSource        *self,
-                                                         GeglTile              *tile,
-                                                         gint                   x,
-                                                         gint                   y,
-                                                         gint                   z);
-static gpointer    gegl_tile_backend_swap_command       (GeglTileSource        *self,
-                                                         GeglTileCommand        command,
-                                                         gint                   x,
-                                                         gint                   y,
-                                                         gint                   z,
-                                                         gpointer               data);
-static guint       gegl_tile_backend_swap_hashfunc      (gconstpointer key);
-static gboolean    gegl_tile_backend_swap_equalfunc     (gconstpointer          a,
-                                                         gconstpointer          b);
-static GObject *   gegl_tile_backend_swap_constructor   (GType                  type,
-                                                         guint                  n_params,
-                                                         GObjectConstructParam *params);
-static void        gegl_tile_backend_swap_finalize      (GObject *object);
-static void        gegl_tile_backend_swap_ensure_exist  (void);
-static void        gegl_tile_backend_swap_class_init    (GeglTileBackendSwapClass *klass);
-static void        gegl_tile_backend_swap_init          (GeglTileBackendSwap *self);
-void               gegl_tile_backend_swap_cleanup       (void);
-
-
-static gchar   *path       = NULL;
-static gint     in_fd      = -1;
-static gint     out_fd     = -1;
-static guint64  in_offset  = 0;
-static guint64  out_offset = 0;
-static GList   *gap_list   = NULL;
-static guint64  total      = 0;
-
-static GThread      *writer_thread = NULL;
-static GQueue       *queue         = NULL;
-static GMutex       *mutex         = NULL;
-static GCond        *queue_cond    = NULL;
-static GCond        *max_cond      = NULL;
-static ThreadParams *in_progress   = NULL;
-static gboolean      exit_thread   = FALSE;
-
-
-static void
-gegl_tile_backend_swap_push_queue (ThreadParams *params)
-{
-  guint length;
-
-  g_mutex_lock (mutex);
-
-  length = g_queue_get_length (queue);
-
-  /* block if the queue has gotten too big */
-  if (length > gegl_config ()->queue_limit)
-    g_cond_wait (max_cond, mutex);
-
-  g_queue_push_tail (queue, params);
-
-  if (params->operation == OP_WRITE)
-    params->entry->link = g_queue_peek_tail_link (queue);
-
-  /* wake up the writer thread */
-  g_cond_signal (queue_cond);
-
-  g_mutex_unlock (mutex);
-}
-
-static void
-gegl_tile_backend_swap_write (ThreadParams *params)
-{
-  gint    to_be_written = params->length;
-  guint64 offset        = params->entry->offset;
-
-  if (out_offset != offset)
-    {
-      if (lseek (out_fd, offset, SEEK_SET) < 0)
-        {
-          g_warning ("unable to seek to tile in buffer: %s", g_strerror (errno));
-          return;
-        }
-      out_offset = offset;
-    }
-
-  while (to_be_written > 0)
-    {
-      gint wrote;
-      wrote = write (out_fd,
-                     params->source + params->length - 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);
-          break;
-        }
-
-      to_be_written -= wrote;
-      out_offset    += wrote;
-    }
-
-  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "writer thread wrote at %i", (gint)offset);
-}
-
-static gpointer
-gegl_tile_backend_swap_writer_thread (gpointer ignored)
-{
-  while (TRUE)
-    {
-      ThreadParams *params;
-
-      g_mutex_lock (mutex);
-
-      while (g_queue_is_empty (queue) && !exit_thread)
-        g_cond_wait (queue_cond, mutex);
-
-      if (exit_thread)
-        {
-          g_mutex_unlock (mutex);
-          GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "exiting writer thread");
-          return NULL;
-        }
-
-      params = (ThreadParams *)g_queue_pop_head (queue);
-      if (params->operation == OP_WRITE)
-        {
-          in_progress = params;
-          params->entry->link = NULL;
-        }
-
-      g_mutex_unlock (mutex);
-
-      switch (params->operation)
-        {
-        case OP_WRITE:
-          gegl_tile_backend_swap_write (params);
-          break;
-        case OP_TRUNCATE:
-          ftruncate (out_fd, total);
-          break;
-        }
-
-      g_mutex_lock (mutex);
-      in_progress = NULL;
-
-      /* unblock the main thread if the queue had gotten too big */
-      if (g_queue_get_length (queue) < gegl_config ()->queue_limit)
-        g_cond_signal (max_cond);
-
-      if (params->source)
-        g_free (params->source);
-
-      g_free (params);
-
-      g_mutex_unlock (mutex);
-    }
-
-  return NULL;
-}
-
-static void
-gegl_tile_backend_swap_entry_read (GeglTileBackendSwap *self,
-                                   SwapEntry           *entry,
-                                   guchar              *dest)
-{
-  gint    tile_size  = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
-  gint    to_be_read = tile_size;
-  guint64 offset     = entry->offset;
-
-  gegl_tile_backend_swap_ensure_exist ();
-
-  if (entry->link || in_progress)
-    {
-      ThreadParams *queued_op = NULL;
-      g_mutex_lock (mutex);
-
-      if (entry->link)
-        queued_op = entry->link->data;
-      else if (in_progress && in_progress->entry == entry)
-        queued_op = in_progress;
-
-      if (queued_op)
-        {
-          memcpy (dest, queued_op->source, to_be_read);
-          g_mutex_unlock (mutex);
-
-          GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "read entry %i, %i, %i from queue", entry->x, entry->y, 
entry->z);
-
-          return;
-        }
-
-      g_mutex_unlock (mutex);
-    }
-
-  if (in_offset != offset)
-    {
-      if (lseek (in_fd, offset, SEEK_SET) < 0)
-        {
-          g_warning ("unable to seek to tile in buffer: %s", g_strerror (errno));
-          return;
-        }
-      in_offset = offset;
-    }
-
-  while (to_be_read > 0)
-    {
-      GError *error = NULL;
-      gint    byte_read;
-
-      byte_read = read (in_fd, dest + tile_size - to_be_read, to_be_read);
-      if (byte_read <= 0)
-        {
-          g_message ("unable to read tile data from swap: "
-                     "%s (%d/%d bytes read) %s",
-                     g_strerror (errno), byte_read, to_be_read, error?error->message:"--");
-          return;
-        }
-      to_be_read -= byte_read;
-      in_offset  += byte_read;
-    }
-
-  GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "read entry %i, %i, %i from %i", entry->x, entry->y, entry->z, 
(gint)offset);
-}
-
-static void
-gegl_tile_backend_swap_entry_write (GeglTileBackendSwap *self,
-                                    SwapEntry           *entry,
-                                    guchar              *source)
+enum
 {
-  ThreadParams *params;
-  gint          length = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
-  guchar       *new_source;
-
-  gegl_tile_backend_swap_ensure_exist ();
-
-  if (entry->link)
-    {
-      g_mutex_lock (mutex);
-
-      if (entry->link)
-        {
-          params = entry->link->data;
-          memcpy (params->source, source, length);
-          g_mutex_unlock (mutex);
-
-          GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "overwrote queue entry %i, %i, %i at %i", entry->x, entry->y, 
entry->z, (gint)entry->offset);
-
-          return;
-        }
-
-      g_mutex_unlock (mutex);
-    }
-
-  new_source = g_malloc (length);
-  memcpy (new_source, source, length);
-
-  params            = g_new0 (ThreadParams, 1);
-  params->operation = OP_WRITE;
-  params->length    = length;
-  params->source    = new_source;
-  params->entry     = entry;
-
-  gegl_tile_backend_swap_push_queue (params);
-
-  GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "pushed write of entry %i, %i, %i at %i", entry->x, entry->y, entry->z, 
(gint)entry->offset);
-}
-
-static SwapEntry *
+  PROP_0,
+  PROP_PATH
+};
+
+
+static inline SwapEntry *gegl_tile_backend_swap_entry_create  (gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static guint64           gegl_tile_backend_swap_find_offset   (GeglTileBackendSwap      *self,
+                                                               gint                      size);
+static inline SwapGap *  gegl_tile_backend_swap_gap_new       (guint64                   start,
+                                                               guint64                   end);
+static void              gegl_tile_backend_swap_entry_destroy (GeglTileBackendSwap      *self,
+                                                               SwapEntry                *entry);
+static inline SwapEntry *gegl_tile_backend_swap_lookup_entry  (GeglTileBackendSwap      *self,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static GeglTile *        gegl_tile_backend_swap_get_tile      (GeglTileSource           *self,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static gpointer          gegl_tile_backend_swap_set_tile      (GeglTileSource           *self,
+                                                               GeglTile                 *tile,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static gpointer          gegl_tile_backend_swap_void_tile     (GeglTileSource           *self,
+                                                               GeglTile                 *tile,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static GeglBufferHeader  gegl_tile_backend_swap_read_header   (GeglTileBackendSwap      *self);
+static void              gegl_tile_backend_swap_write_header  (GeglTileBackendSwap      *self);
+static GList*            gegl_tile_backend_swap_read_index    (GeglTileBackendSwap      *self);
+static void              gegl_tile_backend_swap_load_index    (GeglTileBackendSwap      *self);
+static gpointer          gegl_tile_backend_swap_flush         (GeglTileSource           *source);
+static gpointer          gegl_tile_backend_swap_exist_tile    (GeglTileSource           *self,
+                                                               GeglTile                 *tile,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z);
+static gpointer          gegl_tile_backend_swap_command       (GeglTileSource           *self,
+                                                               GeglTileCommand           command,
+                                                               gint                      x,
+                                                               gint                      y,
+                                                               gint                      z,
+                                                               gpointer                  data);
+static void              gegl_tile_backend_swap_file_changed  (GFileMonitor             *monitor,
+                                                               GFile                    *file,
+                                                               GFile                    *other_file,
+                                                               GFileMonitorEvent         event_type,
+                                                               GeglTileBackendSwap      *self);
+static guint             gegl_tile_backend_swap_hashfunc      (gconstpointer             key);
+static gboolean          gegl_tile_backend_swap_equalfunc     (gconstpointer             a,
+                                                               gconstpointer             b);
+static GObject *         gegl_tile_backend_swap_constructor   (GType                     type,
+                                                               guint                     n_params,
+                                                               GObjectConstructParam    *params);
+static void              gegl_tile_backend_swap_finalize      (GObject                  *object);
+static void              gegl_tile_backend_swap_set_property  (GObject                  *object,
+                                                               guint                     property_id,
+                                                               const GValue             *value,
+                                                               GParamSpec               *pspec);
+static void              gegl_tile_backend_swap_get_property  (GObject                  *object,
+                                                               guint                     property_id,
+                                                               GValue                   *value,
+                                                               GParamSpec               *pspec);
+static void              gegl_tile_backend_swap_class_init    (GeglTileBackendSwapClass *klass);
+static void              gegl_tile_backend_swap_init          (GeglTileBackendSwap      *self);
+void                     gegl_tile_backend_swap_cleanup       (void);
+
+
+
+GeglAIOFile *swap_file     = NULL;
+GList       *swap_gap_list = NULL;
+
+
+static inline SwapEntry *
 gegl_tile_backend_swap_entry_create (gint x,
                                      gint y,
                                      gint z)
 {
-  SwapEntry *entry = g_new0 (SwapEntry, 1);
+  SwapEntry *entry = g_slice_new0 (SwapEntry);
 
-  entry->x    = x;
-  entry->y    = y;
-  entry->z    = z;
-  entry->link = NULL;
+  entry->x = x;
+  entry->y = y;
+  entry->z = z;
 
   return entry;
 }
 
 static guint64
-gegl_tile_backend_swap_find_offset (gint tile_size)
+gegl_tile_backend_swap_find_offset (GeglTileBackendSwap *self,
+                                    gint                 size)
 {
   SwapGap *gap;
   guint64  offset;
 
-  if (gap_list)
+  if (*self->gap_list)
     {
-      GList *link = gap_list;
+      GList *link = *self->gap_list;
 
       while (link)
         {
           gap = link->data;
 
-          if ((gap->end - gap->start) >= tile_size)
+          if ((gap->end - gap->start) >= size)
             {
               guint64 offset = gap->start;
 
-              gap->start += tile_size;
+              gap->start += size;
 
               if (gap->start == gap->end)
                 {
                   g_slice_free (SwapGap, gap);
-                  gap_list = g_list_remove_link (gap_list, link);
+                  *self->gap_list = g_list_remove_link (*self->gap_list, link);
                   g_list_free (link);
                 }
 
@@ -437,14 +204,16 @@ gegl_tile_backend_swap_find_offset (gint tile_size)
         }
     }
 
-  offset = total;
+  offset = self->file->total;
+
+  gegl_aio_file_resize (self->file, self->file->total + size + 32 * 4 * 4 * 128 * 64);
 
-  gegl_tile_backend_swap_resize (total + 32 * tile_size);
+  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "pushed resize to %i", (gint)self->file->total);
 
   return offset;
 }
 
-static SwapGap *
+static inline SwapGap *
 gegl_tile_backend_swap_gap_new (guint64 start,
                                 guint64 end)
 {
@@ -461,35 +230,15 @@ gegl_tile_backend_swap_entry_destroy (GeglTileBackendSwap *self,
                                       SwapEntry           *entry)
 {
   guint64  start, end;
-  guint64 *offset    = g_new (guint64, 1);
   gint     tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
   GList   *link, *link2;
   SwapGap *gap, *gap2;
 
-  *offset = entry->offset;
-
-  if (entry->link)
-    {
-      link = entry->link;
-
-      g_mutex_lock (mutex);
-
-      if (link)
-        {
-          ThreadParams *queued_op = link->data;
-          g_queue_delete_link (queue, link);
-          g_free (queued_op->source);
-          g_free (queued_op);
-        }
-
-      g_mutex_unlock (mutex);
-    }
-
   start = entry->offset;
   end = start + tile_size;
 
-  link = gap_list;
-  while (gap_list)
+  link = *self->gap_list;
+  while (link)
     {
       gap = link->data;
 
@@ -505,7 +254,7 @@ gegl_tile_backend_swap_entry_destroy (GeglTileBackendSwap *self,
                 {
                   gap2->end = gap->end;
                   g_slice_free (SwapGap, gap);
-                  gap_list = g_list_remove_link (gap_list, link);
+                  *self->gap_list = g_list_remove_link (*self->gap_list, link);
                   g_list_free (link);
                 }
             }
@@ -523,7 +272,8 @@ gegl_tile_backend_swap_entry_destroy (GeglTileBackendSwap *self,
                 {
                   gap2->start = gap->start;
                   g_slice_free (SwapGap, gap);
-                  gap_list = g_list_remove_link (gap_list, link);
+                  *self->gap_list =
+                    g_list_remove_link (*self->gap_list, link);
                   g_list_free (link);
                 }
             }
@@ -532,17 +282,17 @@ gegl_tile_backend_swap_entry_destroy (GeglTileBackendSwap *self,
       else if (end < gap->start)
         {
           gap = gegl_tile_backend_swap_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 == gap_list)
-            gap_list = link2;
+          if (link == *self->gap_list)
+            *self->gap_list = link2;
           break;
         }
       else if (!link->next)
@@ -557,42 +307,28 @@ gegl_tile_backend_swap_entry_destroy (GeglTileBackendSwap *self,
       link = link->next;
     }
 
-  if (!gap_list)
+  if (!*self->gap_list)
     {
       gap = gegl_tile_backend_swap_gap_new (start, end);
-      gap_list = g_list_append (gap_list, gap);
+      *self->gap_list = g_list_append (*self->gap_list, gap);
     }
 
-  link = g_list_last (gap_list);
+  link = g_list_last (*self->gap_list);
   gap = link->data;
 
-  if (gap->end < total - 32 * tile_size)
+  if (gap->end == self->file->total)
     {
-      gegl_tile_backend_swap_resize (gap->start + 32 * tile_size);
+      gegl_aio_file_resize (self->file, gap->start);
       g_slice_free (SwapGap, gap);
-      gap_list = g_list_remove_link (gap_list, link);
+      *self->gap_list = g_list_remove_link (*self->gap_list, link);
       g_list_free (link);
     }
 
   g_hash_table_remove (self->index, entry);
-  g_free (entry);
+  g_slice_free (SwapEntry, entry);
 }
 
-static void
-gegl_tile_backend_swap_resize (guint64 size)
-{
-  ThreadParams *params;
-
-  total = size;
-  params = g_new0 (ThreadParams, 1);
-  params->operation = OP_TRUNCATE;
-
-  gegl_tile_backend_swap_push_queue (params);
-
-  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "pushed resize to %i", (gint)total);
-}
-
-static SwapEntry *
+static inline SwapEntry *
 gegl_tile_backend_swap_lookup_entry (GeglTileBackendSwap *self,
                                      gint                 x,
                                      gint                 y,
@@ -602,7 +338,7 @@ gegl_tile_backend_swap_lookup_entry (GeglTileBackendSwap *self,
   SwapEntry *key = gegl_tile_backend_swap_entry_create (x, y, z);
 
   ret = g_hash_table_lookup (self->index, key);
-  g_free (key);
+  g_slice_free (SwapEntry, key);
 
   return ret;
 }
@@ -628,9 +364,13 @@ gegl_tile_backend_swap_get_tile (GeglTileSource *self,
 
   tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
   tile      = gegl_tile_new (tile_size);
+  gegl_tile_set_rev (tile, entry->rev);
   gegl_tile_mark_as_stored (tile);
 
-  gegl_tile_backend_swap_entry_read (tile_backend_swap, entry, gegl_tile_get_data (tile));
+  gegl_aio_file_read (tile_backend_swap->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;
 }
@@ -650,19 +390,22 @@ gegl_tile_backend_swap_set_tile (GeglTileSource *self,
   tile_backend_swap = GEGL_TILE_BACKEND_SWAP (backend);
   entry             = gegl_tile_backend_swap_lookup_entry (tile_backend_swap, x, y, z);
 
-  gegl_tile_backend_swap_ensure_exist ();
-
   if (entry == NULL)
     {
       entry          = gegl_tile_backend_swap_entry_create (x, y, z);
-      entry->offset  = gegl_tile_backend_swap_find_offset (gegl_tile_backend_get_tile_size (backend));
+      entry->offset  = gegl_tile_backend_swap_find_offset (tile_backend_swap,
+                                                           gegl_tile_backend_get_tile_size (backend));
       g_hash_table_insert (tile_backend_swap->index, entry, entry);
     }
 
-  gegl_tile_backend_swap_entry_write (tile_backend_swap, entry, gegl_tile_get_data (tile));
+  gegl_aio_file_write (tile_backend_swap->file, entry->offset,
+                       gegl_tile_backend_get_tile_size (backend),
+                       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;
 }
 
@@ -691,6 +434,211 @@ gegl_tile_backend_swap_void_tile (GeglTileSource *self,
   return NULL;
 }
 
+static GeglBufferHeader
+gegl_tile_backend_swap_read_header (GeglTileBackendSwap *self)
+{
+  GeglBufferHeader ret;
+
+  gegl_aio_file_read(self->file, 0, sizeof (GeglBufferHeader), (guchar*)&ret);
+
+  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_swap_write_header (GeglTileBackendSwap *self)
+{
+  gegl_aio_file_write (self->file, 0, sizeof (GeglBufferHeader), (guchar*)&self->header);
+}
+
+static GList *
+gegl_tile_backend_swap_read_index (GeglTileBackendSwap *self)
+{
+  GList          *ret    = NULL;
+  guint64         offset = self->header.next;
+  GeglBufferTile *item;
+
+  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);
+    }
+
+  ret = g_list_reverse (ret);
+
+  return ret;
+}
+
+static void
+gegl_tile_backend_swap_load_index (GeglTileBackendSwap *self)
+{
+  GeglBufferHeader  new_header;
+  GList            *tiles, *iter;
+  GeglTileBackend  *backend = GEGL_TILE_BACKEND (self);
+  guint64           max     = 0;
+  gint              tile_size;
+
+  new_header = gegl_tile_backend_swap_read_header (self);
+
+  while (new_header.flags & GEGL_FLAG_LOCKED)
+    {
+      g_usleep (50000);
+      new_header = gegl_tile_backend_swap_read_header (self);
+    }
+
+  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);
+    }
+
+  tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
+  tiles = gegl_tile_backend_swap_read_index (self);
+
+  for (iter = tiles; iter; iter = iter->next)
+    {
+      GeglBufferTile *item           = iter->data;
+      SwapEntry      *new, *existing =
+        gegl_tile_backend_swap_lookup_entry (self, item->x, item->y, item->z);
+
+      if (item->offset > max)
+        max = item->offset + tile_size;
+
+      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;
+
+              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_swap_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_swap_flush (GeglTileSource *source)
+{
+  GeglTileBackend     *backend;
+  GeglTileBackendSwap *self;
+  GList               *tiles;
+
+  backend  = GEGL_TILE_BACKEND (source);
+  self     = GEGL_TILE_BACKEND_SWAP (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_swap_find_offset (self, index_len);
+      index_len    = 0;
+
+      for (iter = tiles; iter; iter = iter->next)
+        {
+          SwapEntry      *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_swap_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_swap_exist_tile (GeglTileSource *self,
                                    GeglTile       *tile,
@@ -709,6 +657,37 @@ gegl_tile_backend_swap_exist_tile (GeglTileSource *self,
   return entry!=NULL?((gpointer)0x1):NULL;
 }
 
+gboolean
+gegl_tile_backend_swap_try_lock (GeglTileBackendSwap *self)
+{
+  GeglBufferHeader new_header = gegl_tile_backend_swap_read_header (self);
+
+  if (new_header.flags & GEGL_FLAG_LOCKED)
+    return FALSE;
+
+  self->header.flags += GEGL_FLAG_LOCKED;
+  gegl_tile_backend_swap_write_header (self);
+  gegl_aio_file_sync (self->file);
+
+  return TRUE;
+}
+
+gboolean
+gegl_tile_backend_swap_unlock (GeglTileBackendSwap *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_swap_write_header (self);
+  gegl_aio_file_sync (self->file);
+
+  return TRUE;
+}
+
 static gpointer
 gegl_tile_backend_swap_command (GeglTileSource  *self,
                                 GeglTileCommand  command,
@@ -730,7 +709,7 @@ gegl_tile_backend_swap_command (GeglTileSource  *self,
       case GEGL_TILE_EXIST:
         return gegl_tile_backend_swap_exist_tile (self, data, x, y, z);
       case GEGL_TILE_FLUSH:
-        return NULL;
+        return gegl_tile_backend_swap_flush (self);
 
       default:
         g_assert (command < GEGL_TILE_LAST_COMMAND &&
@@ -739,6 +718,17 @@ gegl_tile_backend_swap_command (GeglTileSource  *self,
   return FALSE;
 }
 
+static void
+gegl_tile_backend_swap_file_changed (GFileMonitor        *monitor,
+                                     GFile               *file,
+                                     GFile               *other_file,
+                                     GFileMonitorEvent    event_type,
+                                     GeglTileBackendSwap *self)
+{
+  if (event_type == G_FILE_MONITOR_EVENT_CHANGED)
+    gegl_tile_backend_swap_load_index (self);
+}
+
 static guint
 gegl_tile_backend_swap_hashfunc (gconstpointer key)
 {
@@ -794,6 +784,67 @@ gegl_tile_backend_swap_constructor (GType                  type,
   self->index = g_hash_table_new (gegl_tile_backend_swap_hashfunc,
                                   gegl_tile_backend_swap_equalfunc);
 
+  if (self->path)
+    { /* if the path is specified, use a separate file for this buffer */
+      GeglTileBackend *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;
+
+      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_swap_file_changed),
+                            self);
+          self->header = gegl_tile_backend_swap_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_swap_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
+    { /* use a file that's shared between multiple buffers */
+      if (swap_file == NULL)
+        {
+          gchar *filename, *path;
+
+          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);
+        }
+
+      self->gap_list = &swap_gap_list;
+      self->file = swap_file;
+    }
+
   GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "constructing swap backend");
 
   return object;
@@ -823,28 +874,83 @@ gegl_tile_backend_swap_finalize (GObject *object)
       self->index = NULL;
     }
 
+  if (self->file && self->file != swap_file)
+    {
+      g_object_unref (self->file);
+      self->file = NULL;
+    }
+
+  if (self->gfile)
+    {
+      g_object_unref (self->gfile);
+      self->gfile = NULL;
+    }
+
+  if (self->monitor)
+    {
+      g_object_unref (self->monitor);
+      self->monitor = NULL;
+    }
+
+  if (self->path)
+    {
+      g_free (self->path);
+      self->path = NULL;
+    }
+
+  if (*self->gap_list && *self->gap_list != swap_gap_list)
+    {
+      GList *iter;
+
+      for (iter = *self->gap_list; iter; iter = iter->next)
+        g_slice_free (SwapGap, iter->data);
+
+      g_list_free (*self->gap_list);
+      *self->gap_list = NULL;
+    }
+
   (*G_OBJECT_CLASS (parent_class)->finalize)(object);
 }
 
 static void
-gegl_tile_backend_swap_ensure_exist (void)
+gegl_tile_backend_swap_set_property (GObject      *object,
+                                     guint         property_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
 {
-  if (in_fd == -1 || out_fd == -1)
-    {
-      gchar *filename = g_strdup_printf ("%i-shared.swap", getpid ());
-      path = g_build_filename (gegl_config ()->swap, filename, NULL);
-      g_free (filename);
+  GeglTileBackendSwap *self = GEGL_TILE_BACKEND_SWAP (object);
 
-      GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "creating swapfile %s", 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;
+    }
+}
 
-      out_fd = g_open (path, O_RDWR|O_CREAT, 0770);
-      in_fd = g_open (path, O_RDONLY);
+static void
+gegl_tile_backend_swap_get_property (GObject    *object,
+                                     guint       property_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  GeglTileBackendSwap *self = GEGL_TILE_BACKEND_SWAP (object);
 
-      if (out_fd == -1 || in_fd == -1)
-        g_warning ("Could not open swap file '%s': %s", path, g_strerror (errno));
+  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_swap_class_init (GeglTileBackendSwapClass *klass)
 {
@@ -854,40 +960,44 @@ gegl_tile_backend_swap_class_init (GeglTileBackendSwapClass *klass)
 
   gobject_class->constructor  = gegl_tile_backend_swap_constructor;
   gobject_class->finalize     = gegl_tile_backend_swap_finalize;
+  gobject_class->set_property = gegl_tile_backend_swap_set_property;
+  gobject_class->get_property = gegl_tile_backend_swap_get_property;
+
+  g_object_class_install_property (gobject_class, PROP_PATH,
+                                   g_param_spec_string ("path",
+                                                        "path",
+                                                        "Path of the backing file",
+                                                        NULL,
+                                                        G_PARAM_CONSTRUCT_ONLY |
+                                                        G_PARAM_READWRITE));
+}
+
+static void
+gegl_tile_backend_swap_init (GeglTileBackendSwap *self)
+{
+  ((GeglTileSource*)self)->command = gegl_tile_backend_swap_command;
 
-  queue         = g_queue_new ();
-  queue_cond    = g_cond_new ();
-  max_cond      = g_cond_new ();
-  mutex         = g_mutex_new ();
-  writer_thread = g_thread_create_full (gegl_tile_backend_swap_writer_thread,
-                                        NULL, 0, TRUE, TRUE,
-                                        G_THREAD_PRIORITY_NORMAL, NULL);
+  self->path     = NULL;
+  self->file     = NULL;
+  self->index    = NULL;
+  self->gap_list = NULL;
+  self->gfile    = NULL;
+  self->monitor  = NULL;
 }
 
 void
 gegl_tile_backend_swap_cleanup (void)
 {
-  if (in_fd != -1 && out_fd != -1)
-    {
-      exit_thread = TRUE;
-      g_cond_signal (queue_cond);
-      g_thread_join (writer_thread);
+  if (swap_file)
+    g_object_unref (swap_file);
 
-      if (g_queue_get_length (queue) != 0)
-        g_warning ("tile-backend-swap writer queue wasn't empty before freeing\n");
+  if (swap_gap_list)
+    {
+      GList *iter;
 
-      g_queue_free (queue);
-      g_cond_free (queue_cond);
-      g_cond_free (max_cond);
-      g_mutex_free (mutex);
+      for (iter = swap_gap_list; iter; iter = iter->next)
+        g_slice_free (SwapGap, iter->data);
 
-      close (in_fd);
-      close (out_fd);
+      g_list_free (swap_gap_list);
     }
 }
-
-static void
-gegl_tile_backend_swap_init (GeglTileBackendSwap *self)
-{
-  ((GeglTileSource*)self)->command = gegl_tile_backend_swap_command;
-}
diff --git a/gegl/buffer/gegl-tile-backend-swap.h b/gegl/buffer/gegl-tile-backend-swap.h
index 87a98f1..f4c7d3b 100644
--- a/gegl/buffer/gegl-tile-backend-swap.h
+++ b/gegl/buffer/gegl-tile-backend-swap.h
@@ -21,6 +21,8 @@
 
 #include <glib.h>
 #include "gegl-tile-backend.h"
+#include "gegl-buffer-index.h"
+#include "gegl-aio-file.h"
 
 G_BEGIN_DECLS
 
@@ -42,12 +44,22 @@ struct _GeglTileBackendSwapClass
 
 struct _GeglTileBackendSwap
 {
-  GeglTileBackend  parent_instance;
-  GHashTable      *index;
+  GeglTileBackend   parent_instance;
+  gchar            *path;
+  GeglAIOFile      *file;
+  GHashTable       *index;
+  GList            **gap_list;
+  GeglBufferHeader  header;
+  GFile            *gfile;
+  GFileMonitor     *monitor;
 };
 
 GType gegl_tile_backend_swap_get_type (void) G_GNUC_CONST;
 
+gboolean gegl_tile_backend_swap_try_lock (GeglTileBackendSwap *self);
+
+gboolean gegl_tile_backend_swap_unlock (GeglTileBackendSwap *self);
+
 G_END_DECLS
 
 #endif
diff --git a/gegl/gegl-config.c b/gegl/gegl-config.c
index 5c156eb..35609a2 100644
--- a/gegl/gegl-config.c
+++ b/gegl/gegl-config.c
@@ -46,7 +46,7 @@ enum
   PROP_TILE_HEIGHT,
   PROP_THREADS,
   PROP_USE_OPENCL,
-  PROP_QUEUE_LIMIT
+  PROP_QUEUE_SIZE
 };
 
 static void
@@ -99,8 +99,8 @@ gegl_config_get_property (GObject    *gobject,
         g_value_set_boolean (value, config->use_opencl);
         break;
 
-      case PROP_QUEUE_LIMIT:
-        g_value_set_int (value, config->queue_limit);
+      case PROP_QUEUE_SIZE:
+        g_value_set_int (value, config->queue_size);
         break;
 
       default:
@@ -181,8 +181,8 @@ gegl_config_set_property (GObject      *gobject,
           gegl_cl_init (NULL);
 
         break;
-      case PROP_QUEUE_LIMIT:
-        config->queue_limit = g_value_get_int (value);
+      case PROP_QUEUE_SIZE:
+        config->queue_size = g_value_get_uint64 (value);
         break;
       default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, property_id, pspec);
@@ -293,13 +293,13 @@ gegl_config_class_init (GeglConfigClass *klass)
                                                          G_PARAM_READWRITE |
                                                          G_PARAM_CONSTRUCT));
 
-  g_object_class_install_property (gobject_class, PROP_QUEUE_LIMIT,
-                                   g_param_spec_int ("queue-limit",
-                                                     "Queue limit",
-                                                     "Maximum number of entries in the file tile backend's 
writer queue",
-                                                     2, G_MAXINT, 1000,
-                                                     G_PARAM_READWRITE |
-                                                     G_PARAM_CONSTRUCT));
+  g_object_class_install_property (gobject_class, PROP_QUEUE_SIZE,
+                                   g_param_spec_uint64 ("queue-size",
+                                                        "Queue size",
+                                                        "Maximum size of the file backend's thread queue in 
megabytes",
+                                                        2, G_MAXUINT64, 100 * 1024 * 1024,
+                                                        G_PARAM_READWRITE |
+                                                        G_PARAM_CONSTRUCT));
 }
 
 static void
diff --git a/gegl/gegl-config.h b/gegl/gegl-config.h
index 4d4c3b4..7f9aeac 100644
--- a/gegl/gegl-config.h
+++ b/gegl/gegl-config.h
@@ -44,7 +44,7 @@ struct _GeglConfig
   gint     tile_height;
   gint     threads;
   gboolean use_opencl;
-  gint     queue_limit;
+  guint64  queue_size;
 };
 
 struct _GeglConfigClass
diff --git a/gegl/gegl-init.c b/gegl/gegl-init.c
index 5c6d15b..776ef16 100644
--- a/gegl/gegl-init.c
+++ b/gegl/gegl-init.c
@@ -202,7 +202,7 @@ static gchar    *cmd_gegl_tile_size   = NULL;
 static gchar    *cmd_babl_tolerance   = NULL;
 static gchar    *cmd_gegl_threads     = NULL;
 static gboolean *cmd_gegl_opencl      = NULL;
-static gint     *cmd_gegl_queue_limit = NULL;
+static gchar    *cmd_gegl_queue_size = NULL;
 
 static const GOptionEntry cmd_entries[]=
 {
@@ -247,9 +247,9 @@ static const GOptionEntry cmd_entries[]=
       N_("Use OpenCL"), NULL
     },
     {
-      "gegl-queue-limit", 0, 0,
-      G_OPTION_ARG_INT, &cmd_gegl_queue_limit,
-      N_("Maximum number of entries in the file tile backend's writer queue"), "<count>"
+      "gegl-queue-size", 0, 0,
+      G_OPTION_ARG_INT, &cmd_gegl_queue_size,
+      N_("Maximum size of the file backend's thread queue in megabytes"), "<megabytes>"
     },
     { NULL }
 };
@@ -303,8 +303,8 @@ GeglConfig *gegl_config (void)
       else
         config->use_opencl = TRUE;
 
-      if (g_getenv ("GEGL_QUEUE_LIMIT"))
-        config->queue_limit = atoi(g_getenv ("GEGL_QUEUE_LIMIT"));
+      if (g_getenv ("GEGL_QUEUE_SIZE"))
+        config->queue_size = atoll(g_getenv ("GEGL_QUEUE_SIZE")) * 1024 * 1024;
 
       if (gegl_swap_dir())
         config->swap = g_strdup(gegl_swap_dir ());
@@ -355,6 +355,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
@@ -371,6 +372,7 @@ gegl_exit (void)
   timing = gegl_ticks ();
 
   gegl_tile_backend_swap_cleanup ();
+  gegl_aio_file_cleanup ();
   gegl_tile_storage_cache_cleanup ();
   gegl_tile_cache_destroy ();
   gegl_operation_gtype_cleanup ();
@@ -520,8 +522,8 @@ gegl_post_parse_hook (GOptionContext *context,
   /* don't override the environment variable */
   if (g_getenv ("GEGL_USE_OPENCL") == NULL && cmd_gegl_opencl)
     g_object_set (config, "use-opencl", cmd_gegl_opencl, NULL);
-  if (cmd_gegl_queue_limit)
-    g_object_set (config, "queue-limit", cmd_gegl_queue_limit, NULL);
+  if (cmd_gegl_queue_size)
+    config->queue_size = atoll (cmd_gegl_queue_size) * 1024 * 1024;
 
 
   time = gegl_ticks ();


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