Initial set of Ward sgen annotations (#5705)
authorAleksey Kliger (λgeek) <akliger@gmail.com>
Thu, 5 Oct 2017 21:31:11 +0000 (17:31 -0400)
committerGitHub <noreply@github.com>
Thu, 5 Oct 2017 21:31:11 +0000 (17:31 -0400)
* [ward] Expand MONO_PERMIT to empty string when not running Ward
  See https://github.com/evincarofautumn/Ward#annotating-your-code for the Ward
  permissions syntax
* sgen_gc_locked
* sgen_world_stopped
* sgen_lock_gc
* sgen_stop_world

mono/metadata/sgen-mono.c
mono/sgen/gc-internal-agnostic.h
mono/sgen/sgen-client.h
mono/sgen/sgen-fin-weak-hash.c
mono/sgen/sgen-gc.h
mono/utils/Makefile.am
mono/utils/ward.h [new file with mode: 0644]

index 4f8a285f4993c05e2bf1a4a90c99967917841032..4fc0e362d4eb90558c06f8c9f575e4128189c66a 100644 (file)
@@ -593,7 +593,7 @@ typedef struct {
 static EphemeronLinkNode *ephemeron_list;
 
 /* LOCKING: requires that the GC lock is held */
 static EphemeronLinkNode *ephemeron_list;
 
 /* LOCKING: requires that the GC lock is held */
-static void
+static MONO_PERMIT (need (sgen_gc_locked)) void
 null_ephemerons_for_domain (MonoDomain *domain)
 {
        EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
 null_ephemerons_for_domain (MonoDomain *domain)
 {
        EphemeronLinkNode *current = ephemeron_list, *prev = NULL;
index fef115796dd1b0d8f4553879d55c653c44959f54..f1c887b5754f0e529afb8890c8a288e788c9c35c 100644 (file)
@@ -14,6 +14,7 @@
 #include <glib.h>
 #include <stdio.h>
 
 #include <glib.h>
 #include <stdio.h>
 
+#include "mono/utils/ward.h"
 #include "mono/utils/mono-compiler.h"
 #include "mono/utils/parse.h"
 #include "mono/utils/memfuncs.h"
 #include "mono/utils/mono-compiler.h"
 #include "mono/utils/parse.h"
 #include "mono/utils/memfuncs.h"
@@ -82,17 +83,21 @@ typedef void* MonoGCDescriptor;
 
 gboolean mono_gc_parse_environment_string_extract_number (const char *str, size_t *out);
 
 
 gboolean mono_gc_parse_environment_string_extract_number (const char *str, size_t *out);
 
-MonoGCDescriptor mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size);
-MonoGCDescriptor mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size);
+MonoGCDescriptor mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
+    MONO_PERMIT (need (sgen_lock_gc));
+MonoGCDescriptor mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
+    MONO_PERMIT (need (sgen_lock_gc));
 
 /* simple interface for data structures needed in the runtime */
 
 /* simple interface for data structures needed in the runtime */
-MonoGCDescriptor mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits);
+MonoGCDescriptor mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
+    MONO_PERMIT (need (sgen_lock_gc));
 
 /* Return a root descriptor for a vector with repeating refs bitmap */
 MonoGCDescriptor mono_gc_make_vector_descr (void);
 
 /* Return a root descriptor for a root with all refs */
 
 /* Return a root descriptor for a vector with repeating refs bitmap */
 MonoGCDescriptor mono_gc_make_vector_descr (void);
 
 /* Return a root descriptor for a root with all refs */
-MonoGCDescriptor mono_gc_make_root_descr_all_refs (int numbits);
+MonoGCDescriptor mono_gc_make_root_descr_all_refs (int numbits)
+    MONO_PERMIT (need (sgen_lock_gc));
 
 /* Return the bitmap encoded by a descriptor */
 gsize* mono_gc_get_bitmap_for_descr (MonoGCDescriptor descr, int *numbits);
 
 /* Return the bitmap encoded by a descriptor */
 gsize* mono_gc_get_bitmap_for_descr (MonoGCDescriptor descr, int *numbits);
index 99d77857935604a695e84b73bb93418b18f51b97..4137cacc3c2a94a6e26e25f77b9de57df373cef2 100644 (file)
@@ -69,13 +69,15 @@ void sgen_client_finalize_notify (void);
  * Returns TRUE if no ephemerons have been marked.  Will be called again if it returned
  * FALSE.  If ephemerons are not supported, just return TRUE.
  */
  * Returns TRUE if no ephemerons have been marked.  Will be called again if it returned
  * FALSE.  If ephemerons are not supported, just return TRUE.
  */
-gboolean sgen_client_mark_ephemerons (ScanCopyContext ctx);
+gboolean sgen_client_mark_ephemerons (ScanCopyContext ctx)
+    MONO_PERMIT (need (sgen_gc_locked));
 
 /*
  * Clear ephemeron pairs with unreachable keys.
  * We pass the copy func so we can figure out if an array was promoted or not.
  */
 
 /*
  * Clear ephemeron pairs with unreachable keys.
  * We pass the copy func so we can figure out if an array was promoted or not.
  */
-void sgen_client_clear_unreachable_ephemerons (ScanCopyContext ctx);
+void sgen_client_clear_unreachable_ephemerons (ScanCopyContext ctx)
+    MONO_PERMIT (need (sgen_gc_locked));
 
 /*
  * May return NULL.  Must be an aligned pointer.
 
 /*
  * May return NULL.  Must be an aligned pointer.
@@ -174,8 +176,10 @@ void sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gbool
  * Stop and restart the world, i.e., all threads that interact with the managed heap.  For
  * single-threaded programs this is a nop.
  */
  * Stop and restart the world, i.e., all threads that interact with the managed heap.  For
  * single-threaded programs this is a nop.
  */
-void sgen_client_stop_world (int generation);
-void sgen_client_restart_world (int generation, gint64 *stw_time);
+void sgen_client_stop_world (int generation)
+    MONO_PERMIT (need (sgen_gc_locked));
+void sgen_client_restart_world (int generation, gint64 *stw_time)
+    MONO_PERMIT (need (sgen_gc_locked));
 
 /*
  * Must return FALSE.  The bridge is not supported outside of Mono.
 
 /*
  * Must return FALSE.  The bridge is not supported outside of Mono.
index e227281c12d96e0d45b2d49fd6f557e9d24afaee..814b516a272042624a2e9a95a73a86ba5ffc4659 100644 (file)
@@ -229,7 +229,7 @@ sgen_finalize_in_range (int generation, ScanCopyContext ctx)
 }
 
 /* LOCKING: requires that the GC lock is held */
 }
 
 /* LOCKING: requires that the GC lock is held */
