[runtime] Fix DISABLE_REFLECTION_EMIT build.
[mono.git] / mono / utils / hazard-pointer.c
index df1e4cdc8e96744b8aed11c16686b66af0064fdd..a89b7713714ae95aa2c20e4a00e90c4834ab4e61 100644 (file)
@@ -1,7 +1,9 @@
-/*
- * hazard-pointer.c: Hazard pointer related code.
+/**
+ * \file
+ * Hazard pointer related code.
  *
  * (C) Copyright 2011 Novell, Inc
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
  */
 
 #include <config.h>
 #include <mono/utils/mono-mmap.h>
 #include <mono/utils/mono-threads.h>
 #include <mono/utils/mono-counters.h>
-#include <mono/io-layer/io-layer.h>
 #endif
 
 typedef struct {
        gpointer p;
        MonoHazardousFreeFunc free_func;
-       HazardFreeLocking locking;
 } DelayedFreeItem;
 
 /* The hazard table */
@@ -42,6 +42,7 @@ typedef struct {
 
 static volatile int hazard_table_size = 0;
 static MonoThreadHazardPointers * volatile hazard_table = NULL;
+static MonoHazardFreeQueueSizeCallback queue_size_cb;
 
 /*
  * Each entry is either 0 or 1, indicating whether that overflow small
@@ -51,7 +52,7 @@ static volatile gint32 overflow_busy [HAZARD_TABLE_OVERFLOW];
 
 /* The table where we keep pointers to blocks to be freed but that
    have to wait because they're guarded by a hazard pointer. */
-static MonoLockFreeArrayQueue delayed_free_queue = MONO_LOCK_FREE_ARRAY_QUEUE_INIT (sizeof (DelayedFreeItem));
+static MonoLockFreeArrayQueue delayed_free_queue = MONO_LOCK_FREE_ARRAY_QUEUE_INIT (sizeof (DelayedFreeItem), MONO_MEM_ACCOUNT_HAZARD_POINTERS);
 
 /* The table for small ID assignment */
 static mono_mutex_t small_id_mutex;
@@ -111,7 +112,7 @@ mono_thread_small_id_alloc (void)
                if (hazard_table == NULL) {
                        hazard_table = (MonoThreadHazardPointers *volatile) mono_valloc (NULL,
                                sizeof (MonoThreadHazardPointers) * HAZARD_TABLE_MAX_SIZE,
-                               MONO_MMAP_NONE);
+                               MONO_MMAP_NONE, MONO_MEM_ACCOUNT_HAZARD_POINTERS);
                }
 
                g_assert (hazard_table != NULL);
@@ -189,7 +190,7 @@ mono_hazard_pointer_get (void)
    mono_jit_info_table_add(), which doesn't have to care about hazards
    because it holds the respective domain lock. */
 gpointer
-get_hazardous_pointer (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index)
+mono_get_hazardous_pointer (gpointer volatile *pp, MonoThreadHazardPointers *hp, int hazard_index)
 {
        gpointer p;
 
@@ -285,64 +286,105 @@ mono_hazard_pointer_restore_for_signal_handler (int small_id)
        overflow_busy [small_id] = 0;
 }
 
