[gegl] tile-backend-swap: make thread safe; move more work to writer thread



commit 5f4414f0261778644ba96db98cb9d0ea09ff6a6a
Author: Ell <ell_se yahoo com>
Date:   Fri Jul 21 08:20:56 2017 -0400

    tile-backend-swap: make thread safe; move more work to writer thread
    
    Make the swap tile backend thread safe, by adding locks as necessary,
    and by moving more of the work to the writer thread, to avoid having
    to synchronize.
    
    In particular, all reader threads share the same file destrcriptor,
    so they must not try to read from the swap in parallel.  Likewise,
    all manipulation of the gap list has to be synchronized, and was
    moved to the writer thread.  This has the additional benefit that
    storage reclamation for dead entries now happens in the writer thread,
    before any outstanding write operations are performed, while, in turn,
    storage allocation also happens in the writer thread, right before an
    entry is written to the swap; this helps minimize the gaps between swap
    entries.
    
    Additionally, TILE_GET commands for tiles that are queued for writing
    now return a COWed copy of the queued data, instead of a new copy.
    
    This fixes ceratin artifacts in multithreaded programs.  See
    bug #784907.

 gegl/buffer/gegl-tile-backend-swap.c |  589 +++++++++++++++++++---------------
 1 files changed, 324 insertions(+), 265 deletions(-)
---
diff --git a/gegl/buffer/gegl-tile-backend-swap.c b/gegl/buffer/gegl-tile-backend-swap.c
index 50a34a0..f7b6364 100644
--- a/gegl/buffer/gegl-tile-backend-swap.c
+++ b/gegl/buffer/gegl-tile-backend-swap.c
@@ -61,16 +61,16 @@ static GObjectClass * parent_class = NULL;
 typedef enum
 {
   OP_WRITE,
-  OP_TRUNCATE,
+  OP_DESTROY,
 } ThreadOp;
 
 typedef struct
 {
-  guint64  offset;
-  GList   *link;
-  gint     x;
-  gint     y;
-  gint     z;
+  gint64  offset;
+  GList  *link;
+  gint    x;
+  gint    y;
+  gint    z;
 } SwapEntry;
 
 typedef struct
@@ -83,29 +83,30 @@ typedef struct
 
 typedef struct
 {
-  guint64 start;
-  guint64 end;
+  gint64 start;
+  gint64 end;
 } SwapGap;
 
 