-static void
+static MONO_PERMIT (need (sgen_gc_locked)) void
 register_for_finalization (GCObject *obj, void *user_data, int generation)
 {
        SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
 register_for_finalization (GCObject *obj, void *user_data, int generation)
 {
        SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
@@ -346,7 +346,7 @@ try_lock_stage_for_processing (int num_entries, volatile gint32 *next_entry)
 }
 
 /* LOCKING: requires that the GC lock is held */
 }
 
 /* LOCKING: requires that the GC lock is held */
-static void
+static MONO_PERMIT (need (sgen_gc_locked)) void
 process_stage_entries (int num_entries, volatile gint32 *next_entry, StageEntry *entries, void (*process_func) (GCObject*, void*, int))
 {
        int i;
 process_stage_entries (int num_entries, volatile gint32 *next_entry, StageEntry *entries, void (*process_func) (GCObject*, void*, int))
 {
        int i;
@@ -528,7 +528,7 @@ add_stage_entry (int num_entries, volatile gint32 *next_entry, StageEntry *entri
 }
 
 /* LOCKING: requires that the GC lock is held */
 }
 
 /* LOCKING: requires that the GC lock is held */
-static void
+static MONO_PERMIT (need (sgen_gc_locked)) void
 process_fin_stage_entry (GCObject *obj, void *user_data, int index)
 {
        if (ptr_in_nursery (obj))
 process_fin_stage_entry (GCObject *obj, void *user_data, int index)
 {
        if (ptr_in_nursery (obj))
@@ -558,7 +558,7 @@ sgen_object_register_for_finalization (GCObject *obj, void *user_data)
 }
 
 /* LOCKING: requires that the GC lock is held */
 }
 
 /* LOCKING: requires that the GC lock is held */
-static void
+static MONO_PERMIT (need (sgen_gc_locked)) void
 finalize_with_predicate (SgenObjectPredicateFunc predicate, void *user_data, SgenHashTable *hash_table)
 {
        GCObject *object;
 finalize_with_predicate (SgenObjectPredicateFunc predicate, void *user_data, SgenHashTable *hash_table)
 {
        GCObject *object;
index 4ed1ec495f98cd7ba2d87e689458aab47ecbd979..4c7377e31ee6328a32ce895a6115454ee4cbfcda 100644 (file)
@@ -31,6 +31,7 @@ typedef struct _SgenThreadInfo SgenThreadInfo;
 #include "mono/utils/atomic.h"
 #include "mono/utils/mono-os-mutex.h"
 #include "mono/utils/mono-coop-mutex.h"
 #include "mono/utils/atomic.h"
 #include "mono/utils/mono-os-mutex.h"
 #include "mono/utils/mono-coop-mutex.h"
+#include "mono/utils/ward.h"
 #include "mono/sgen/sgen-conf.h"
 #include "mono/sgen/sgen-hash-table.h"
 #include "mono/sgen/sgen-protocol.h"
 #include "mono/sgen/sgen-conf.h"
 #include "mono/sgen/sgen-hash-table.h"
 #include "mono/sgen/sgen-protocol.h"
@@ -292,7 +293,8 @@ enum {
        SGEN_GC_BIT_FINALIZER_AWARE = 4,
 };
 
        SGEN_GC_BIT_FINALIZER_AWARE = 4,
 };
 
-void sgen_gc_init (void);
+void sgen_gc_init (void)
+    MONO_PERMIT (need (sgen_lock_gc));
 
 void sgen_os_init (void);
 
 
 void sgen_os_init (void);
 
@@ -392,8 +394,10 @@ enum {
 
 extern SgenHashTable roots_hash [ROOT_TYPE_NUM];
 
 
 extern SgenHashTable roots_hash [ROOT_TYPE_NUM];
 
-int sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_type, int source, const char *msg);
-void sgen_deregister_root (char* addr);
+int sgen_register_root (char *start, size_t size, SgenDescriptor descr, int root_type, int source, const char *msg)
+       MONO_PERMIT (need (sgen_lock_gc));
+void sgen_deregister_root (char* addr)
+       MONO_PERMIT (need (sgen_lock_gc));
 
 typedef void (*IterateObjectCallbackFunc) (GCObject*, size_t, void*);
 
 
 typedef void (*IterateObjectCallbackFunc) (GCObject*, size_t, void*);
 
@@ -797,27 +801,35 @@ void sgen_clear_togglerefs (char *start, char *end, ScanCopyContext ctx);
 void sgen_process_togglerefs (void);
 void sgen_register_test_toggleref_callback (void);
 
 void sgen_process_togglerefs (void);
 void sgen_register_test_toggleref_callback (void);
 
-void sgen_mark_bridge_object (GCObject *obj);
-void sgen_collect_bridge_objects (int generation, ScanCopyContext ctx);
+void sgen_mark_bridge_object (GCObject *obj)
+       MONO_PERMIT (need (sgen_gc_locked));
+void sgen_collect_bridge_objects (int generation, ScanCopyContext ctx)
+       MONO_PERMIT (need (sgen_gc_locked));
 
 typedef gboolean (*SgenObjectPredicateFunc) (GCObject *obj, void *user_data);
 
 
 typedef gboolean (*SgenObjectPredicateFunc) (GCObject *obj, void *user_data);
 
-void sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation, gboolean track);
+void sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation, gboolean track)
+       MONO_PERMIT (need (sgen_gc_locked, sgen_world_stopped));
 
 gboolean sgen_gc_is_object_ready_for_finalization (GCObject *object);
 
 gboolean sgen_gc_is_object_ready_for_finalization (GCObject *object);