-static gboolean
-try_free_delayed_free_item (HazardFreeContext context)
+/**
+ * mono_thread_hazardous_try_free:
+ * \param p the pointer to free
+ * \param free_func the function that can free the pointer
+ *
+ * If \p p is not a hazardous pointer it will be immediately freed by calling \p free_func.
+ * Otherwise it will be queued for later.
+ *
+ * Use this function if \p free_func can ALWAYS be called in the context where this function is being called.
+ *
+ * This function doesn't pump the free queue so try to accommodate a call at an appropriate time.
+ * See mono_thread_hazardous_try_free_some for when it's appropriate.
+ *
+ * \returns TRUE if \p p was free or FALSE if it was queued.
+ */
+gboolean
+mono_thread_hazardous_try_free (gpointer p, MonoHazardousFreeFunc free_func)
 {
-       DelayedFreeItem item;
-       gboolean popped = mono_lock_free_array_queue_pop (&delayed_free_queue, &item);
-
-       if (!popped)
-               return FALSE;
-
-       if ((context == HAZARD_FREE_ASYNC_CTX && item.locking == HAZARD_FREE_MAY_LOCK) ||
-           (is_pointer_hazardous (item.p))) {
-               mono_lock_free_array_queue_push (&delayed_free_queue, &item);
+       if (!is_pointer_hazardous (p)) {
+               free_func (p);
+               return TRUE;
+       } else {
+               mono_thread_hazardous_queue_free (p, free_func);
                return FALSE;
        }
+}
 
-       item.free_func (item.p);
+/**
+ * mono_thread_hazardous_queue_free:
+ * \param p the pointer to free
+ * \param free_func the function that can free the pointer
+ * Queue \p p to be freed later. \p p will be freed once the hazard free queue is pumped.
+ *
+ * This function doesn't pump the free queue so try to accommodate a call at an appropriate time.
+ * See \c mono_thread_hazardous_try_free_some for when it's appropriate.
+ */
+void
+mono_thread_hazardous_queue_free (gpointer p, MonoHazardousFreeFunc free_func)
+{
+       DelayedFreeItem item = { p, free_func };
 
-       return TRUE;
+       InterlockedIncrement (&hazardous_pointer_count);
+
+       mono_lock_free_array_queue_push (&delayed_free_queue, &item);
+
+       guint32 queue_size = delayed_free_queue.num_used_entries;
+       if (queue_size && queue_size_cb)
+               queue_size_cb (queue_size);
 }
 
+
 void
-mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func,
-                                     HazardFreeLocking locking, HazardFreeContext context)
+mono_hazard_pointer_install_free_queue_size_callback (MonoHazardFreeQueueSizeCallback cb)
 {
-       int i;
+       queue_size_cb = cb;
+}
 
-       /* First try to free a few entries in the delayed free
-          table. */
-       for (i = 0; i < 3; ++i)
-               try_free_delayed_free_item (context);
+static void
+try_free_delayed_free_items (guint32 limit)
+{
+       GArray *hazardous = NULL;
+       DelayedFreeItem item;
+       guint32 freed = 0;
 
-       /* Now see if the pointer we're freeing is hazardous.  If it
-          isn't, free it.  Otherwise put it in the delay list. */
-       if ((context == HAZARD_FREE_ASYNC_CTX && locking == HAZARD_FREE_MAY_LOCK) ||
-           is_pointer_hazardous (p)) {
-               DelayedFreeItem item = { p, free_func, locking };
+       // Free all the items we can and re-add the ones we can't to the queue.
+       while (mono_lock_free_array_queue_pop (&delayed_free_queue, &item)) {
+               if (is_pointer_hazardous (item.p)) {
+                       if (!hazardous)
+                               hazardous = g_array_sized_new (FALSE, FALSE, sizeof (DelayedFreeItem), delayed_free_queue.num_used_entries);
 
-               ++hazardous_pointer_count;
+                       g_array_append_val (hazardous, item);
+                       continue;
+               }
 
-               mono_lock_free_array_queue_push (&delayed_free_queue, &item);
-       } else {
-               free_func (p);
+               item.free_func (item.p);
+               freed++;
+
+               if (limit && freed == limit)
+                       break;
+       }
+
+       if (hazardous) {
+               for (gint i = 0; i < hazardous->len; i++)
+                       mono_lock_free_array_queue_push (&delayed_free_queue, &g_array_index (hazardous, DelayedFreeItem, i));
+
+               g_array_free (hazardous, TRUE);
        }
 }
 
 void
 mono_thread_hazardous_try_free_all (void)
 {
-       while (try_free_delayed_free_item (HAZARD_FREE_SAFE_CTX))
-               ;
+       try_free_delayed_free_items (0);
 }
 
 void
 mono_thread_hazardous_try_free_some (void)
 {
-       int i;
-       for (i = 0; i < 10; ++i)
-               try_free_delayed_free_item (HAZARD_FREE_SAFE_CTX);
+       try_free_delayed_free_items (10);
 }
 
 void