[gegl/wip/Jehan/gegl_node_process_success: 1/4] gegl: new gegl_node_process_success() and gegl_operation_set_error().



commit fe2c7bf60dd74d7fce3f36203e8ba4ca083ab0e7
Author: Jehan <jehan girinstud io>
Date:   Fri Mar 22 19:08:49 2019 +0100

    gegl: new gegl_node_process_success() and gegl_operation_set_error().
    
    gegl_node_process_success() returns TRUE if the last call of
    gegl_node_process(), gegl_node_blit() or gegl_node_blit_buffer() was a
    success.
    This will also return the success from a call to gegl_processor_work() if
    you use the node given to gegl_node_new_processor() as argument.
    
    Additionally in case of process failure, a GError is returned, allowing
    calling applications to process failure appropriately.
    To make usage of this new possibility, GeglOperation subclasses should
    call gegl_operation_set_error(). If they don't a generic error will be
    set instead.
    
    Note that even if subclass operations do not set an error, this is still
    very useful to know that an error occured since currently all we can do
    is assume everything always goes fine.

 gegl/graph/gegl-node-private.h      |  3 +++
 gegl/graph/gegl-node.c              | 21 ++++++++++++++++++++
 gegl/graph/gegl-node.h              | 16 +++++++++++++++
 gegl/operation/gegl-operation.c     | 39 +++++++++++++++++++++++++++++++++----
 gegl/operation/gegl-operation.h     | 18 +++++++++++++++++
 gegl/process/gegl-eval-manager.c    |  4 +++-
 gegl/process/gegl-graph-traversal.c | 35 ++++++++++++++++++++++++---------
 gegl/process/gegl-graph-traversal.h |  3 ++-
 gegl/process/gegl-processor.c       | 14 ++++++-------
 9 files changed, 131 insertions(+), 22 deletions(-)
---
diff --git a/gegl/graph/gegl-node-private.h b/gegl/graph/gegl-node-private.h
index 21d127dd7..2ea3b0c0e 100644
--- a/gegl/graph/gegl-node-private.h
+++ b/gegl/graph/gegl-node-private.h
@@ -91,6 +91,9 @@ struct _GeglNode
 
   gint            passthrough;
 
+  gboolean        success;
+  GError         *error;
+
   /*< private >*/
   GeglNodePrivate *priv;
 };
diff --git a/gegl/graph/gegl-node.c b/gegl/graph/gegl-node.c
index 80152605f..9a962fab3 100644
--- a/gegl/graph/gegl-node.c
+++ b/gegl/graph/gegl-node.c
@@ -239,6 +239,8 @@ gegl_node_init (GeglNode *self)
   self->is_graph         = FALSE;
   self->cache            = NULL;
   self->output_visitable = gegl_node_output_visitable_new (self);
+  self->success          = FALSE;
+  self->error            = NULL;
   g_mutex_init (&self->mutex);
 
 }
@@ -288,6 +290,7 @@ gegl_node_finalize (GObject *gobject)
   g_clear_object (&self->output_visitable);
   g_free (self->priv->name);
   g_free (self->priv->debug_name);
+  g_clear_error (&self->error);
 
   g_mutex_clear (&self->mutex);
 
@@ -1850,6 +1853,24 @@ gegl_node_process (GeglNode *self) /* XXX: add level argument?  */
   g_object_unref (processor);
 }
 