-void sgen_gc_lock (void);
-void sgen_gc_unlock (void);
+void sgen_gc_lock (void) MONO_PERMIT (use (sgen_lock_gc), grant (sgen_gc_locked), revoke (sgen_lock_gc));
+void sgen_gc_unlock (void) MONO_PERMIT (use (sgen_gc_locked), revoke (sgen_gc_locked), grant (sgen_lock_gc));
 
 void sgen_queue_finalization_entry (GCObject *obj);
 const char* sgen_generation_name (int generation);
 
 
 void sgen_queue_finalization_entry (GCObject *obj);
 const char* sgen_generation_name (int generation);
 
-void sgen_finalize_in_range (int generation, ScanCopyContext ctx);
-void sgen_null_link_in_range (int generation, ScanCopyContext ctx, gboolean track);
-void sgen_process_fin_stage_entries (void);
+void sgen_finalize_in_range (int generation, ScanCopyContext ctx)
+       MONO_PERMIT (need (sgen_gc_locked));
+void sgen_null_link_in_range (int generation, ScanCopyContext ctx, gboolean track)
+       MONO_PERMIT (need (sgen_gc_locked, sgen_world_stopped));
+void sgen_process_fin_stage_entries (void)
+       MONO_PERMIT (need (sgen_gc_locked));
 gboolean sgen_have_pending_finalizers (void);
 gboolean sgen_have_pending_finalizers (void);