-static void        gegl_tile_backend_swap_push_queue    (ThreadParams *params);
-static void        gegl_tile_backend_swap_write         (ThreadParams *params);
+static void        gegl_tile_backend_swap_push_queue    (ThreadParams          *params,
+                                                         gboolean               head);
+static void        gegl_tile_backend_swap_resize        (gint64                 size);
+static SwapGap *   gegl_tile_backend_swap_gap_new       (gint64                 start,
+                                                         gint64                 end);
+static gint64      gegl_tile_backend_swap_find_offset   (gint                   tile_size);
+static void        gegl_tile_backend_swap_write         (ThreadParams          *params);
+static void        gegl_tile_backend_swap_destroy       (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 GeglTile   *gegl_tile_backend_swap_entry_read    (GeglTileBackendSwap   *self,
+                                                         SwapEntry             *entry);
 static void        gegl_tile_backend_swap_entry_write   (GeglTileBackendSwap   *self,
                                                          SwapEntry             *entry,
                                                          GeglTile              *tile);
 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,
@@ -146,43 +147,127 @@ static void        gegl_tile_backend_swap_init          (GeglTileBackendSwap *se
 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 gchar  *path       = NULL;
+static gint    in_fd      = -1;
+static gint    out_fd     = -1;
+static gint64  in_offset  = 0;
+static gint64  out_offset = 0;
+static GList  *gap_list   = NULL;
+static gint64  total      = 0;
 
 static GThread      *writer_thread = NULL;
 static GQueue       *queue         = NULL;
 static ThreadParams *in_progress   = NULL;
 static gboolean      exit_thread   = FALSE;
-static GMutex        mutex;
+static GMutex        read_mutex;
+static GMutex        queue_mutex;
 static GCond         queue_cond;
 
 
 static void
-gegl_tile_backend_swap_push_queue (ThreadParams *params)
+gegl_tile_backend_swap_push_queue (ThreadParams *params,
+                                   gboolean      head)
 {
-  g_mutex_lock (&mutex);
-
-  g_queue_push_tail (queue, params);
+  if (head)
+    g_queue_push_head (queue, params);
+  else
+    g_queue_push_tail (queue, params);
 
-  if (params->operation == OP_WRITE)
+  if (params->entry)
     params->entry->link = g_queue_peek_tail_link (queue);
 
   /* wake up the writer thread */
   g_cond_signal (&queue_cond);
+}
+
+static void
+gegl_tile_backend_swap_resize (gint64 size)
+{
+  total = size;
+
+  if (ftruncate (out_fd, total) != 0)
+    {
+      g_warning ("failed to resize swap file: %s", g_strerror (errno));
+      return;
+    }
+
+  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "resized swap to %i", (gint)total);
+}
 
-  g_mutex_unlock (&mutex);
+static SwapGap *
+gegl_tile_backend_swap_gap_new (gint64 start,
+                                gint64 end)
+{
+  SwapGap *gap = g_slice_new (SwapGap);
+
+  gap->start = start;
+  gap->end = end;
+
+  return gap;
+}
+
+static gint64
+gegl_tile_backend_swap_find_offset (gint tile_size)
+{
+  SwapGap *gap;
+  gint64   offset;
+
+  if (gap_list)
+    {
+      GList *link = gap_list;
+
+      while (link)
+        {
+          gint64 length;
+
+          gap    = link->data;
+          length = gap->end - gap->start;
+
+          if (length > tile_size)
+            {
+              offset = gap->start;
+              gap->start += tile_size;
+
+              return offset;
+            }
+          else if (length == tile_size)
+            {
+              offset = gap->start;
+              g_slice_free (SwapGap, gap);
+              gap_list = g_list_delete_link (gap_list, link);
+
+              return offset;
+            }
+
+          link = link->next;
+        }
+    }
+
+  offset = total;
+
+  gegl_tile_backend_swap_resize (total + 32 * tile_size);
+
+  gap = gegl_tile_backend_swap_gap_new (offset + tile_size, total);
+  gap_list = g_list_append (gap_list, gap);
+
+  return offset;
 }
 
 static void
 gegl_tile_backend_swap_write (ThreadParams *params)
 {
-  gint    to_be_written = params->length;
-  guint64 offset        = params->entry->offset;
+  gint   to_be_written = params->length;
+  gint64 offset        = params->entry->offset;
+
+  gegl_tile_backend_swap_ensure_exist ();
+
+  if (offset < 0)
+    {
+      /* storage for entry not allocated yet.  allocate now. */
+      offset = gegl_tile_backend_swap_find_offset (to_be_written);
+
+      params->entry->offset = offset;
+    }
 
   if (out_offset != offset)
     {
@@ -216,100 +301,199 @@ gegl_tile_backend_swap_write (ThreadParams *params)
   GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "writer thread wrote at %i", (gint)offset);
 }
 
+static void
+gegl_tile_backend_swap_destroy (ThreadParams *params)
+{
+  gint64  start, end;
+  GList  *hlink;
+
+  start = params->entry->offset;
+  end = start + params->length;
+
+  g_assert (start >= 0);
+
+  g_slice_free (SwapEntry, params->entry);
+
+  if ((hlink = gap_list))
+    while (hlink)
+      {
+        GList *llink = hlink->prev;
+        SwapGap *lgap, *hgap;
+
+        if (llink)
+          lgap = llink->data;
+
+        hgap = hlink->data;
+
+        /* attempt to merge lower, upper and this gap */
+        if (llink != NULL && lgap->end == start &&
+            hgap->start == end)
+          {
+            llink->next = hlink->next;
+            if (hlink->next)
+              hlink->next->prev = llink;
+            lgap->end = hgap->end;
+
+            g_slice_free (SwapGap, hgap);
+            hlink->next = NULL;
+            hlink->prev = NULL;
+            g_list_free (hlink);
+            break;
+          }
+        /* attempt to merge with upper gap */
+        else if (hgap->start == end)
+          {
+            hgap->start = start;
+            break;
+          }
+        /* attempt to merge with lower gap */
+        else if (llink != NULL && lgap->end == start)
+          {
+            lgap->end = end;
+            break;
+          }
+        /* create new gap */
+        else if (hgap->start > end)
+          {
+            lgap = gegl_tile_backend_swap_gap_new (start, end);
+            gap_list = g_list_insert_before (gap_list, hlink, lgap);
+            break;
+          }
+
+        /* if there's no more elements in the list after this */
+        else if (hlink->next == NULL)
+          {
+            /* attempt to merge with the last gap */
+            if (hgap->end == start)
+              {
+                hgap->end = end;
+              }
+            /* create a new gap in the end of the list */
+            else
+              {
+                GList *new_link;
+                hgap = gegl_tile_backend_swap_gap_new (start, end);
+                new_link = g_list_alloc ();
+                new_link->next = NULL;
+                new_link->prev = hlink;
+                new_link->data = hgap;
+                hlink->next = new_link;
+              }
+            break;
+          }
+
+        hlink = hlink->next;
+      }
+  else
+    gap_list = g_list_prepend (NULL,
+                               gegl_tile_backend_swap_gap_new (start, end));
+}
+
 static gpointer
 gegl_tile_backend_swap_writer_thread (gpointer ignored)
 {
+  g_mutex_lock (&queue_mutex);
+
   while (TRUE)
     {
       ThreadParams *params;
 
-      g_mutex_lock (&mutex);
-
       while (g_queue_is_empty (queue) && !exit_thread)
-        g_cond_wait (&queue_cond, &mutex);
+        g_cond_wait (&queue_cond, &queue_mutex);
 
       if (exit_thread)
-        {
-          g_mutex_unlock (&mutex);
-          GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "exiting writer thread");
-          return NULL;
-        }
+        break;
 
       params = (ThreadParams *)g_queue_pop_head (queue);
-      if (params->operation == OP_WRITE)
+      if (params->entry)
         {
           in_progress = params;
           params->entry->link = NULL;
         }
 
-      g_mutex_unlock (&mutex);
+      g_mutex_unlock (&queue_mutex);
 
       switch (params->operation)
         {
         case OP_WRITE:
           gegl_tile_backend_swap_write (params);
           break;
-        case OP_TRUNCATE:
-          if (ftruncate (out_fd, total) != 0)
-            g_warning ("failed to resize swap file: %s", g_strerror (errno));
+        case OP_DESTROY:
+          gegl_tile_backend_swap_destroy (params);
           break;
         }
 
-      g_mutex_lock (&mutex);
+      g_mutex_lock (&queue_mutex);
 
       in_progress = NULL;
 
-      if (params->operation == OP_WRITE)
+      if (params->tile)
         gegl_tile_unref (params->tile);
 
       g_slice_free (ThreadParams, params);
-
-      g_mutex_unlock (&mutex);
     }
 
+  g_mutex_unlock (&queue_mutex);
+  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "exiting writer thread");
   return NULL;
 }
 