+gboolean
+gegl_node_process_success (GeglNode  *self,
+                           GError   **error)
+{
+  GeglNode *real_node;
+
+  /* If @self is a graph, the error will be set on its output proxy. */
+  if (GEGL_IS_OPERATION (self->operation))
+    real_node = self;
+  else
+    real_node = gegl_node_get_output_proxy (self, "output");
+
+  if (error && ! real_node->success && real_node->error)
+    *error = g_error_copy (real_node->error);
+
+  return real_node->success;
+}
+
 GeglNode *
 gegl_node_detect (GeglNode *root,
                   gint      x,
diff --git a/gegl/graph/gegl-node.h b/gegl/graph/gegl-node.h
index 6eca2cfb9..8d63df977 100644
--- a/gegl/graph/gegl-node.h
+++ b/gegl/graph/gegl-node.h
@@ -342,6 +342,22 @@ void          gegl_node_blit_buffer      (GeglNode            *node,
  */
 void          gegl_node_process          (GeglNode      *sink_node);
 
+/**
+ * gegl_node_process_success:
+ * @node: a #GeglNode.
+ * @error: (out): an optional error string.
+ *
+ * Returns: TRUE is the last call to gegl_node_process(), gegl_node_blit() or
+ * gegl_node_blit_buffer() on @node was a success, FALSE otherwise.
+ *
+ * This will also return the success from a call to gegl_processor_work() if
+ * you use the node given to gegl_node_new_processor().
+ *
+ * In case of failure and if @error was not NULL, a copy of the error string
+ * will be allocated.
+ */
+gboolean      gegl_node_process_success  (GeglNode      *node,
+                                          GError       **error);
 
 /***
  * Reparenting:
diff --git a/gegl/operation/gegl-operation.c b/gegl/operation/gegl-operation.c
index 0b31c146f..4650be2f6 100644
--- a/gegl/operation/gegl-operation.c
+++ b/gegl/operation/gegl-operation.c
@@ -20,6 +20,7 @@
 #include "config.h"
 
 #include <glib-object.h>
+#include <glib/gi18n-lib.h>
 #include <string.h>
 
 #include "gegl.h"
@@ -146,20 +147,32 @@ gegl_operation_process (GeglOperation        *operation,
                         const GeglRectangle  *result,
                         gint                  level)
 {
-  GeglOperationClass  *klass;
+  GeglOperationClass *klass;
+  gboolean            success = TRUE;
 
   g_return_val_if_fail (GEGL_IS_OPERATION (operation), FALSE);
 
   klass = GEGL_OPERATION_GET_CLASS (operation);
 
+  operation->node->success = TRUE;
+  g_clear_error (&operation->node->error);
+
   if (!strcmp (output_pad, "output") &&
       (result->width == 0 || result->height == 0))
     {
       GeglBuffer *output = gegl_buffer_new (NULL, NULL);
-      g_warning ("%s Eeek: processing 0px rectangle", G_STRLOC);
+
+      gegl_operation_set_error (operation,
+                                g_error_new (g_quark_from_static_string ("gegl"),
+                                             0, _("Operation %s failed: %s"),
+                                             gegl_operation_get_name (operation),
+                                             _("processing 0px rectangle.")));
+      operation->node->success = FALSE;
+
       /* when this case is hit.. we've done something bad.. */
       gegl_operation_context_take_object (context, "output", G_OBJECT (output));
-      return TRUE;
+
+      return FALSE;
     }
 
   if (operation->node->passthrough)
@@ -171,7 +184,25 @@ gegl_operation_process (GeglOperation        *operation,
 
   g_return_val_if_fail (klass->process, FALSE);
 
-  return klass->process (operation, context, output_pad, result, level);
+  success = klass->process (operation, context, output_pad, result, level);
+
+  operation->node->success = success;
+  if (! success && operation->node->error == NULL)
+    gegl_operation_set_error (operation,
+                              g_error_new (g_quark_from_static_string ("gegl"),
+                                           0, _("Operation %s failed: %s"),
+                                           gegl_operation_get_name (operation),
+                                           _("unknown error.")));
+
+  return success;
+}
+
+void
+gegl_operation_set_error (GeglOperation *operation,
+                          GError        *error)
+{
+  g_clear_error (&operation->node->error);
+  g_propagate_error (&operation->node->error, error);
 }
 
 /* Calls an extending class' get_bound_box method if defined otherwise
diff --git a/gegl/operation/gegl-operation.h b/gegl/operation/gegl-operation.h
index fa100034c..6107d5016 100644
--- a/gegl/operation/gegl-operation.h
+++ b/gegl/operation/gegl-operation.h
@@ -133,6 +133,9 @@ struct _GeglOperationClass
   /* Compute the rectangular region output roi for the specified output_pad.
    * For operations that are sinks (have no output pads), roi is the rectangle
    * to consume and the output_pad argument is to be ignored.
+   * If the processing may fail and return FALSE, your implementation should
+   * call gegl_operation_set_error() before returning FALSE (if you don't, GEGL
+   * will only be able to report a generic error).
    */
   gboolean      (*process)                   (GeglOperation        *operation,
                                               GeglOperationContext *context,
@@ -194,6 +197,21 @@ gboolean        gegl_operation_process       (GeglOperation *operation,
                                               const gchar          *output_pad,
                                               const GeglRectangle  *roi,
                                               gint                  level);
+/**
+ * gegl_operation_set_error:
+ * @operation: the #GeglOperation.
+ * @error: a #GError.
+ *
+ * Document the error encountered by an operation.
+ * This should typically be used in GeglOperation subclasses when they return
+ * #FALSE in their implementation of process(). You should not set an error
+ * while returning TRUE.
+ * Note that @error is propagated to the caller and therefore no longer valid
+ * after this call. In particular you should not try to free it (if you wish to
+ * reuse the variable, just set it to NULL).
+ */
+void            gegl_operation_set_error     (GeglOperation *operation,
+                                              GError        *error);
 
 /* create a pad for a specified property for this operation, this method is
  * to be called from the attach method of operations, most operations do not
diff --git a/gegl/process/gegl-eval-manager.c b/gegl/process/gegl-eval-manager.c
index 6934ced4e..351b9b26e 100644
--- a/gegl/process/gegl-eval-manager.c
+++ b/gegl/process/gegl-eval-manager.c
@@ -113,6 +113,8 @@ gegl_eval_manager_apply (GeglEvalManager     *self,
   g_return_val_if_fail (GEGL_IS_EVAL_MANAGER (self), NULL);
   g_return_val_if_fail (GEGL_IS_NODE (self->node), NULL);
 
+  g_clear_error (&self->node->error);
+
   if (level >= GEGL_CACHE_VALID_MIPMAPS)
     level = GEGL_CACHE_VALID_MIPMAPS-1;
 
@@ -125,7 +127,7 @@ gegl_eval_manager_apply (GeglEvalManager     *self,
   GEGL_INSTRUMENT_END ("gegl", "prepare-request");
 
   GEGL_INSTRUMENT_START();
-  object = gegl_graph_process (self->traversal, level);
+  object = gegl_graph_process (self->traversal, level, &self->node->error);
   GEGL_INSTRUMENT_END ("gegl", "process");
 
   return object;
diff --git a/gegl/process/gegl-graph-traversal.c b/gegl/process/gegl-graph-traversal.c
index e095c9c66..af5b32dac 100644
--- a/gegl/process/gegl-graph-traversal.c
+++ b/gegl/process/gegl-graph-traversal.c
@@ -396,6 +396,8 @@ gegl_graph_get_shared_empty (GeglGraphTraversal *path)
 /**
  * gegl_graph_process:
  * @path: The traversal path
+ * @error: the #GError into which gegl_operation_process() should propagate any
+ *         error.
  *
  * Process the prepared request. This will return the
  * resulting buffer from the final node, or NULL if
@@ -404,18 +406,21 @@ gegl_graph_get_shared_empty (GeglGraphTraversal *path)
  * If gegl_graph_prepare_request has not been called
  * the behavior of this function is undefined.
  *
- * Return value: (transfer full): The result of the graph, or NULL if
- * there is no output pad.
+ * Return value: (transfer full): The result of the graph, or #NULL either if
+ * there is no output pad or if an error was encountered. In the latter case,
+ * any error will be propagated to @error.
  */
 GeglBuffer *
-gegl_graph_process (GeglGraphTraversal *path,
-                    gint                level)
+gegl_graph_process (GeglGraphTraversal  *path,
+                    gint                 level,
+                    GError             **error)
 {
   GList *list_iter = NULL;
   GeglBuffer *result = NULL;
   GeglOperationContext *context = NULL;
   GeglOperationContext *last_context = NULL;
   GeglBuffer *operation_result = NULL;
+  gboolean    success = TRUE;
 
   for (list_iter = g_queue_peek_head_link (&path->path);
        list_iter;
@@ -467,7 +472,16 @@ gegl_graph_process (GeglGraphTraversal *path,
               /* note: this hard-coding of "output" makes some more custom
                * graph topologies harder than necessary.
                */
-              gegl_operation_process (operation, context, "output", &context->need_rect, context->level);
+              if (! gegl_operation_process (operation, context, "output", &context->need_rect, 
context->level))
+                {
+                  /* Propagate the error to the calling node, because this is
+                   * where we will look when checking for errors.
+                   */
+                  g_propagate_error (error, operation->node->error);
+                  operation->node->error = NULL;
+                  success = FALSE;
+                  break;
+                }
               operation_result = GEGL_BUFFER (gegl_operation_context_get_object (context, "output"));
 
               if (operation_result && operation_result == (GeglBuffer *)operation->node->cache)
@@ -507,10 +521,13 @@ gegl_graph_process (GeglGraphTraversal *path,
     }
   if (last_context)
     {
-      if (operation_result)
-        result = g_object_ref (operation_result);
-      else if (gegl_node_has_pad (last_context->operation->node, "output"))
-        result = g_object_ref (gegl_graph_get_shared_empty (path));
+      if (success)
+        {
+          if (operation_result)
+            result = g_object_ref (operation_result);
+          else if (gegl_node_has_pad (last_context->operation->node, "output"))
+            result = g_object_ref (gegl_graph_get_shared_empty (path));
+        }
       gegl_operation_context_purge (last_context);
     }
 
diff --git a/gegl/process/gegl-graph-traversal.h b/gegl/process/gegl-graph-traversal.h
index 38f3774a0..c7eb658cd 100644
--- a/gegl/process/gegl-graph-traversal.h
+++ b/gegl/process/gegl-graph-traversal.h
@@ -30,7 +30,8 @@ void                gegl_graph_prepare_request  (GeglGraphTraversal  *path,
                                                  const GeglRectangle *roi,
                                                  gint                 level);
 GeglBuffer         *gegl_graph_process          (GeglGraphTraversal  *path,
-                                                 gint                 level);
+                                                 gint                 level,
+                                                 GError             **error);
 
 GeglRectangle       gegl_graph_get_bounding_box (GeglGraphTraversal  *path);
 
diff --git a/gegl/process/gegl-processor.c b/gegl/process/gegl-processor.c
index 15668d758..dcdbe936a 100644
--- a/gegl/process/gegl-processor.c
+++ b/gegl/process/gegl-processor.c
@@ -785,20 +785,20 @@ gegl_processor_work (GeglProcessor *processor,
       *progress = 1.0;
     }
 
+  more_work = FALSE;
   if (processor->context)
     {
       /* the actual writing to the destination */
-      gegl_operation_process (processor->real_node->operation,
-                              processor->context,
-                              "output"  /* ignored output_pad */,
-                              &processor->context->result_rect, processor->context->level);
+      more_work = gegl_operation_process (processor->real_node->operation,
+                                          processor->context,
+                                          "output"  /* ignored output_pad */,
+                                          &processor->context->result_rect, processor->context->level);
+      processor->real_node->success = more_work;
       gegl_operation_context_destroy (processor->context);
       processor->context = NULL;
-
-      return TRUE;
     }
 
-  return FALSE;
+  return more_work;
 }
 
 GeglProcessor *


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