-void sgen_object_register_for_finalization (GCObject *obj, void *user_data);
+void sgen_object_register_for_finalization (GCObject *obj, void *user_data)
+       MONO_PERMIT (need (sgen_lock_gc));
 
 
-void sgen_finalize_if (SgenObjectPredicateFunc predicate, void *user_data);
+void sgen_finalize_if (SgenObjectPredicateFunc predicate, void *user_data)
+       MONO_PERMIT (need (sgen_lock_gc));
 void sgen_remove_finalizers_if (SgenObjectPredicateFunc predicate, void *user_data, int generation);
 void sgen_set_suspend_finalizers (void);
 
 void sgen_remove_finalizers_if (SgenObjectPredicateFunc predicate, void *user_data, int generation);
 void sgen_set_suspend_finalizers (void);
 
@@ -839,19 +851,25 @@ enum {
 void sgen_pin_object (GCObject *object, SgenGrayQueue *queue);
 void sgen_set_pinned_from_failed_allocation (mword objsize);
 
 void sgen_pin_object (GCObject *object, SgenGrayQueue *queue);
 void sgen_set_pinned_from_failed_allocation (mword objsize);
 
-void sgen_ensure_free_space (size_t size, int generation);
-void sgen_gc_collect (int generation);
-void sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish, gboolean stw);
+void sgen_ensure_free_space (size_t size, int generation)
+       MONO_PERMIT (need (sgen_gc_locked, sgen_stop_world));
+void sgen_gc_collect (int generation)
+       MONO_PERMIT (need (sgen_lock_gc, sgen_stop_world));
+void sgen_perform_collection (size_t requested_size, int generation_to_collect, const char *reason, gboolean wait_to_finish, gboolean stw)
+       MONO_PERMIT (need (sgen_gc_locked, sgen_stop_world));
 
 int sgen_gc_collection_count (int generation);
 /* FIXME: what exactly does this return? */
 
 int sgen_gc_collection_count (int generation);
 /* FIXME: what exactly does this return? */
-size_t sgen_gc_get_used_size (void);
+size_t sgen_gc_get_used_size (void)
+       MONO_PERMIT (need (sgen_lock_gc));
 size_t sgen_gc_get_total_heap_allocation (void);
 
 /* STW */
 
 size_t sgen_gc_get_total_heap_allocation (void);
 
 /* STW */
 