-static void
+static GeglTile *
 gegl_tile_backend_swap_entry_read (GeglTileBackendSwap *self,
-                                   SwapEntry           *entry,
-                                   guchar              *dest)
+                                   SwapEntry           *entry)
 {
-  gint    tile_size  = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
-  gint    to_be_read = tile_size;
-  guint64 offset     = entry->offset;
+  gint      tile_size  = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
+  gint      to_be_read = tile_size;
+  gint64    offset;
+  GeglTile *tile;
+  guchar   *dest;
 
-  gegl_tile_backend_swap_ensure_exist ();
+  g_mutex_lock (&queue_mutex);
 
   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)
+      else if (in_progress->entry == entry)
         queued_op = in_progress;
 
       if (queued_op)
         {
-          memcpy (dest, gegl_tile_get_data (queued_op->tile), to_be_read);
-          g_mutex_unlock (&mutex);
+          tile = gegl_tile_dup (queued_op->tile);
+          g_mutex_unlock (&queue_mutex);
 
           GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "read entry %i, %i, %i from queue", entry->x, entry->y, 
entry->z);
 
-          return;
+          return tile;
         }
+    }
 
-      g_mutex_unlock (&mutex);
+  offset = entry->offset;
+
+  g_mutex_unlock (&queue_mutex);
+
+  if (offset < 0 || in_fd < 0)
+    {
+      g_warning ("no swap storage allocated for tile");
+      return NULL;
     }
 
