[MonoError] Add MonoErrorBoxed for mempool-stored errors.
authorAleksey Kliger <aleksey@xamarin.com>
Thu, 29 Sep 2016 21:15:48 +0000 (17:15 -0400)
committerAleksey Kliger <aleksey@xamarin.com>
Tue, 4 Oct 2016 15:21:53 +0000 (11:21 -0400)
The usual MonoError is suitable as a short-lived stack-allocated error
with a lifetime confined to a function call.
Internally it performs allocations using g_malloc/g_free.

The MonoErrorBoxed is a mempool-allocated error whose lifetime is tied
to the lifetime of a MonoImage.

We provide two operations:

1. MonoErrorBoxed* mono_error_box (MonoError *ierror, MonoImage *image);

  Creates a new boxed error in the given mempool from MonoError.
  It does not alter ierror, so you still have to clean it up with
  mono_error_cleanup or mono_error_convert_to_exception or another such function.

2.   gboolean mono_error_set_from_boxed (MonoError *oerror, MonoErrorBoxed *from_box);

  Sets the error condition in the oerror from the contents of the given
  boxed error.  Does not alter the boxed error, so it can be used in a
  future call to mono_error_set_from_boxed as needed.  The oerror
  should've been previously initialized with mono_error_init, as usual.

mono/utils/mono-error-internals.h
mono/utils/mono-error.c
mono/utils/mono-error.h

index fb9fdcacb7e84ef33c2587dbf9f067bfd6627350..ee1a370f5e26d1d0068d3ad8cbbd5eee87bd6e39 100644 (file)
@@ -32,6 +32,12 @@ typedef struct {
        void *padding [3];
 } MonoErrorInternal;
 
+/* Invariant: the error strings are allocated in the mempool of the given image */
+struct _MonoErrorBoxed {
+       MonoError error;
+       MonoImage *image;
+};
+
 #define error_init(error) do { \
        ((MonoErrorInternal*)(error))->error_code = MONO_ERROR_NONE;    \
        ((MonoErrorInternal*)(error))->flags = 0;       \
@@ -128,4 +134,11 @@ mono_error_raise_exception (MonoError *error);
 void
 mono_error_move (MonoError *dest, MonoError *src);
 
+MonoErrorBoxed*
+mono_error_box (const MonoError *error, MonoImage *image);
+
+gboolean
+mono_error_set_from_boxed (MonoError *error, const MonoErrorBoxed *from);
+
+
 #endif
index 7e186b6b717ce06b2f4103bb2ab3abe22e5ce2df..04a30cb5861cb6131687d9f20efad67a3b3f7b61 100644 (file)
@@ -38,6 +38,12 @@ is_managed_exception (MonoErrorInternal *error)
        return (error->error_code == MONO_ERROR_EXCEPTION_INSTANCE);
 }
 
+static gboolean
+is_boxed (MonoErrorInternal *error)
+{
+       return ((error->flags & MONO_ERROR_MEMPOOL_BOXED) != 0);
+}
+
 static void
 mono_error_prepare (MonoErrorInternal *error)
 {
@@ -116,6 +122,8 @@ mono_error_cleanup (MonoError *oerror)
 
        /* Two cleanups in a row without an intervening init. */
        g_assert (orig_error_code != MONO_ERROR_CLEANUP_CALLED_SENTINEL);
+       /* Mempool stored error shouldn't be cleaned up */
+       g_assert (!is_boxed (error));
 
        /* Mark it as cleaned up. */
        error->error_code = MONO_ERROR_CLEANUP_CALLED_SENTINEL;
@@ -730,6 +738,9 @@ mono_error_convert_to_exception (MonoError *target_error)
        MonoError error;
        MonoException *ex;
 
+       /* Mempool stored error shouldn't be cleaned up */
+       g_assert (!is_boxed ((MonoErrorInternal*)target_error));
+
        if (mono_error_ok (target_error))
                return NULL;
 
@@ -752,3 +763,97 @@ mono_error_move (MonoError *dest, MonoError *src)
        memcpy (dest, src, sizeof (MonoErrorInternal));
        mono_error_init (src);
 }