-void sgen_stop_world (int generation);
-void sgen_restart_world (int generation);
+void sgen_stop_world (int generation)
+       MONO_PERMIT (need (sgen_gc_locked), use (sgen_stop_world), grant (sgen_world_stopped), revoke (sgen_stop_world));
+void sgen_restart_world (int generation)
+       MONO_PERMIT (need (sgen_gc_locked), use (sgen_world_stopped), revoke (sgen_world_stopped), grant (sgen_stop_world));
 gboolean sgen_is_world_stopped (void);
 
 gboolean sgen_set_allow_synchronous_major (gboolean flag);
 gboolean sgen_is_world_stopped (void);
 
 gboolean sgen_set_allow_synchronous_major (gboolean flag);
@@ -874,7 +892,8 @@ extern mword los_memory_usage;
 extern mword los_memory_usage_total;
 
 void sgen_los_free_object (LOSObject *obj);
 extern mword los_memory_usage_total;
 
 void sgen_los_free_object (LOSObject *obj);
-void* sgen_los_alloc_large_inner (GCVTable vtable, size_t size);
+void* sgen_los_alloc_large_inner (GCVTable vtable, size_t size)
+       MONO_PERMIT (need (sgen_gc_locked, sgen_stop_world));
 void sgen_los_sweep (void);
 gboolean sgen_ptr_is_in_los (char *ptr, char **start);
 void sgen_los_iterate_objects (IterateObjectCallbackFunc cb, void *user_data);
 void sgen_los_sweep (void);
 gboolean sgen_ptr_is_in_los (char *ptr, char **start);
 void sgen_los_iterate_objects (IterateObjectCallbackFunc cb, void *user_data);
@@ -912,7 +931,8 @@ void sgen_nursery_alloc_prepare_for_major (void);
 
 GCObject* sgen_alloc_for_promotion (GCObject *obj, size_t objsize, gboolean has_references);
 
 
 GCObject* sgen_alloc_for_promotion (GCObject *obj, size_t objsize, gboolean has_references);
 
-GCObject* sgen_alloc_obj_nolock (GCVTable vtable, size_t size);
+GCObject* sgen_alloc_obj_nolock (GCVTable vtable, size_t size)
+       MONO_PERMIT (need (sgen_gc_locked, sgen_stop_world));
 GCObject* sgen_try_alloc_obj_nolock (GCVTable vtable, size_t size);
 
 /* Threads */
 GCObject* sgen_try_alloc_obj_nolock (GCVTable vtable, size_t size);
 
 /* Threads */
@@ -958,21 +978,25 @@ sgen_is_object_alive_for_current_gen (GCObject *object)
        return sgen_major_is_object_alive (object);
 }
 
        return sgen_major_is_object_alive (object);
 }
 
-int sgen_gc_invoke_finalizers (void);
+int sgen_gc_invoke_finalizers (void)
+       MONO_PERMIT (need (sgen_lock_gc));
 
 /* GC handles */
 
 void sgen_init_gchandles (void);
 
 
 /* GC handles */
 
 void sgen_init_gchandles (void);
 
-void sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation, gboolean track);
+void sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation, gboolean track)
+       MONO_PERMIT (need (sgen_gc_locked));
 
 typedef gpointer (*SgenGCHandleIterateCallback) (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user);
 
 guint32 sgen_gchandle_new (GCObject *obj, gboolean pinned);
 guint32 sgen_gchandle_new_weakref (GCObject *obj, gboolean track_resurrection);
 
 typedef gpointer (*SgenGCHandleIterateCallback) (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user);
 
 guint32 sgen_gchandle_new (GCObject *obj, gboolean pinned);
 guint32 sgen_gchandle_new_weakref (GCObject *obj, gboolean track_resurrection);
-void sgen_gchandle_iterate (GCHandleType handle_type, int max_generation, SgenGCHandleIterateCallback callback, gpointer user);
+void sgen_gchandle_iterate (GCHandleType handle_type, int max_generation, SgenGCHandleIterateCallback callback, gpointer user)
+       MONO_PERMIT (need (sgen_world_stopped));
 void sgen_gchandle_set_target (guint32 gchandle, GCObject *obj);
 void sgen_gchandle_set_target (guint32 gchandle, GCObject *obj);