+  tile = gegl_tile_new (tile_size);
+  dest = gegl_tile_get_data (tile);
+  gegl_tile_mark_as_stored (tile);
+
+  g_mutex_lock (&read_mutex);
+
   if (in_offset != offset)
     {
       if (lseek (in_fd, offset, SEEK_SET) < 0)
         {
+          g_mutex_unlock (&read_mutex);
+
           g_warning ("unable to seek to tile in buffer: %s", g_strerror (errno));
-          return;
+          return tile;
         }
       in_offset = offset;
     }
@@ -322,16 +506,22 @@ gegl_tile_backend_swap_entry_read (GeglTileBackendSwap *self,
       byte_read = read (in_fd, dest + tile_size - to_be_read, to_be_read);
       if (byte_read <= 0)
         {
+          g_mutex_unlock (&read_mutex);
+
           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;
+          return tile;
         }
       to_be_read -= byte_read;
       in_offset  += byte_read;
     }
 
+  g_mutex_unlock (&read_mutex);
+
   GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "read entry %i, %i, %i from %i", entry->x, entry->y, entry->z, 
(gint)offset);
+
+  return tile;
 }
 
 static void
@@ -342,25 +532,19 @@ gegl_tile_backend_swap_entry_write (GeglTileBackendSwap *self,
   ThreadParams *params;
   gint          length = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
 
-  gegl_tile_backend_swap_ensure_exist ();
+  g_mutex_lock (&queue_mutex);
 
   if (entry->link)
     {
-      g_mutex_lock (&mutex);
+      params = entry->link->data;
+      g_assert (params->operation == OP_WRITE);
+      gegl_tile_unref (params->tile);
+      params->tile = gegl_tile_dup (tile);
+      g_mutex_unlock (&queue_mutex);
 
-      if (entry->link)
-        {
-          params = entry->link->data;
-          gegl_tile_unref (params->tile);
-          params->tile = gegl_tile_dup (tile);
-          g_mutex_unlock (&mutex);
+      GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "tile %i, %i, %i at %i is already enqueued, changed data", 
entry->x, entry->y, entry->z, (gint)entry->offset);
 
-          GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "tile %i, %i, %i at %i is already enqueued, changed data", 
entry->x, entry->y, entry->z, (gint)entry->offset);
-
-          return;
-        }
-
-      g_mutex_unlock (&mutex);
+      return;
     }
 
   params            = g_slice_new0 (ThreadParams);
@@ -369,7 +553,9 @@ gegl_tile_backend_swap_entry_write (GeglTileBackendSwap *self,
   params->tile      = gegl_tile_dup (tile);
   params->entry     = entry;
 
-  gegl_tile_backend_swap_push_queue (params);
+  gegl_tile_backend_swap_push_queue (params, /* head = */ FALSE);
+
+  g_mutex_unlock (&queue_mutex);
 
   GEGL_NOTE(GEGL_DEBUG_TILE_BACKEND, "pushed write of entry %i, %i, %i at %i", entry->x, entry->y, entry->z, 
(gint)entry->offset);
 }