+
+/**
+ * mono_error_box:
+ * @ierror: The input error that will be boxed.
+ * @image: The mempool of this image will hold the boxed error.
+ *
+ * Creates a new boxed error in the given mempool from MonoError.
+ * It does not alter ierror, so you still have to clean it up with
+ * mono_error_cleanup or mono_error_convert_to_exception or another such function.
+ *
+ * Returns the boxed error, or NULL if the mempool could not allocate.
+ */
+MonoErrorBoxed*
+mono_error_box (const MonoError *ierror, MonoImage *image)
+{
+       MonoErrorInternal *from = (MonoErrorInternal*)ierror;
+       /* Don't know how to box a gchandle */
+       g_assert (!is_managed_exception (from));
+       MonoErrorBoxed* box = mono_image_alloc (image, sizeof (MonoErrorBoxed));
+       box->image = image;
+       mono_error_init_flags (&box->error, MONO_ERROR_MEMPOOL_BOXED);
+       MonoErrorInternal *to = (MonoErrorInternal*)&box->error;
+
+#define DUP_STR(field) do {                                            \
+               if (from->field) {                                      \
+                       if (!(to->field = mono_image_strdup (image, from->field))) \
+                               to->flags |= MONO_ERROR_INCOMPLETE;     \
+               } else {                                                \
+                       to->field = NULL;                               \
+               }                                                       \
+       } while (0)
+
+       to->error_code = from->error_code;
+       DUP_STR (type_name);
+       DUP_STR (assembly_name);
+       DUP_STR (member_name);
+       DUP_STR (exception_name_space);
+       DUP_STR (exception_name);
+       DUP_STR (full_message);
+       DUP_STR (full_message_with_fields);
+       DUP_STR (first_argument);
+       to->exn.klass = from->exn.klass;
+
+#undef DUP_STR
+       
+       return box;
+}
+
+
+/**
+ * mono_error_set_from_boxed:
+ * @oerror: The error that will be set to the contents of the box.
+ * @box: A mempool-allocated error.
+ *
+ * Sets the error condition in the oerror from the contents of the
+ * given boxed error.  Does not alter the boxed error, so it can be
+ * used in a future call to mono_error_set_from_boxed as needed.  The
+ * oerror should've been previously initialized with mono_error_init,
+ * as usual.
+ *
+ * Returns TRUE on success or FALSE on failure.
+ */
+gboolean
+mono_error_set_from_boxed (MonoError *oerror, const MonoErrorBoxed *box)
+{
+       MonoErrorInternal* to = (MonoErrorInternal*)oerror;
+       MonoErrorInternal* from = (MonoErrorInternal*)&box->error;
+       g_assert (!is_managed_exception (from));
+
+       mono_error_prepare (to);
+       to->flags |= MONO_ERROR_FREE_STRINGS;
+#define DUP_STR(field) do {                                            \
+               if (from->field) {                                      \
+                       if (!(to->field = g_strdup (from->field)))      \
+                               to->flags |= MONO_ERROR_INCOMPLETE;     \
+               } else {                                                \
+                       to->field = NULL;                               \
+               }                                                       \
+       } while (0)
+
+       to->error_code = from->error_code;
+       DUP_STR (type_name);
+       DUP_STR (assembly_name);
+       DUP_STR (member_name);
+       DUP_STR (exception_name_space);
+       DUP_STR (exception_name);
+       DUP_STR (full_message);
+       DUP_STR (full_message_with_fields);
+       DUP_STR (first_argument);
+       to->exn.klass = from->exn.klass;
+                 
+#undef DUP_STR
+       return (to->flags & MONO_ERROR_INCOMPLETE) == 0 ;
+}
index 0aba0e1d61abb4219d6cfc15a9a8ead976c1ceaa..fa9caa6759e36bb2cecb619b19822f56e08520e2 100644 (file)
@@ -12,7 +12,11 @@ enum {
        /*
        Something happened while processing the error and the resulting message is incomplete.
        */
-       MONO_ERROR_INCOMPLETE = 0x0002
+       MONO_ERROR_INCOMPLETE = 0x0002,
+       /*
+       This MonoError is heap allocated in a mempool
+        */
+       MONO_ERROR_MEMPOOL_BOXED = 0x0004
 };
 
 enum {
@@ -48,6 +52,9 @@ typedef struct _MonoError {
        void *hidden_1 [12]; /*DON'T TOUCH */
 } MonoError;
 
+/* Mempool-allocated MonoError.*/
+typedef struct _MonoErrorBoxed MonoErrorBoxed;
+
 MONO_BEGIN_DECLS
 
 MONO_API void