-void sgen_mark_normal_gc_handles (void *addr, SgenUserMarkFunc mark_func, void *gc_data);
+void sgen_mark_normal_gc_handles (void *addr, SgenUserMarkFunc mark_func, void *gc_data)
+       MONO_PERMIT (need (sgen_world_stopped));
 gpointer sgen_gchandle_get_metadata (guint32 gchandle);
 GCObject *sgen_gchandle_get_target (guint32 gchandle);
 void sgen_gchandle_free (guint32 gchandle);
 gpointer sgen_gchandle_get_metadata (guint32 gchandle);
 GCObject *sgen_gchandle_get_target (guint32 gchandle);
 void sgen_gchandle_free (guint32 gchandle);
@@ -1018,9 +1042,12 @@ typedef enum {
 
 void sgen_clear_tlabs (void);
 
 
 void sgen_clear_tlabs (void);
 
-GCObject* sgen_alloc_obj (GCVTable vtable, size_t size);
-GCObject* sgen_alloc_obj_pinned (GCVTable vtable, size_t size);
-GCObject* sgen_alloc_obj_mature (GCVTable vtable, size_t size);
+GCObject* sgen_alloc_obj (GCVTable vtable, size_t size)
+       MONO_PERMIT (need (sgen_lock_gc, sgen_stop_world));
+GCObject* sgen_alloc_obj_pinned (GCVTable vtable, size_t size)
+       MONO_PERMIT (need (sgen_lock_gc, sgen_stop_world));
+GCObject* sgen_alloc_obj_mature (GCVTable vtable, size_t size)
+       MONO_PERMIT (need (sgen_lock_gc, sgen_stop_world));
 
 /* Debug support */
 
 
 /* Debug support */
 
@@ -1028,7 +1055,8 @@ void sgen_check_remset_consistency (void);
 void sgen_check_mod_union_consistency (void);
 void sgen_check_major_refs (void);
 void sgen_check_whole_heap (gboolean allow_missing_pinning);
 void sgen_check_mod_union_consistency (void);
 void sgen_check_major_refs (void);
 void sgen_check_whole_heap (gboolean allow_missing_pinning);
-void sgen_check_whole_heap_stw (void);
+void sgen_check_whole_heap_stw (void)
+       MONO_PERMIT (need (sgen_gc_locked, sgen_stop_world));
 void sgen_check_objref (char *obj);
 void sgen_check_heap_marked (gboolean nursery_must_be_pinned);
 void sgen_check_nursery_objects_pinned (gboolean pinned);
 void sgen_check_objref (char *obj);
 void sgen_check_heap_marked (gboolean nursery_must_be_pinned);
 void sgen_check_nursery_objects_pinned (gboolean pinned);
index 119d7c94c2cce1c8e2427211a53467708fc0eeb4..94416bc95372b9022f653cdcabf5d5bc4eefa38c 100644 (file)
@@ -180,7 +180,8 @@ monoutils_sources = \
        os-event.h \
        refcount.h      \
        w32api.h        \
        os-event.h \
        refcount.h      \
        w32api.h        \
-       unlocked.h
+       unlocked.h      \
+       ward.h
 
 arch_sources = 
 
 
 arch_sources = 
 
diff --git a/mono/utils/ward.h b/mono/utils/ward.h
new file mode 100644 (file)
index 0000000..5d85d35
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef WARD_H
+#define WARD_H
+
+/*
+ * Ward is a static analysis tool that can be used to check for the presense of
+ * a certain class of bugs in C code.
+ *
+ * See https://github.com/evincarofautumn/Ward#annotating-your-code for the Ward
+ * permission annotations syntax.
+ *
+ * The Mono permissions are defined in
+ * https://github.com/evincarofautumn/Ward/blob/prod/mono.config
+ */
+#if defined(__WARD__)
+#define MONO_PERMIT(...) __attribute__ ((ward (__VA_ARGS__)))
+#else
+#define MONO_PERMIT(...) /*empty*/
+#endif
+
+#endif