@@ -381,191 +567,65 @@ gegl_tile_backend_swap_entry_create (gint x,
 {
   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;
+  entry->link   = NULL;
+  entry->offset = -1;
 
   return entry;
 }
 
-static guint64
-gegl_tile_backend_swap_find_offset (gint tile_size)
-{
-  SwapGap *gap;
-  guint64  offset;
-
-  if (gap_list)
-    {
-      GList *link = gap_list;
-
-      while (link)
-        {
-          guint64 length;
-
-          gap    = link->data;
-          length = gap->end - gap->start;
-
-          if (length > tile_size)
-            {
-              offset = gap->start;
-              gap->start += tile_size;
-
-              return offset;
-            }
-          else if (length == tile_size)
-            {
-              offset = gap->start;
-              g_slice_free (SwapGap, gap);
-              gap_list = g_list_delete_link (gap_list, link);
-
-              return offset;
-            }
-
-          link = link->next;
-        }
-    }
-
-  offset = total;
-
-  gegl_tile_backend_swap_resize (total + 32 * tile_size);
-
-  gap = gegl_tile_backend_swap_gap_new (offset + tile_size, total);
-  gap_list = g_list_append (gap_list, gap);
-
-  return offset;
-}
-
-static SwapGap *
-gegl_tile_backend_swap_gap_new (guint64 start,
-                                guint64 end)
-{
-  SwapGap *gap = g_slice_new (SwapGap);
-
-  gap->start = start;
-  gap->end = end;
-
-  return gap;
-}
-
 static void
 gegl_tile_backend_swap_entry_destroy (GeglTileBackendSwap *self,
                                       SwapEntry           *entry)
 {
-  guint64  start, end;
-  gint     tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
-  GList   *hlink;
+  ThreadParams *params;
 
   if (entry->link)
     {
-      GList *link;
+      GList        *link      = entry->link;
+      ThreadParams *queued_op = link->data;
 
-      g_mutex_lock (&mutex);
+      if (queued_op->tile)
+        {
+          gegl_tile_unref (queued_op->tile);
+          queued_op->tile = NULL;
+        }
 
-      if ((link = entry->link))
+      if (entry->offset >= 0)
+        {
+          /* the queued op's entry already has storage allocated to it, which
+           * needs to be reclaimed.  change it to an OP_DESTROY, and move it to
+           * the top.
+           */
+          queued_op->operation = OP_DESTROY;
+
+          g_queue_unlink (queue, link);
+          g_queue_push_head_link (queue, link);
+        }
+      else
         {
-          ThreadParams *queued_op = link->data;
+          /* the queued op's entry doesn't have storage allocated to it, so we
+           * can simply free it here.
+           */
           g_queue_delete_link (queue, link);
-          gegl_tile_unref (queued_op->tile);
           g_slice_free (ThreadParams, queued_op);
+          g_slice_free (SwapEntry, entry);
         }
 
-      g_mutex_unlock (&mutex);
+      return;
     }
 
-  start = entry->offset;
-  end = start + tile_size;
-
-  if ((hlink = gap_list))
-    while (hlink)
-      {
-        GList *llink = hlink->prev;
-        SwapGap *lgap, *hgap;
-
-        if (llink)
-          lgap = llink->data;
-
-        hgap = hlink->data;
-
-        /* attempt to merge lower, upper and this gap */
-        if (llink != NULL && lgap->end == start &&
-            hgap->start == end)
-          {
-            llink->next = hlink->next;
-            if (hlink->next)
-              hlink->next->prev = llink;
-            lgap->end = hgap->end;
-
-            g_slice_free (SwapGap, hgap);
-            hlink->next = NULL;
-            hlink->prev = NULL;
-            g_list_free (hlink);
-            break;
-          }
-        /* attempt to merge with upper gap */
-        else if (hgap->start == end)
-          {
-            hgap->start = start;
-            break;
-          }
-        /* attempt to merge with lower gap */
-        else if (llink != NULL && lgap->end == start)
-          {
-            lgap->end = end;
-            break;
-          }
-        /* create new gap */
-        else if (hgap->start > end)
-          {
-            lgap = gegl_tile_backend_swap_gap_new (start, end);
-            gap_list = g_list_insert_before (gap_list, hlink, lgap);
-            break;
-          }
-
-        /* if there's no more elements in the list after this */
-        else if (hlink->next == NULL)
-          {
-            /* attempt to merge with the last gap */
-            if (hgap->end == start)
-              {
-                hgap->end = end;
-              }
-            /* create a new gap in the end of the list */
-            else
-              {
-                GList *new_link;
-                hgap = gegl_tile_backend_swap_gap_new (start, end);
-                new_link = g_list_alloc ();
-                new_link->next = NULL;
-                new_link->prev = hlink;
-                new_link->data = hgap;
-                hlink->next = new_link;
-              }
-            break;
-          }
-
-        hlink = hlink->next;
-      }
-  else
-    gap_list = g_list_prepend (NULL,
-                               gegl_tile_backend_swap_gap_new (start, end));
-
-  g_hash_table_remove (self->index, entry);
-  g_slice_free (SwapEntry, entry);
-}
-
-static void
-gegl_tile_backend_swap_resize (guint64 size)
-{
-  ThreadParams *params;
-
-  total = size;
-  params = g_slice_new0 (ThreadParams);
-  params->operation = OP_TRUNCATE;
-
-  gegl_tile_backend_swap_push_queue (params);
+  params            = g_slice_new0 (ThreadParams);
+  params->operation = OP_DESTROY;
+  params->length    = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
+  params->entry     = entry;
 
-  GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "pushed resize to %i", (gint)total);
+  /* push the destroy op at the top of the queue, so that it gets served before
+   * any write ops, which are then free to reuse the reclaimed space.
+   */
+  gegl_tile_backend_swap_push_queue (params, /* head = */ TRUE);
 }
 
 static SwapEntry *
