[gegl] Improved locking in GEGL



commit 3cffea77c943527457eec469a6f876dc1e5e005a
Author: �yvind Kolås <pippin gimp org>
Date:   Fri Nov 20 01:37:34 2009 +0000

    Improved locking in GEGL
    
    Resurrected GEGL multi threaded locks with locking at the tile level,
    the performance gained from multi threading is small if detectable,
    through the added overhead and lock contention. There still are
    outstanding paralellisation issues.
    
    There is a per tile mutex, per buffer (in ->tile_backend) mutex and
    a per node mutex. You need to pass --enable-mt to configure/autogen
    to enable the multi threading support.

 bin/gegl.c                            |    1 +
 configure.ac                          |   63 ++++++----
 examples/gegl-paint.c                 |    3 +-
 examples/hello-world.c                |    1 +
 examples/util/gegl-view.c             |    3 +
 gegl/buffer/gegl-buffer-access.c      |  108 ++++++---------
 gegl/buffer/gegl-buffer-iterator.c    |   75 +++++++++--
 gegl/buffer/gegl-buffer-linear.c      |   18 ++-
 gegl/buffer/gegl-buffer-private.h     |   43 ++++---
 gegl/buffer/gegl-buffer.c             |   42 ++++---
 gegl/buffer/gegl-cache.c              |   15 ++
 gegl/buffer/gegl-sampler.c            |    4 -
 gegl/buffer/gegl-tile-backend.c       |    2 +-
 gegl/buffer/gegl-tile-handler-cache.c |   71 +++++++++-
 gegl/buffer/gegl-tile-storage.c       |    2 +
 gegl/buffer/gegl-tile-storage.h       |    1 +
 gegl/buffer/gegl-tile.c               |   30 ++++-
 gegl/gegl-instrument.c                |    2 +
 gegl/graph/gegl-node.c                |  235 +++++++++++++++++++++++++++++----
 gegl/graph/gegl-node.h                |    4 +
 gegl/graph/gegl-visitor.c             |    6 +
 gegl/process/gegl-eval-mgr.c          |    6 +
 gegl/property-types/gegl-path.c       |   14 +-
 operations/affine/affine.c            |   89 +++++++------
 operations/affine/affine.h            |    1 -
 tools/introspect.c                    |    1 +
 26 files changed, 607 insertions(+), 233 deletions(-)
---
diff --git a/bin/gegl.c b/bin/gegl.c
index a5b117d..e8ba51c 100644
--- a/bin/gegl.c
+++ b/bin/gegl.c
@@ -92,6 +92,7 @@ main (gint    argc,
       gegl_enable_fatal_warnings ();
     }
 
+  g_thread_init (NULL);
   gegl_init (&argc, &argv);
 #ifdef HAVE_SPIRO
   gegl_path_spiro_init ();
diff --git a/configure.ac b/configure.ac
index 493c464..38146ba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -185,6 +185,20 @@ else
   AC_MSG_RESULT([no])
 fi
 
+AC_MSG_CHECKING([whether to enable multi threading])
+AC_ARG_ENABLE(mt,
+              [  --enable-mt             turn on multi-threading (default=no)],
+)
+if eval "test x$enable_mt = xyes"; then
+    AC_DEFINE(ENABLE_MT, 1, [Defined to 1 if multi-threading is enabled.])
+    AC_MSG_RESULT([yes])
+    enable_mt="yes"
+else
+    enable_mt="no"
+    AC_MSG_RESULT([no])
+fi
+
+
 changequote(,)dnl
 if eval "test x$GCC = xyes"; then
   case " $CFLAGS " in
@@ -1013,30 +1027,31 @@ AC_MSG_RESULT([
 Building GEGL with prefix=$prefix
 
 Optional features:
-  GEGL docs:      $enable_docs
-  Build workshop: $enable_workshop
-  Build website:  $have_asciidoc
-  SIMD:           sse:$enable_sse mmx:$enable_mmx
+  GEGL docs:       $enable_docs
+  Build workshop:  $enable_workshop
+  Build website:   $have_asciidoc
+  SIMD:            sse:$enable_sse mmx:$enable_mmx
+  Multi threading: $enable_mt
 
 Optional dependencies:
-  asciidoc:       $have_asciidoc
-  enscript:       $have_enscript
-  GIO:            $have_gio
-  GTK+:           $have_gtk
-  Ruby:           $have_ruby
-  Lua:            $have_lua
-  Cairo:          $have_cairo
-  Pango:          $have_pango
-  pangocairo:     $have_pangocairo
-  GDKPixbuf:      $have_gdk_pixbuf
-  JPEG:           $jpeg_ok
-  PNG:            $have_libpng
-  OpenEXR:        $have_openexr
-  rsvg:           $have_librsvg
-  SDL:            $have_sdl
-  openraw:        $have_libopenraw
-  graphviz:       $have_graphviz
-  avformat:       $have_libavformat
-  V4L:            $have_v4l
-  spiro:          $spiro_ok
+  asciidoc:        $have_asciidoc
+  enscript:        $have_enscript
+  GIO:             $have_gio
+  GTK+:            $have_gtk
+  Ruby:            $have_ruby
+  Lua:             $have_lua
+  Cairo:           $have_cairo
+  Pango:           $have_pango
+  pangocairo:      $have_pangocairo
+  GDKPixbuf:       $have_gdk_pixbuf
+  JPEG:            $jpeg_ok
+  PNG:             $have_libpng
+  OpenEXR:         $have_openexr
+  rsvg:            $have_librsvg
+  SDL:             $have_sdl
+  openraw:         $have_libopenraw
+  graphviz:        $have_graphviz
+  avformat:        $have_libavformat
+  V4L:             $have_v4l
+  spiro:           $spiro_ok
 ]);