@@ -587,8 +647,6 @@ gegl_tile_backend_swap_get_tile (GeglTileSource *self,
 {
   GeglTileBackendSwap *tile_backend_swap;
   SwapEntry           *entry;
-  GeglTile            *tile = NULL;
-  gint                 tile_size;
 
   tile_backend_swap = GEGL_TILE_BACKEND_SWAP (self);
   entry             = gegl_tile_backend_swap_lookup_entry (tile_backend_swap, x, y, z);
@@ -596,13 +654,7 @@ gegl_tile_backend_swap_get_tile (GeglTileSource *self,
   if (!entry)
     return NULL;
 
-  tile_size = gegl_tile_backend_get_tile_size (GEGL_TILE_BACKEND (self));
-  tile      = gegl_tile_new (tile_size);
-  gegl_tile_mark_as_stored (tile);
-
-  gegl_tile_backend_swap_entry_read (tile_backend_swap, entry, gegl_tile_get_data (tile));
-
-  return tile;
+  return gegl_tile_backend_swap_entry_read (tile_backend_swap, entry);
 }
 
 static gpointer
@@ -620,12 +672,9 @@ 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 = gegl_tile_backend_swap_entry_create (x, y, z);
       g_hash_table_insert (tile_backend_swap->index, entry, entry);
     }
 
@@ -655,7 +704,13 @@ gegl_tile_backend_swap_void_tile (GeglTileSource *self,
     {
       GEGL_NOTE (GEGL_DEBUG_TILE_BACKEND, "void tile %i, %i, %i", x, y, z);
 
+      g_hash_table_remove (tile_backend_swap->index, entry);
+
+      g_mutex_lock (&queue_mutex);
+
       gegl_tile_backend_swap_entry_destroy (tile_backend_swap, entry);
+
+      g_mutex_unlock (&queue_mutex);
     }
 
   return NULL;
@@ -769,17 +824,21 @@ gegl_tile_backend_swap_finalize (GObject *object)
 
   if (self->index)
     {
-      GList *tiles = g_hash_table_get_keys (self->index);
-
-      if (tiles != NULL)
+      if (g_hash_table_size (self->index) > 0)
         {
-          GList *iter;
+          GHashTableIter iter;
+          gpointer       key;
+          gpointer       value;
 
-          for (iter = tiles; iter; iter = iter->next)
-            gegl_tile_backend_swap_entry_destroy (self, iter->data);
-        }
+          g_hash_table_iter_init (&iter, self->index);
+
+          g_mutex_lock (&queue_mutex);
 
-      g_list_free (tiles);
+          while (g_hash_table_iter_next (&iter, &key, &value))
+            gegl_tile_backend_swap_entry_destroy (self, value);
+
+          g_mutex_unlock (&queue_mutex);
+        }
 
       g_hash_table_unref (self->index);
 


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