diff --git a/examples/gegl-paint.c b/examples/gegl-paint.c
index d6741d5..c78850c 100644
--- a/examples/gegl-paint.c
+++ b/examples/gegl-paint.c
@@ -111,7 +111,7 @@ static gboolean paint_release (GtkWidget      *widget,
       processor = gegl_node_new_processor (writebuf, &roi);
       while (gegl_processor_work (processor, NULL)) ;
 
-      g_object_unref (processor);
+      gegl_processor_destroy (processor);
       g_object_unref (writebuf);
 
       gegl_node_link_many (top, out, NULL);
@@ -132,6 +132,7 @@ main (gint    argc,
       gchar **argv)
 {
 
+  g_thread_init (NULL);
   gtk_init (&argc, &argv);
   gegl_init (&argc, &argv);
 
diff --git a/examples/hello-world.c b/examples/hello-world.c
index 6caaeb2..97358e3 100644
--- a/examples/hello-world.c
+++ b/examples/hello-world.c
@@ -7,6 +7,7 @@ gint
 main (gint    argc,
       gchar **argv)
 {
+  g_thread_init (NULL);
   gegl_init (&argc, &argv);  /* initialize the GEGL library */
 
   {
diff --git a/examples/util/gegl-view.c b/examples/util/gegl-view.c
index b965033..b60b7cb 100644
--- a/examples/util/gegl-view.c
+++ b/examples/util/gegl-view.c
@@ -195,6 +195,9 @@ finalize (GObject *gobject)
   if (priv->node)
     g_object_unref (priv->node);
 
+  if (priv->processor)
+    g_object_unref (priv->processor);
+
   G_OBJECT_CLASS (gegl_view_parent_class)->finalize (gobject);
 }
 
diff --git a/gegl/buffer/gegl-buffer-access.c b/gegl/buffer/gegl-buffer-access.c
index c36520a..a91a1f1 100644
--- a/gegl/buffer/gegl-buffer-access.c
+++ b/gegl/buffer/gegl-buffer-access.c
@@ -43,11 +43,6 @@
 #include "gegl-tile-backend.h"
 #include "gegl-buffer-iterator.h"
 
-#if ENABLE_MP
-GStaticRecMutex mutex = G_STATIC_REC_MUTEX_INIT;
-#endif
-
-
 #if 0
 static inline void
 gegl_buffer_pixel_set (GeglBuffer *buffer,
@@ -584,19 +579,12 @@ gegl_buffer_iterate (GeglBuffer          *buffer,
 }
 
 void
-gegl_buffer_set (GeglBuffer          *buffer,
-                 const GeglRectangle *rect,
-                 const Babl          *format,
-                 void                *src,
-                 gint                 rowstride)
+gegl_buffer_set_unlocked (GeglBuffer          *buffer,
+                          const GeglRectangle *rect,
+                          const Babl          *format,
+                          void                *src,
+                          gint                 rowstride)
 {
-  g_return_if_fail (GEGL_IS_BUFFER (buffer));
-
-#if ENABLE_MP
-  g_static_rec_mutex_lock (&mutex);
-#endif
-  gegl_buffer_lock (buffer);
-
   if (format == NULL)
     format = buffer->format;
 
@@ -613,12 +601,23 @@ gegl_buffer_set (GeglBuffer          *buffer,
     {
       gegl_buffer_flush (buffer);
     }
-  gegl_buffer_unlock (buffer); /* XXX: should this happen before flush? */
-#if ENABLE_MP
-  g_static_rec_mutex_unlock (&mutex);
-#endif
 }
 
+void
+gegl_buffer_set (GeglBuffer          *buffer,
+                 const GeglRectangle *rect,
+                 const Babl          *format,
+                 void                *src,
+                 gint                 rowstride)
+{
+  g_return_if_fail (GEGL_IS_BUFFER (buffer));
+
+  gegl_buffer_lock (buffer);
+  gegl_buffer_set_unlocked (buffer, rect, format, src, rowstride);
+  gegl_buffer_unlock (buffer);
+}
+
+
 
 
 #if 0
@@ -935,18 +934,15 @@ resample_boxfilter_u8 (void   *dest_buf,
     }
 }
 
+
 void
-gegl_buffer_get (GeglBuffer          *buffer,
-                 gdouble              scale,
-                 const GeglRectangle *rect,
-                 const Babl          *format,
-                 gpointer             dest_buf,
-                 gint                 rowstride)
+gegl_buffer_get_unlocked (GeglBuffer          *buffer,
+                          gdouble              scale,
+                          const GeglRectangle *rect,
+                          const Babl          *format,
+                          gpointer             dest_buf,
+                          gint                 rowstride)
 {
-  g_return_if_fail (GEGL_IS_BUFFER (buffer));
-#if ENABLE_MP
-  g_static_rec_mutex_lock (&mutex);
-#endif
 
   if (format == NULL)
     format = buffer->format;
@@ -957,34 +953,22 @@ gegl_buffer_get (GeglBuffer          *buffer,
       rect->height == 1)  /* fast path */
     {
       gegl_buffer_get_pixel (buffer, rect->x, rect->y, format, dest_buf);
-#if ENABLE_MP
-      g_static_rec_mutex_unlock (&mutex);
-#endif
       return;
     }
 
   if (!rect && scale == 1.0)
     {
       gegl_buffer_iterate (buffer, NULL, dest_buf, rowstride, FALSE, format, 0);
-#if ENABLE_MP
-      g_static_rec_mutex_unlock (&mutex);
-#endif
       return;
     }
   if (rect->width == 0 ||
       rect->height == 0)
     {
-#if ENABLE_MP
-      g_static_rec_mutex_unlock (&mutex);
-#endif
       return;
     }
   if (GEGL_FLOAT_EQUAL (scale, 1.0))
     {
       gegl_buffer_iterate (buffer, rect, dest_buf, rowstride, FALSE, format, 0);
-#if ENABLE_MP
-      g_static_rec_mutex_unlock (&mutex);
-#endif
       return;
     }
   else
@@ -1066,9 +1050,20 @@ gegl_buffer_get (GeglBuffer          *buffer,
         }
       g_free (sample_buf);
     }
-#if ENABLE_MP
-  g_static_rec_mutex_unlock (&mutex);
-#endif
+}
+
+void
+gegl_buffer_get (GeglBuffer          *buffer,
+                 gdouble              scale,
+                 const GeglRectangle *rect,
+                 const Babl          *format,
+                 gpointer             dest_buf,
+                 gint                 rowstride)
+{
+  g_return_if_fail (GEGL_IS_BUFFER (buffer));
+  gegl_buffer_lock (buffer);
+  gegl_buffer_get_unlocked (buffer, scale, rect, format, dest_buf, rowstride);
+  gegl_buffer_unlock (buffer);
 }
 
 const GeglRectangle *
@@ -1097,9 +1092,7 @@ gegl_buffer_sample (GeglBuffer       *buffer,
   return;
 #endif
 
-#if ENABLE_MP
-  g_static_rec_mutex_lock (&mutex);
-#endif
+  gegl_buffer_lock (buffer);
 
   desired_type = gegl_sampler_type_from_interpolation (interpolation);
 
@@ -1121,16 +1114,9 @@ gegl_buffer_sample (GeglBuffer       *buffer,
     }
   gegl_sampler_get (buffer->sampler, x, y, dest);
 
-#if ENABLE_MP
-  g_static_rec_mutex_unlock (&mutex);
-#endif
-
-  /* if none found, create a singleton sampler for this buffer,
-   * a function to clean up the samplers set for a buffer should
-   * also be provided */
+  gegl_buffer_unlock (buffer);
 
   /* if (scale < 1.0) do decimation, possibly using pyramid instead */
-
 }
 
 void
@@ -1239,13 +1225,5 @@ gegl_buffer_sampler (GeglBuffer       *buffer,
   g_return_if_fail (GEGL_IS_BUFFER (buffer));
   g_return_if_fail (GEGL_IS_SAMPLER (sampler));
 
-#if ENABLE_MP
-  g_static_rec_mutex_lock (&mutex);
-#endif
-
   gegl_sampler_get (sampler, x, y, dest);
-
-#if ENABLE_MP
-  g_static_rec_mutex_unlock (&mutex);
-#endif
 }
diff --git a/gegl/buffer/gegl-buffer-iterator.c b/gegl/buffer/gegl-buffer-iterator.c
index 3c7d16b..d429e88 100644
--- a/gegl/buffer/gegl-buffer-iterator.c
+++ b/gegl/buffer/gegl-buffer-iterator.c
@@ -32,7 +32,6 @@
 #include "gegl-tile-storage.h"
 #include "gegl-utils.h"
 
-
 typedef struct GeglBufferTileIterator
 {
   GeglBuffer    *buffer;
@@ -265,6 +264,7 @@ gegl_buffer_iterator_add (GeglBufferIterator  *iterator,
     roi = self==0?&(buffer->extent):&(i->rect[0]);
   i->rect[self]=*roi;
 
+  /* XXX: if this buffer creation could be avoided, it would be a speedup */
   i->buffer[self]=gegl_buffer_create_sub_buffer (buffer, roi);
   if (format)
     i->format[self]=format;
@@ -312,9 +312,17 @@ typedef struct BufInfo {
 
 static GArray *buf_pool = NULL;
 
+#if ENABLE_MP
+static GStaticMutex pool_mutex = G_STATIC_MUTEX_INIT;
+#endif
+
 static gpointer iterator_buf_pool_get (gint size)
 {
   gint i;
+#if ENABLE_MP
+  g_static_mutex_lock (&pool_mutex);
+#endif
+
   if (G_UNLIKELY (!buf_pool))
     {
       buf_pool = g_array_new (TRUE, TRUE, sizeof (BufInfo));
@@ -325,7 +333,10 @@ static gpointer iterator_buf_pool_get (gint size)
       if (info->size >= size && info->used == 0)
         {
           info->used ++;
+#if ENABLE_MP
+          g_static_mutex_unlock (&pool_mutex);
           return info->buf;
+#endif
         }
     }
   {
@@ -333,6 +344,9 @@ static gpointer iterator_buf_pool_get (gint size)
     info.size = size;
     info.buf = gegl_malloc (size);
     g_array_append_val (buf_pool, info);
+#if ENABLE_MP
+    g_static_mutex_unlock (&pool_mutex);
+#endif
     return info.buf;
   }
 }
@@ -340,23 +354,29 @@ static gpointer iterator_buf_pool_get (gint size)
 static void iterator_buf_pool_release (gpointer buf)
 {
   gint i;
+#if ENABLE_MP
+  g_static_mutex_lock (&pool_mutex);
+#endif
   for (i=0; i<buf_pool->len; i++)
     {
       BufInfo *info = &g_array_index (buf_pool, BufInfo, i);
       if (info->buf == buf)
         {
           info->used --;
+#if ENABLE_MP
+          g_static_mutex_unlock (&pool_mutex);
+#endif
           return;
         }
     }
   g_assert (0);
+#if ENABLE_MP
+  g_static_mutex_unlock (&pool_mutex);
+#endif
 }
 
 static void ensure_buf (GeglBufferIterators *i, gint no)
 {
-  /* XXX: keeping a small pool of such buffres around for the used formats
-   * would probably improve performance
-   */
   if (i->buf[no]==NULL)
     i->buf[no] = iterator_buf_pool_get (babl_format_get_bytes_per_pixel (i->format[no]) *
                                         i->i[0].max_size);
@@ -367,12 +387,30 @@ gboolean gegl_buffer_iterator_next     (GeglBufferIterator *iterator)
   GeglBufferIterators *i = (gpointer)iterator;
   gboolean result = FALSE;
   gint no;
-  /* first we need to finish off any pending write work */
 
   if (i->buf[0] == (void*)0xdeadbeef)
     g_error ("%s called on finished buffer iterator", G_STRFUNC);
-  if (i->iteration_no > 0)
+  if (i->iteration_no == 0)
     {
+#if ENABLE_MP
+      for (no=0; no<i->iterators;no++)
+        {
+          gint j;
+          gboolean found = FALSE;
+          for (j=0; j<no; j++)
+            if (i->buffer[no]==i->buffer[j])
+              {
+                found = TRUE;
+                break;
+              }
+          if (!found)
+            gegl_buffer_lock (i->buffer[no]);
+        }
+#endif
+    }
+  else
+    {
+      /* complete pending write work */
       for (no=0; no<i->iterators;no++)
         {
           if (i->flags[no] & GEGL_BUFFER_WRITE)
@@ -395,7 +433,7 @@ gboolean gegl_buffer_iterator_next     (GeglBufferIterator *iterator)
 
                   ensure_buf (i, no);
 
-                  gegl_buffer_set (i->buffer[no], &(i->roi[no]), i->format[no], i->buf[no], GEGL_AUTO_ROWSTRIDE);
+                  gegl_buffer_set_unlocked (i->buffer[no], &(i->roi[no]), i->format[no], i->buf[no], GEGL_AUTO_ROWSTRIDE);
                 }
             }
         }
@@ -439,7 +477,7 @@ gboolean gegl_buffer_iterator_next     (GeglBufferIterator *iterator)
 
               if (i->flags[no] & GEGL_BUFFER_READ)
                 {
-                  gegl_buffer_get (i->buffer[no], 1.0, &(i->roi[no]), i->format[no], i->buf[no], GEGL_AUTO_ROWSTRIDE);
+                  gegl_buffer_get_unlocked (i->buffer[no], 1.0, &(i->roi[no]), i->format[no], i->buf[no], GEGL_AUTO_ROWSTRIDE);
                 }
 
               i->data[no]=i->buf[no];
@@ -459,7 +497,7 @@ gboolean gegl_buffer_iterator_next     (GeglBufferIterator *iterator)
 
           if (i->flags[no] & GEGL_BUFFER_READ)
             {
-              gegl_buffer_get (i->buffer[no], 1.0, &(i->roi[no]), i->format[no], i->buf[no], GEGL_AUTO_ROWSTRIDE);
+              gegl_buffer_get_unlocked (i->buffer[no], 1.0, &(i->roi[no]), i->format[no], i->buf[no], GEGL_AUTO_ROWSTRIDE);
             }
           i->data[no]=i->buf[no];
 
@@ -474,6 +512,23 @@ gboolean gegl_buffer_iterator_next     (GeglBufferIterator *iterator)
 
   if (result == FALSE)
     {
+
+#if ENABLE_MP
+      for (no=0; no<i->iterators;no++)
+        {
+          gint j;
+          gboolean found = FALSE;
+          for (j=0; j<no; j++)
+            if (i->buffer[no]==i->buffer[j])
+              {
+                found = TRUE;
+                break;
+              }
+          if (!found)
+            gegl_buffer_unlock (i->buffer[no]);
+        }
+#endif
+
       for (no=0; no<i->iterators;no++)
         {
           if (i->buf[no])
@@ -488,6 +543,8 @@ gboolean gegl_buffer_iterator_next     (GeglBufferIterator *iterator)
       i->buf[0]=(void*)0xdeadbeef;
       g_free (i);
     }
+
+
   return result;
 }
 
diff --git a/gegl/buffer/gegl-buffer-linear.c b/gegl/buffer/gegl-buffer-linear.c
index d50453e..f7c1ded 100644
--- a/gegl/buffer/gegl-buffer-linear.c
+++ b/gegl/buffer/gegl-buffer-linear.c
@@ -132,6 +132,10 @@ gegl_buffer_linear_open (GeglBuffer          *buffer,
   if (extent == NULL)
     extent=&buffer->extent;
 
+  /*gegl_buffer_lock (buffer);*/
+#if ENABLE_MP
+  g_mutex_lock (buffer->tile_storage->mutex);
+#endif
   if (extent->x     == buffer->extent.x &&
       extent->y     == buffer->extent.y &&
       extent->width == buffer->tile_width &&
@@ -151,7 +155,6 @@ gegl_buffer_linear_open (GeglBuffer          *buffer,
       tile = gegl_tile_source_get_tile ((GeglTileSource*) (buffer),
                                         0,0,0);
       g_assert (tile);
-      gegl_buffer_lock (buffer);
       gegl_tile_lock (tile);
 
       g_object_set_data (G_OBJECT (buffer), "linear-tile", tile);
@@ -179,6 +182,7 @@ gegl_buffer_linear_open (GeglBuffer          *buffer,
               )
             {
               info->refs++;
+              g_print ("!!!!!! sharing a linear buffer!!!!!\n");
               return info->buf;
             }
         }
@@ -199,7 +203,7 @@ gegl_buffer_linear_open (GeglBuffer          *buffer,
     if(rowstride)*rowstride = rs;
 
     info->buf = gegl_malloc (rs * info->extent.height);
-    gegl_buffer_get (buffer, 1.0, &info->extent, format, info->buf, rs);
+    gegl_buffer_get_unlocked (buffer, 1.0, &info->extent, format, info->buf, rs);
     return info->buf;
   }
   return NULL;
@@ -214,7 +218,6 @@ gegl_buffer_linear_close (GeglBuffer *buffer,
   if (tile)
     {
       gegl_tile_unlock (tile);
-      gegl_buffer_unlock (buffer);
       g_object_set_data (G_OBJECT (buffer), "linear-tile", NULL);
       tile = NULL;
     }
@@ -233,11 +236,14 @@ gegl_buffer_linear_close (GeglBuffer *buffer,
               info->refs--;
 
               if (info->refs>0)
+                {
+                  g_print ("EEeeek! %s\n", G_STRLOC);
                 return; /* there are still others holding a reference to
                          * this linear buffer
                          */
+                }
 
-              gegl_buffer_set (buffer, &info->extent, info->format, info->buf, 0);
+              gegl_buffer_set_unlocked (buffer, &info->extent, info->format, info->buf, 0);
               break;
             }
           else
@@ -255,5 +261,9 @@ gegl_buffer_linear_close (GeglBuffer *buffer,
 
       g_object_set_data (G_OBJECT (buffer), "linear-buffers", linear_buffers);
     }
+  /*gegl_buffer_unlock (buffer);*/
+#if ENABLE_MP
+  g_mutex_unlock (buffer->tile_storage->mutex);
+#endif
   return;
 }
diff --git a/gegl/buffer/gegl-buffer-private.h b/gegl/buffer/gegl-buffer-private.h
index c6c98ba..1f55193 100644
--- a/gegl/buffer/gegl-buffer-private.h
+++ b/gegl/buffer/gegl-buffer-private.h
@@ -79,35 +79,40 @@ struct _GeglBufferClass
 
 
 
-gint                 gegl_buffer_leaks       (void);
+gint              gegl_buffer_leaks       (void);
 
-void                 gegl_buffer_stats       (void);
+void              gegl_buffer_stats       (void);
 
-void                 gegl_buffer_save        (GeglBuffer          *buffer,
-                                              const gchar         *path,
-                                              const GeglRectangle *roi);
+void              gegl_buffer_save        (GeglBuffer          *buffer,
+                                           const gchar         *path,
+                                           const GeglRectangle *roi);
 
 
-const gchar         *gegl_swap_dir           (void);
+const gchar      *gegl_swap_dir           (void);
 
 
-void                 gegl_tile_cache_init    (void);
+void              gegl_tile_cache_init    (void);
 
-void                 gegl_tile_cache_destroy (void);
+void              gegl_tile_cache_destroy (void);
 
-GeglTileBackend    * gegl_buffer_backend     (GeglBuffer *buffer);
+GeglTileBackend * gegl_buffer_backend     (GeglBuffer *buffer);
 
-gboolean             gegl_buffer_is_shared   (GeglBuffer *buffer);
-
-gboolean             gegl_buffer_try_lock    (GeglBuffer *buffer);
-#if 0
-gboolean             gegl_buffer_lock        (GeglBuffer *buffer);
-gboolean             gegl_buffer_unlock      (GeglBuffer *buffer);
-#else
-#define gegl_buffer_lock(o)  {}
-#define gegl_buffer_unlock(o)  {}
-#endif
+gboolean          gegl_buffer_is_shared   (GeglBuffer *buffer);
 
+gboolean          gegl_buffer_try_lock    (GeglBuffer *buffer);
+gboolean          gegl_buffer_lock        (GeglBuffer *buffer);
+gboolean          gegl_buffer_unlock      (GeglBuffer *buffer);
+void              gegl_buffer_set_unlocked (GeglBuffer          *buffer,
+                                            const GeglRectangle *rect,
+                                            const Babl          *format,
+                                            void                *src,
+                                            gint                 rowstride);
+void              gegl_buffer_get_unlocked (GeglBuffer          *buffer,
+                                            gdouble              scale,
+                                            const GeglRectangle *rect,
+                                            const Babl          *format,
+                                            gpointer             dest_buf,
+                                            gint                 rowstride);
 
 GeglBuffer *
 gegl_buffer_new_ram (const GeglRectangle *extent,
diff --git a/gegl/buffer/gegl-buffer.c b/gegl/buffer/gegl-buffer.c
index 61c467a..dad5669 100644
--- a/gegl/buffer/gegl-buffer.c
+++ b/gegl/buffer/gegl-buffer.c
@@ -71,11 +71,13 @@
 #include "gegl-buffer-index.h"
 #include "gegl-config.h"
 
+//#define GEGL_BUFFER_DEBUG_ALLOCATIONS
+
 /* #define GEGL_BUFFER_DEBUG_ALLOCATIONS to print allocation stack
  * traces for leaked GeglBuffers using GNU C libs backtrace_symbols()
  */
-#ifdef GEGL_BUFFER_DEBUG_ALLOCATIONS
 #include <execinfo.h>
+#ifdef GEGL_BUFFER_DEBUG_ALLOCATIONS
 #endif
 
 
@@ -705,6 +707,7 @@ gegl_buffer_get_tile (GeglTileSource *source,
     {
       GeglBuffer *buffer = GEGL_BUFFER (handler);
 
+      /* XXX: lock buffer? */
       if (x < buffer->min_x)
         buffer->min_x = x;
       if (y < buffer->min_y)
@@ -721,7 +724,12 @@ gegl_buffer_get_tile (GeglTileSource *source,
        * coordinates.
        */
       {
-        tile->tile_storage = buffer->tile_storage;
+        if (!tile->tile_storage)
+          {
+            gegl_tile_lock (tile);
+            tile->tile_storage = buffer->tile_storage;
+            gegl_tile_unlock (tile);
+          }
         tile->x = x;
         tile->y = y;
         tile->z = z;
@@ -857,6 +865,7 @@ gegl_buffer_class_init (GeglBufferClass *class)
 }
 
 #ifdef GEGL_BUFFER_DEBUG_ALLOCATIONS
+#endif
 #define MAX_N_FUNCTIONS 100
 static gchar *
 gegl_buffer_get_alloc_stack (void)
@@ -890,7 +899,12 @@ gegl_buffer_get_alloc_stack (void)
 
   return result;
 }
-#endif
+
+void gegl_bt (void);
+void gegl_bt (void)
+{
+  g_print ("%s\n", gegl_buffer_get_alloc_stack ());
+}
 
 static void
 gegl_buffer_init (GeglBuffer *buffer)
@@ -1180,14 +1194,14 @@ gboolean gegl_buffer_is_shared (GeglBuffer *buffer)
 
 gboolean gegl_buffer_try_lock (GeglBuffer *buffer)
 {
+#if 0
   GeglTileBackend *backend = gegl_buffer_backend (buffer);
-  gboolean ret;
-
   if (buffer->lock_count>0)
     {
       buffer->lock_count++;
       return TRUE;
     }
+  gboolean ret;
   if (gegl_buffer_is_shared(buffer))
     ret =gegl_tile_backend_file_try_lock (GEGL_TILE_BACKEND_FILE (backend));
   else
@@ -1195,27 +1209,21 @@ gboolean gegl_buffer_try_lock (GeglBuffer *buffer)
   if (ret)
     buffer->lock_count++;
   return TRUE;
+#else
+  return g_mutex_trylock (buffer->tile_storage->mutex);
+#endif
 }
 
-#if 0
+#if 1
 gboolean gegl_buffer_lock (GeglBuffer *buffer)
 {
-  while (gegl_buffer_try_lock (buffer)==FALSE)
-    {
-      g_print ("failed to aquire lock blocking ..");
-        g_usleep (100000);
-    }
+  if(0)g_mutex_lock (buffer->tile_storage->mutex);
   return TRUE;
 }
 
 gboolean gegl_buffer_unlock (GeglBuffer *buffer)
 {
-  GeglTileBackend *backend = gegl_buffer_backend (buffer);
-  g_assert (buffer->lock_count>=0);
-  buffer->lock_count--;
-  g_assert (buffer->lock_count>=0);
-  if (buffer->lock_count==0 && gegl_buffer_is_shared (buffer))
-    return gegl_tile_backend_file_unlock (GEGL_TILE_BACKEND_FILE (backend));
+  if(0)g_mutex_unlock (buffer->tile_storage->mutex);
   return TRUE;
 }
 #endif
diff --git a/gegl/buffer/gegl-cache.c b/gegl/buffer/gegl-cache.c
index c50449b..8691051 100644
--- a/gegl/buffer/gegl-cache.c
+++ b/gegl/buffer/gegl-cache.c
@@ -33,6 +33,9 @@
 #include "gegl-cache.h"
 #include "gegl-region.h"
 
+#if ENABLE_MP
+static GStaticRecMutex mutex = G_STATIC_REC_MUTEX_INIT;
+#endif
 
 enum
 {
@@ -367,6 +370,9 @@ void
 gegl_cache_invalidate (GeglCache           *self,
                        const GeglRectangle *roi)
 {
+#if ENABLE_MP
+  g_static_rec_mutex_lock (&mutex);
+#endif
 #if 0
   if (roi)
     {
@@ -399,6 +405,9 @@ gegl_cache_invalidate (GeglCache           *self,
       g_signal_emit (self, gegl_cache_signals[INVALIDATED], 0,
                      &rect, NULL);
     }
+#if ENABLE_MP
+  g_static_rec_mutex_unlock (&mutex);
+#endif
 }
 
 void
@@ -408,6 +417,12 @@ gegl_cache_computed (GeglCache           *self,
   g_return_if_fail (GEGL_IS_CACHE (self));
   g_return_if_fail (rect != NULL);
 
+#if ENABLE_MP
+  g_static_rec_mutex_lock (&mutex);
+#endif
   gegl_region_union_with_rect (self->valid_region, rect);
   g_signal_emit (self, gegl_cache_signals[COMPUTED], 0, rect, NULL);
+#if ENABLE_MP
+  g_static_rec_mutex_unlock (&mutex);
+#endif
 }
diff --git a/gegl/buffer/gegl-sampler.c b/gegl/buffer/gegl-sampler.c
index 887582c..8b9872c 100644
--- a/gegl/buffer/gegl-sampler.c
+++ b/gegl/buffer/gegl-sampler.c
@@ -34,10 +34,6 @@
 #include "gegl-sampler-sharp.h"
 #include "gegl-sampler-yafr.h"
 
-#if ENABLE_MP
-GStaticRecMutex mutex = G_STATIC_REC_MUTEX_INIT;
-#endif
-
 enum
 {
   PROP_0,
diff --git a/gegl/buffer/gegl-tile-backend.c b/gegl/buffer/gegl-tile-backend.c
index ce5c4a0..2222aae 100644
--- a/gegl/buffer/gegl-tile-backend.c
+++ b/gegl/buffer/gegl-tile-backend.c
@@ -115,13 +115,13 @@ constructor (GType                  type,
   g_assert (backend->tile_width > 0 && backend->tile_height > 0);
   g_assert (backend->format);
 
+
   backend->px_size = babl_format_get_bytes_per_pixel (backend->format);
   backend->tile_size = backend->tile_width * backend->tile_height * backend->px_size;
 
   return object;
 }
 
-
 static void
 gegl_tile_backend_class_init (GeglTileBackendClass *klass)
 {
diff --git a/gegl/buffer/gegl-tile-handler-cache.c b/gegl/buffer/gegl-tile-handler-cache.c
index 268706a..5910f08 100644
--- a/gegl/buffer/gegl-tile-handler-cache.c
+++ b/gegl/buffer/gegl-tile-handler-cache.c
@@ -30,12 +30,17 @@
 #include "gegl-tile-handler-cache.h"
 #include "gegl-debug.h"
 
+#if ENABLE_MP
+static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
+#endif
+
 struct _GeglTileHandlerCache
 {
   GeglTileHandler parent_instance;
   GSList *free_list;
 };
 
+
 G_DEFINE_TYPE (GeglTileHandlerCache, gegl_tile_handler_cache, GEGL_TYPE_TILE_HANDLER)
 
 typedef struct CacheItem
@@ -51,8 +56,10 @@ typedef struct CacheItem
 static GQueue *cache_queue = NULL;
 static GHashTable *cache_ht = NULL;
 static gint    cache_wash_percentage = 20;
+#if 0
 static gint    cache_hits = 0;
 static gint    cache_misses = 0;
+#endif
 
 static gint    cache_total = 0;  /* approximate amount of bytes stored */
 
@@ -157,7 +164,8 @@ static void
 gegl_tile_handler_cache_dispose_buffer_tiles (gpointer itm,
                                               gpointer userdata)
 {
-  CacheItem *item = itm;
+  CacheItem *item;
+  item = itm;
   if (item->handler == userdata)
     {
       GeglTileHandlerCache *cache = userdata;
@@ -173,6 +181,9 @@ dispose (GObject *object)
   CacheItem            *item;
   GSList               *iter;
 
+#ifdef ENABLE_MP
+  g_static_mutex_lock (&mutex);
+#endif
   cache = GEGL_TILE_HANDLER_CACHE (object);
 
   /* only throw out items belonging to this cache instance */
@@ -193,6 +204,9 @@ dispose (GObject *object)
     }
   g_slist_free (cache->free_list);
   cache->free_list = NULL;
+#ifdef ENABLE_MP
+  g_static_mutex_unlock (&mutex);
+#endif
 
   G_OBJECT_CLASS (gegl_tile_handler_cache_parent_class)->dispose (object);
 }
@@ -210,10 +224,14 @@ get_tile (GeglTileSource *tile_store,
   tile = gegl_tile_handler_cache_get_tile (cache, x, y, z);
   if (tile)
     {
+#if 0
       cache_hits++;
+#endif
       return tile;
     }
+#if 0
   cache_misses++;
+#endif
 
   if (source)
     tile = gegl_tile_source_get_tile (source, x, y, z);
@@ -350,19 +368,27 @@ gegl_tile_handler_cache_get_tile (GeglTileHandlerCache *cache,
   CacheItem *result;
   CacheItem pin;
 
-  GList *link;
   pin.x = x;
   pin.y = y;
   pin.z = z;
   pin.handler = cache;
 
+#ifdef ENABLE_MP
+  g_static_mutex_lock (&mutex);
+#endif
   result = g_hash_table_lookup (cache_ht, &pin);
   if (result)
     {
       g_queue_remove (cache_queue, result);
       g_queue_push_head (cache_queue, result);
+#ifdef ENABLE_MP
+      g_static_mutex_unlock (&mutex);
+#endif
       return g_object_ref (result->tile);
     }
+#ifdef ENABLE_MP
+  g_static_mutex_unlock (&mutex);
+#endif
   return NULL;
 }
 
@@ -386,7 +412,12 @@ gegl_tile_handler_cache_has_tile (GeglTileHandlerCache *cache,
 static gboolean
 gegl_tile_handler_cache_trim (GeglTileHandlerCache *cache)
 {
-  CacheItem *last_writable = g_queue_pop_tail (cache_queue);
+  CacheItem *last_writable;
+ 
+#ifdef ENABLE_MP
+  g_static_mutex_lock (&mutex);
+#endif
+  last_writable = g_queue_pop_tail (cache_queue);
 
   if (last_writable != NULL)
     {
@@ -394,8 +425,14 @@ gegl_tile_handler_cache_trim (GeglTileHandlerCache *cache)
       cache_total  -= last_writable->tile->size;
       g_object_unref (last_writable->tile);
       g_slice_free (CacheItem, last_writable);
+#ifdef ENABLE_MP
+      g_static_mutex_unlock (&mutex);
+#endif
       return TRUE;
     }
+#ifdef ENABLE_MP
+      g_static_mutex_unlock (&mutex);
+#endif
 
   return FALSE;
 }
@@ -408,6 +445,9 @@ gegl_tile_handler_cache_invalidate (GeglTileHandlerCache *cache,
 {
   GList *link;
 
+#ifdef ENABLE_MP
+  g_static_mutex_lock (&mutex);
+#endif
   for (link = g_queue_peek_head_link (cache_queue); link; link = link->next)
     {
       CacheItem *item = link->data;
@@ -426,9 +466,15 @@ gegl_tile_handler_cache_invalidate (GeglTileHandlerCache *cache,
           g_hash_table_remove (cache_ht, item);
           g_slice_free (CacheItem, item);
           g_queue_delete_link (cache_queue, link);
+#ifdef ENABLE_MP
+          g_static_mutex_unlock (&mutex);
+#endif
           return;
         }
     }
+#ifdef ENABLE_MP
+  g_static_mutex_unlock (&mutex);
+#endif
 }
 
 
@@ -443,6 +489,9 @@ gegl_tile_handler_cache_void (GeglTileHandlerCache *cache,
   if (!cache_queue)
     return;
 
+#ifdef ENABLE_MP
+  g_static_mutex_lock (&mutex);
+#endif
   for (link = g_queue_peek_head_link (cache_queue); link; link = link->next)
     {
       CacheItem *item = link->data;
@@ -455,14 +504,20 @@ gegl_tile_handler_cache_void (GeglTileHandlerCache *cache,
           item->handler == cache)
         {
           gegl_tile_void (tile);
-          cache_total  -= item->tile->size;
+          cache_total -= item->tile->size;
           g_object_unref (tile);
           g_hash_table_remove (cache_ht, item);
           g_slice_free (CacheItem, item);
           g_queue_delete_link (cache_queue, link);
+#ifdef ENABLE_MP
+          g_static_mutex_unlock (&mutex);
+#endif
           return;
         }
     }
+#ifdef ENABLE_MP
+  g_static_mutex_unlock (&mutex);
+#endif
 }
 
 void
@@ -480,8 +535,11 @@ gegl_tile_handler_cache_insert (GeglTileHandlerCache *cache,
   item->x       = x;
   item->y       = y;
   item->z       = z;
-  cache_total  += item->tile->size;
 
+#if ENABLE_MP
+  g_static_mutex_lock (&mutex);
+#endif
+  cache_total  += item->tile->size;
   g_queue_push_head (cache_queue, item);
 
   count = g_queue_get_length (cache_queue);
@@ -493,4 +551,7 @@ gegl_tile_handler_cache_insert (GeglTileHandlerCache *cache,
       GEGL_NOTE(GEGL_DEBUG_CACHE, "%f%% hit:%i miss:%i  %i]", cache_hits*100.0/(cache_hits+cache_misses), cache_hits, cache_misses, g_queue_get_length (cache_queue));*/
       gegl_tile_handler_cache_trim (cache);
     }
+#if ENABLE_MP
+  g_static_mutex_unlock (&mutex);
+#endif
 }
diff --git a/gegl/buffer/gegl-tile-storage.c b/gegl/buffer/gegl-tile-storage.c
index a4d9848..badb283 100644
--- a/gegl/buffer/gegl-tile-storage.c
+++ b/gegl/buffer/gegl-tile-storage.c
@@ -273,6 +273,7 @@ gegl_tile_storage_constructor (GType                  type,
                                               tile_storage,
                                               NULL);
   tile_storage->seen_zoom = FALSE;
+  tile_storage->mutex = g_mutex_new ();
 
   return object;
 }
@@ -287,6 +288,7 @@ gegl_tile_storage_finalize (GObject *object)
 
   if (self->path)
     g_free (self->path);
+  g_mutex_free (self->mutex);
 
   (*G_OBJECT_CLASS (parent_class)->finalize)(object);
 }
diff --git a/gegl/buffer/gegl-tile-storage.h b/gegl/buffer/gegl-tile-storage.h
index 7af3fe2..f20fa86 100644
--- a/gegl/buffer/gegl-tile-storage.h
+++ b/gegl/buffer/gegl-tile-storage.h
@@ -32,6 +32,7 @@ struct _GeglTileStorage
 {
   GeglTileHandlerChain parent_instance;
 
+  GMutex      *mutex;
   Babl        *format;
   gint         tile_width;
   gint         tile_height;
diff --git a/gegl/buffer/gegl-tile.c b/gegl/buffer/gegl-tile.c
index 4e0febc..39b45d7 100644
--- a/gegl/buffer/gegl-tile.c
+++ b/gegl/buffer/gegl-tile.c
@@ -139,6 +139,7 @@ dispose (GObject *object)
         }
     }
 
+//#define ENABLE_MP 1
 #if ENABLE_MP
   if (tile->mutex)
     {
@@ -213,7 +214,19 @@ gegl_tile_dup (GeglTile *src)
   tile->next_shared              = src->next_shared;
   src->next_shared               = tile;
   tile->prev_shared              = src;
+#if ENABLE_MP
+  if (tile->next_shared != src)
+    {
+      g_mutex_lock (tile->next_shared->mutex);
+    }
+#endif
   tile->next_shared->prev_shared = tile;
+#if ENABLE_MP
+  if (tile->next_shared != src)
+    {
+      g_mutex_unlock (tile->next_shared->mutex);
+    }
+#endif
 
   return tile;
 }
@@ -254,22 +267,27 @@ gegl_tile_unclone (GeglTile *tile)
       tile->next_shared              = tile;
     }
 }
-
+#if 0
 static gint total_locks   = 0;
 static gint total_unlocks = 0;
+#endif
+
+void gegl_bt (void);
 
 void
 gegl_tile_lock (GeglTile *tile)
 {
+#if ENABLE_MP
+  g_mutex_lock (tile->mutex);
+#endif
+
   if (tile->lock != 0)
     {
-      g_print ("hm\n");
       g_warning ("strange tile lock count: %i", tile->lock);
+      gegl_bt ();
     }
+#if 0
   total_locks++;
-
-#if ENABLE_MP
-  g_mutex_lock (tile->mutex);
 #endif
 
   tile->lock++;
@@ -309,7 +327,9 @@ gegl_tile_void_pyramid (GeglTile *tile)
 void
 gegl_tile_unlock (GeglTile *tile)
 {
+#if 0
   total_unlocks++;
+#endif
   if (tile->lock == 0)
     {
       g_warning ("unlocked a tile with lock count == 0");
diff --git a/gegl/gegl-instrument.c b/gegl/gegl-instrument.c
index 1292b67..2683274 100644
--- a/gegl/gegl-instrument.c
+++ b/gegl/gegl-instrument.c
@@ -104,6 +104,8 @@ gegl_instrument (const gchar *parent_name,
   Timing *iter;
   Timing *parent;
 
+  return;
+
   if (root == NULL)
     {
       root       = g_slice_new0 (Timing);
diff --git a/gegl/graph/gegl-node.c b/gegl/graph/gegl-node.c
index 6c7eda0..aee71af 100644
--- a/gegl/graph/gegl-node.c
+++ b/gegl/graph/gegl-node.c
@@ -69,7 +69,7 @@ struct _GeglNodePrivate
   GeglNode       *parent;
   gchar          *name;
   GeglProcessor  *processor;
-  GeglEvalMgr    *eval_mgr;
+  GeglEvalMgr    *eval_mgr[16];
   GHashTable     *contexts;
 };
 
@@ -198,6 +198,9 @@ gegl_node_init (GeglNode *self)
   self->operation      = NULL;
   self->is_graph       = FALSE;
   self->cache          = NULL;
+#if ENABLE_MP
+  self->mutex          = g_mutex_new ();
+#endif
 
 }
 
@@ -230,11 +233,15 @@ gegl_node_dispose (GObject *gobject)
       self->cache = NULL;
     }
 
-  if (self->priv->eval_mgr)
-    {
-      g_object_unref (self->priv->eval_mgr);
-      self->priv->eval_mgr = NULL;
-    }
+  {
+    gint i;
+    for (i=0; i<4; i++)
+      if (self->priv->eval_mgr[i])
+        {
+          g_object_unref (self->priv->eval_mgr[i]);
+          self->priv->eval_mgr[i] = NULL;
+        }
+  }
 
   if (self->priv->processor)
     {
@@ -273,6 +280,9 @@ gegl_node_finalize (GObject *gobject)
       g_free (self->priv->name);
     }
   g_hash_table_destroy (self->priv->contexts);
+#if ENABLE_MP
+  g_mutex_free (self->mutex);
+#endif
 
   G_OBJECT_CLASS (gegl_node_parent_class)->finalize (gobject);
 }
@@ -811,10 +821,11 @@ gegl_node_link_many (GeglNode *source,
 }
 
 static void gegl_node_ensure_eval_mgr (GeglNode    *self,
-                                       const gchar *pad)
+                                       const gchar *pad,
+                                       gint         no)
 {
-  if (!self->priv->eval_mgr)
-    self->priv->eval_mgr = gegl_eval_mgr_new (self, pad);
+  if (!self->priv->eval_mgr[no])
+    self->priv->eval_mgr[no] = gegl_eval_mgr_new (self, pad);
 }
 
 /* Will set the eval_mgr's roi to the supplied roi if defined, otherwise
@@ -824,24 +835,78 @@ static void gegl_node_ensure_eval_mgr (GeglNode    *self,
 static GeglBuffer *
 gegl_node_apply_roi (GeglNode            *self,
                      const gchar         *output_pad_name,
-                     const GeglRectangle *roi)
+                     const GeglRectangle *roi,
+                     gint                 tid)
 {
+  /* This is a potential spot to multiplex paralell processing,
+   * doing so, might cause a lot of tile overlap between
+   * processes if were not careful (wouldn't neccesarily be totally
+   * bad if that happens though.
+   */
   GeglBuffer *buffer;
 
-  gegl_node_ensure_eval_mgr (self, output_pad_name);
+  //g_print ("%i %i %i %i %i\n", tid, roi->x, roi->y, roi->width, roi->height);
 
   if (roi)
     {
-      self->priv->eval_mgr->roi = *roi;
+      self->priv->eval_mgr[tid]->roi = *roi;
     }
   else
     {
-      self->priv->eval_mgr->roi = gegl_node_get_bounding_box (self);
+      self->priv->eval_mgr[tid]->roi = gegl_node_get_bounding_box (self);
     }
-  buffer = gegl_eval_mgr_apply (self->priv->eval_mgr);
+  buffer = gegl_eval_mgr_apply (self->priv->eval_mgr[tid]);
   return buffer;
 }
 
+
+#if ENABLE_MP
+typedef struct ThreadData
+{
+  GeglNode      *node;
+  gint           tid;
+  GeglRectangle  roi;
+  const gchar   *pad;
+
+  const Babl          *format;
+  gpointer             destination_buf;
+  gint                 rowstride;
+  GeglBlitFlags        flags;
+} ThreadData;
+
+static GThreadPool *pool = NULL;
+static GMutex *mutex = NULL;
+static GCond  *cond = NULL;
+static gint    remaining_tasks = 0;
+
+static void spawnrender (gpointer data,
+                         gpointer foo)
+{
+  ThreadData *td = data;
+  GeglBuffer * buffer;
+  buffer = gegl_node_apply_roi (td->node, td->pad, &td->roi, td->tid);
+
+  if ((buffer ) && td->destination_buf)
+    {
+      gegl_buffer_get (buffer, 1.0, &td->roi, td->format, td->destination_buf, td->rowstride);
+    }
+
+  /* and unrefing to ultimately clean it off from the graph */
+  if (buffer)
+    g_object_unref (buffer);
+
+  g_mutex_lock (mutex);
+  remaining_tasks --;
+  if (remaining_tasks == 0)
+    {
+      /* we were the last task, we're not busy rendering any more */
+      g_cond_signal (cond);
+    }
+  g_mutex_unlock (mutex);
+}
+#endif
+
+
 void
 gegl_node_blit (GeglNode            *self,
                 gdouble              scale,
@@ -851,24 +916,112 @@ gegl_node_blit (GeglNode            *self,
                 gint                 rowstride,
                 GeglBlitFlags        flags)
 {
+  gint threads;
   g_return_if_fail (GEGL_IS_NODE (self));
   g_return_if_fail (roi != NULL);
 
+  threads = 2; /* tunable here for now, should be picked up through GeglConfig */
+#if ENABLE_MP
+  if (pool == NULL)
+    {
+      pool = g_thread_pool_new (spawnrender, NULL, threads, TRUE, NULL);
+      mutex = g_mutex_new ();
+      cond = g_cond_new ();
+    }
+
 #if 0
   if (flags == GEGL_BLIT_DEFAULT)
     flags = GEGL_BLIT_CACHE;
 #endif
 
-  /* temporarily made blit use caching, but render
-   * blocking, this to be able to have less coupling
-   * with the processor
-   */
-#if 1
+  flags = GEGL_BLIT_DEFAULT; /* force all rendering through this path,
+                              * to have less code to worry about making
+                              * multi thread safe
+                              */
   if (flags == GEGL_BLIT_DEFAULT)
     {
+      ThreadData data[16];
+      gint i;
+
+      /* Subdivide along the largest of width/height, this should be further
+       * extended similar to the subdivizion done in GeglProcessor, to get as
+       * square as possible subregions.
+       */
+      gboolean horizontal = roi->width > roi->height;
+
+      gint rowskip = 0;
+
+      if (!format)
+        format = babl_format ("RGBA float"); /* XXX: This probably duplicates
+                                                another hardcoded format, they
+                                                should be turned into a
+                                                constant. */
+      if (horizontal)
+        rowskip = (roi->width/threads) * babl_format_get_bytes_per_pixel (format);
+
+      if (rowstride == GEGL_AUTO_ROWSTRIDE)
+        rowstride = roi->width * babl_format_get_bytes_per_pixel (format);
+
+      data[0].node = self;
+      data[0].pad = "output";
+      data[0].format = format;
+      data[0].destination_buf = destination_buf;
+      data[0].rowstride = rowstride;
+      data[0].flags = flags;
+
+      for (i=0;i<threads;i++)
+        {
+          data[i] = data[0];
+          data[i].roi = *roi;
+          gegl_node_ensure_eval_mgr (self, "output", i);
+          if (horizontal)
+            {
+              data[i].roi.width = roi->width / threads;
+              data[i].roi.x = roi->x + roi->width/threads * i;
+            }
+          else
+            {
+              data[i].roi.height = roi->height / threads;
+              data[i].roi.y = roi->y + roi->height/threads * i;
+            }
+
+          data[i].tid = i;
+          if (horizontal)
+            data[i].destination_buf = ((gchar*)destination_buf + rowskip * i);
+          else
+            data[i].destination_buf = ((gchar*)destination_buf + rowstride * (roi->height/threads) * i);
+        }
+      if (horizontal)
+        data[threads-1].roi.width = roi->width - (roi->width / threads)*(threads-1);
+      else
+        data[threads-1].roi.height = roi->height - (roi->height / threads)*(threads-1);
+
+      remaining_tasks+=threads;
+
+      if (threads==1)
+        {
+          for (i=0; i<threads; i++)
+              spawnrender (&data[i], NULL);
+        }
+      else
+        {
+          for (i=0; i<threads-1; i++)
+            g_thread_pool_push (pool, &data[i], NULL);
+          spawnrender (&data[threads-1], NULL);
+
+          g_mutex_lock (mutex);
+          while (remaining_tasks!=0)
+            g_cond_wait (cond, mutex);
+          g_mutex_unlock (mutex);
+        }
+    }
+#else
+    if (flags == GEGL_BLIT_DEFAULT)
+    {
       GeglBuffer *buffer;
 
-      buffer = gegl_node_apply_roi (self, "output", roi);
+      gegl_node_ensure_eval_mgr (self, "output", 0);
+      buffer = gegl_node_apply_roi (self, "output", roi, 0);
       if (buffer && destination_buf)
         {
           if (destination_buf)
@@ -886,9 +1039,8 @@ gegl_node_blit (GeglNode            *self,
       if (buffer)
         g_object_unref (buffer);
     }
-  else 
 #endif
-    
+  else  /* these code paths currently not used */
     if ((flags & GEGL_BLIT_CACHE) ||
            (flags & GEGL_BLIT_DIRTY))
     {
@@ -908,7 +1060,6 @@ gegl_node_blit (GeglNode            *self,
     }
 }
 
-
 GSList *
 gegl_node_get_depends_on (GeglNode *self)
 {
@@ -1599,7 +1750,7 @@ gegl_node_process (GeglNode *self)
 
   input   = gegl_node_get_producer (self, "input", NULL);
   defined = gegl_node_get_bounding_box (input);
-  buffer  = gegl_node_apply_roi (input, "output", &defined);
+  buffer  = gegl_node_apply_roi (input, "output", &defined, 3);
 
   g_assert (GEGL_IS_BUFFER (buffer));
   context = gegl_node_add_context (self, &defined);
@@ -1626,12 +1777,17 @@ gegl_node_get_context (GeglNode *self,
                        gpointer  context_id)
 {
   GeglOperationContext *context = NULL;
+#if ENABLE_MP
+  g_mutex_lock (self->mutex);
+#endif
 
   g_return_val_if_fail (GEGL_IS_NODE (self), NULL);
   g_return_val_if_fail (context_id != NULL, NULL);
 
   context = g_hash_table_lookup (self->priv->contexts, context_id);
-
+#if ENABLE_MP
+  g_mutex_unlock (self->mutex);
+#endif
   return context;
 }
 
@@ -1645,14 +1801,23 @@ gegl_node_remove_context (GeglNode *self,
   g_return_if_fail (context_id != NULL);
 
   context = gegl_node_get_context (self, context_id);
+#if ENABLE_MP
+  g_mutex_lock (self->mutex);
+#endif
   if (!context)
     {
       g_warning ("didn't find context %p for %s",
                  context_id, gegl_node_get_debug_name (self));
+#if ENABLE_MP
+      g_mutex_unlock (self->mutex);
+#endif
       return;
     }
   g_hash_table_remove (self->priv->contexts, context_id);
   gegl_operation_context_destroy (context);
+#if ENABLE_MP
+  g_mutex_unlock (self->mutex);
+#endif
 }
 
 /* Creates, sets up and returns a new context for the node, or just returns it
@@ -1667,18 +1832,27 @@ gegl_node_add_context (GeglNode *self,
   g_return_val_if_fail (GEGL_IS_NODE (self), NULL);
   g_return_val_if_fail (context_id != NULL, NULL);
 
+#if ENABLE_MP
+  g_mutex_lock (self->mutex);
+#endif
   context = g_hash_table_lookup (self->priv->contexts, context_id);
 
   if (context)
     {
       /* silently ignore, since multiple traversals of prepare are done
        * to saturate the graph */
+#if ENABLE_MP
+      g_mutex_unlock (self->mutex);
+#endif
       return context;
     }
 
   context             = gegl_operation_context_new ();
   context->operation  = self->operation;
   g_hash_table_insert (self->priv->contexts, context_id, context);
+#if ENABLE_MP
+  g_mutex_unlock (self->mutex);
+#endif
   return context;
 }
 
@@ -1825,6 +1999,9 @@ gegl_node_get_cache (GeglNode *node)
       GeglPad    *pad;
       const Babl *format;
 
+      /* XXX: it should be possible to have cache for other pads than
+       * only "output" pads
+       */
       pad = gegl_node_get_pad (node, "output");
       g_assert (pad);
       format = gegl_pad_get_format (pad);
@@ -1872,7 +2049,7 @@ gegl_node_remove_children (GeglNode *self)
     {
       GeglNode *child = gegl_node_get_nth_child (self, 0);
 
-      if (child)
+      if (child && GEGL_IS_NODE (child))
         gegl_node_remove_child (self, child);
       else
         break;
@@ -1901,11 +2078,14 @@ gegl_node_remove_child (GeglNode *self,
                         GeglNode *child)
 {
   g_return_val_if_fail (GEGL_IS_NODE (self), NULL);
+  if (!GEGL_IS_NODE (child))
+    {
+      g_print ("%p %s\n", child, G_OBJECT_TYPE_NAME (child));
+    }
   g_return_val_if_fail (GEGL_IS_NODE (child), NULL);
 
   g_assert (child->priv->parent == self ||
             child->priv->parent == NULL);
-  g_return_val_if_fail (GEGL_IS_NODE (child), NULL);
 
   self->priv->children = g_slist_remove (self->priv->children, child);
 
@@ -1917,7 +2097,6 @@ gegl_node_remove_child (GeglNode *self,
       g_object_unref (child);
     }
 
-
   if (self->priv->children == NULL)
     self->is_graph = FALSE;
 
diff --git a/gegl/graph/gegl-node.h b/gegl/graph/gegl-node.h
index bc45801..97c810b 100644
--- a/gegl/graph/gegl-node.h
+++ b/gegl/graph/gegl-node.h
@@ -75,6 +75,10 @@ struct _GeglNode
   /* Whether result is cached or not, inherited by children */
   gboolean        dont_cache;
 
+#if ENABLE_MP
+  GMutex         *mutex;
+#endif
+
   /*< private >*/
   GeglNodePrivate *priv;
 };
diff --git a/gegl/graph/gegl-visitor.c b/gegl/graph/gegl-visitor.c
index 44fb96d..9e109a3 100644
--- a/gegl/graph/gegl-visitor.c
+++ b/gegl/graph/gegl-visitor.c
@@ -537,5 +537,11 @@ static void
 visit_node (GeglVisitor *self,
             GeglNode    *node)
 {
+#if ENABLE_MP
+  g_mutex_lock (node->mutex);
+#endif
   self->visits_list = g_slist_prepend (self->visits_list, node);
+#if ENABLE_MP
+  g_mutex_unlock (node->mutex);
+#endif
 }
diff --git a/gegl/process/gegl-eval-mgr.c b/gegl/process/gegl-eval-mgr.c
index a575db0..b8a4ab7 100644
--- a/gegl/process/gegl-eval-mgr.c
+++ b/gegl/process/gegl-eval-mgr.c
@@ -179,6 +179,12 @@ gegl_eval_mgr_apply (GeglEvalMgr *self)
 
   /* set up the context's rectangle (breadth first traversal) */
   gegl_visitor_reset (self->need_visitor);
+
+  /* should the need rect be moved into the context, making this
+   * part of gegl re-entrable without locking?.. or does that
+   * hamper other useful API that depends on the need_rect to be
+   * in the nodes?
+   */
   gegl_visitor_bfs_traverse (self->need_visitor, GEGL_VISITABLE (root));
 
 #if 0
diff --git a/gegl/property-types/gegl-path.c b/gegl/property-types/gegl-path.c
index 9a71312..3896f38 100644
--- a/gegl/property-types/gegl-path.c
+++ b/gegl/property-types/gegl-path.c
@@ -185,15 +185,17 @@ gegl_path_list_append_item  (GeglPathList  *head,
 
   if (iter)
     {
+      /* the +3 is padding, +1 was excpected to be sufficient */
       iter->next = 
-        g_slice_alloc0 (sizeof (gpointer) + sizeof (gchar) + sizeof (gfloat)*2 *(info->n_items+1)/2);
+        g_slice_alloc0 (sizeof (gpointer) + sizeof (gchar) + sizeof (gfloat)*2 *(info->n_items+3)/2);
       iter->next->d.type = type;
       iter = iter->next;
     }
   else /* creating new path */
     {
+      /* the +3 is padding, +1 was excpected to be sufficient */
       head = 
-        g_slice_alloc0 (sizeof (gpointer) + sizeof (gchar) + sizeof (gfloat)*2 *(info->n_items+1)/2);
+        g_slice_alloc0 (sizeof (gpointer) + sizeof (gchar) + sizeof (gfloat)*2 *(info->n_items+3)/2);
       head->d.type = type;
       iter=head;
     }
@@ -1993,7 +1995,7 @@ static void gegl_path_stamp (GeglBuffer *buffer,
     }
   g_assert (s.buf);
 
-  gegl_buffer_get (buffer, 1.0, &roi, s.format, s.buf, 0);
+  gegl_buffer_get_unlocked (buffer, 1.0, &roi, s.format, s.buf, 0);
 
   {
     gint u, v;
@@ -2031,7 +2033,7 @@ static void gegl_path_stamp (GeglBuffer *buffer,
         }
     }
   }
-  gegl_buffer_set (buffer, &roi, s.format, s.buf, 0);
+  gegl_buffer_set_unlocked (buffer, &roi, s.format, s.buf, 0);
 }
 
 
@@ -2083,8 +2085,7 @@ void gegl_path_stroke (GeglBuffer *buffer,
    {
      return;
    }
-  if (gegl_buffer_is_shared (buffer))
-    while (!gegl_buffer_try_lock (buffer));
+  gegl_buffer_lock (buffer);
 
   /*gegl_buffer_clear (buffer, &extent);*/
 
@@ -2163,7 +2164,6 @@ void gegl_path_stroke (GeglBuffer *buffer,
       iter=iter->next;
     }
 
-  if (gegl_buffer_is_shared (buffer))
   gegl_buffer_unlock (buffer);
 }
 
diff --git a/operations/affine/affine.c b/operations/affine/affine.c
index 600a2e8..d65960d 100644
--- a/operations/affine/affine.c
+++ b/operations/affine/affine.c
@@ -132,8 +132,8 @@ GType
 gegl_sampler_type_from_interpolation (GeglInterpolation interpolation);
 
 /* ************************* */
-static void
-op_affine_sampler_init (OpAffine *self)
+static GeglSampler *
+op_affine_sampler (OpAffine *self)
 {
   Babl                 *format;
   GeglSampler          *sampler;
@@ -145,6 +145,33 @@ op_affine_sampler_init (OpAffine *self)
   interpolation = gegl_buffer_interpolation_from_string (self->filter);
   desired_type = gegl_sampler_type_from_interpolation (interpolation);
 
+  if (interpolation == GEGL_INTERPOLATION_LANCZOS)
+    {
+      sampler = g_object_new (desired_type,
+                              "format", format,
+                              "lanczos_width",  self->lanczos_width,
+                              NULL);
+    }
+  else
+    {
+      sampler = g_object_new (desired_type,
+                              "format", format,
+                              NULL);
+    }
+  return sampler;
+}
+/* XXX: keep a pool of samplers */
+
+#if 0
+static void
+op_affine_sampler_init (OpAffine *self)
+{
+  GType                 desired_type;
+  GeglInterpolation     interpolation;
+
+  interpolation = gegl_buffer_interpolation_from_string (self->filter);
+  desired_type = gegl_sampler_type_from_interpolation (interpolation);
+
   if (self->sampler != NULL &&
       !G_TYPE_CHECK_INSTANCE_TYPE (self->sampler, desired_type))
     {
@@ -153,48 +180,21 @@ op_affine_sampler_init (OpAffine *self)
       self->sampler = NULL;
     }
 
-  if (self->sampler == NULL)
-    {
-      if (interpolation == GEGL_INTERPOLATION_LANCZOS)
-        {
-          sampler = g_object_new (desired_type,
-                                  "format", format,
-                                  "lanczos_width",  self->lanczos_width,
-                                  NULL);
-        }
-      else
-        {
-          sampler = g_object_new (desired_type,
-                                  "format", format,
-                                  NULL);
-        }
-      self->sampler = g_object_ref(sampler);
-    }
+  self->sampler = op_affine_sampler (self);
 }
+#endif
 
 static void
 gegl_affine_prepare (GeglOperation *operation)
 {
-  OpAffine  *affine = (OpAffine *) operation;
   Babl      *format = babl_format ("RaGaBaA float");
-  op_affine_sampler_init (affine);
+  /*op_affine_sampler_init (affine);*/
   /*gegl_operation_set_format (operation, "input", format);
   gegl_operation_set_format (operation, "aux", format); XXX(not used yet) */
   gegl_operation_set_format (operation, "output", format);
 }
 
 static void
-gegl_affine_finalize (GObject *object)
-{
-  OpAffine  *affine = (OpAffine *) object;
-  if (affine->sampler != NULL)
-    {
-      g_object_unref(affine->sampler);
-      affine->sampler = NULL;
-    }
-}
-
-static void
 op_affine_class_init (OpAffineClass *klass)
 {
   GObjectClass             *gobject_class = G_OBJECT_CLASS (klass);
@@ -203,7 +203,6 @@ op_affine_class_init (OpAffineClass *klass)
 
   gobject_class->set_property         = gegl_affine_set_property;
   gobject_class->get_property         = gegl_affine_get_property;
-  gobject_class->finalize             = gegl_affine_finalize;
 
   op_class->get_invalidated_by_change = gegl_affine_get_invalidated_by_change;
   op_class->get_bounding_box          = gegl_affine_get_bounding_box;
@@ -441,9 +440,9 @@ gegl_affine_get_bounding_box (GeglOperation *op)
   GeglRectangle  context_rect;
   GeglSampler   *sampler;
 
-  op_affine_sampler_init (affine);
-  sampler = affine->sampler;
+  sampler = op_affine_sampler (OP_AFFINE (op));
   context_rect = sampler->context_rect;
+  g_object_unref (sampler);
 
 
   if (gegl_operation_source_get_bounding_box (op, "input"))
@@ -542,8 +541,9 @@ gegl_affine_get_required_for_output (GeglOperation       *op,
   gint           i;
 
   requested_rect = *region;
-  sampler = affine->sampler;
+  sampler = op_affine_sampler (OP_AFFINE (op));
   context_rect = sampler->context_rect;
+  g_object_unref (sampler);
 
   gegl_matrix3_copy (inverse, affine->matrix);
   gegl_matrix3_invert (inverse);
@@ -595,9 +595,9 @@ gegl_affine_get_invalidated_by_change (GeglOperation       *op,
   gint               i;
   GeglRectangle      region = *input_region;
 
-  op_affine_sampler_init (affine);
-  sampler = affine->sampler;
+  sampler = op_affine_sampler (OP_AFFINE (op));
   context_rect = sampler->context_rect;
+  g_object_unref (sampler);
   /* invoke child's matrix creation function */
   g_assert (klass->create_matrix);
   gegl_matrix3_identity (affine->matrix);
@@ -764,15 +764,18 @@ gegl_affine_process (GeglOperation        *operation,
   else
     {
       /* for all other cases, do a proper resampling */
+      GeglSampler *sampler;
 
       input  = gegl_operation_context_get_source (context, "input");
       output = gegl_operation_context_get_target (context, "output");
 
-      g_object_set(affine->sampler, "buffer", input, NULL);
-      gegl_sampler_prepare (affine->sampler);
-      affine_generic (output, input, affine->matrix, affine->sampler);
-      g_object_unref(affine->sampler->buffer);
-      affine->sampler->buffer = NULL;
+      sampler = op_affine_sampler (affine);
+      g_object_set(sampler, "buffer", input, NULL);
+      gegl_sampler_prepare (sampler);
+      affine_generic (output, input, affine->matrix, sampler);
+      g_object_unref(sampler->buffer);
+      sampler->buffer = NULL;
+      g_object_unref (sampler);
 
       if (input != NULL)
         g_object_unref (input);
diff --git a/operations/affine/affine.h b/operations/affine/affine.h
index 41c09ad..9a1ee31 100644
--- a/operations/affine/affine.h
+++ b/operations/affine/affine.h
@@ -26,7 +26,6 @@ struct _OpAffine
   gchar       *filter;
   gboolean     hard_edges;
   gint         lanczos_width;
-  GeglSampler *sampler;
 };
 
 typedef struct _OpAffineClass OpAffineClass;
diff --git a/tools/introspect.c b/tools/introspect.c
index ebe54f0..034293a 100644
--- a/tools/introspect.c
+++ b/tools/introspect.c
@@ -449,6 +449,7 @@ gint
 stuff (gint    argc,
       gchar **argv)
 {
+  g_thread_init (NULL);
   gegl_init (&argc, &argv);
   
     {



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