Merge pull request #2810 from kumpera/fix_hazard_free
[mono.git] / mono / utils / hazard-pointer.c
index 606387c073ef9e93a815366808aee01bbe3d0cd6..efe6b649b3039450cbdc176f0373cd55754a69be 100644 (file)
@@ -2,6 +2,7 @@
  * hazard-pointer.c: 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>
@@ -28,7 +29,7 @@
 typedef struct {
        gpointer p;
        MonoHazardousFreeFunc free_func;
-       gboolean might_lock;
+       HazardFreeLocking locking;
 } DelayedFreeItem;
 
 /* The hazard table */
@@ -42,6 +43,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
@@ -177,7 +179,7 @@ mono_hazard_pointer_get (void)
 
        if (small_id < 0) {
                static MonoThreadHazardPointers emerg_hazard_table;
-               g_warning ("Thread %p may have been prematurely finalized", (gpointer)mono_native_thread_id_get ());
+               g_warning ("Thread %p may have been prematurely finalized", (gpointer) (gsize) mono_native_thread_id_get ());
                return &emerg_hazard_table;
        }
 
@@ -286,7 +288,7 @@ mono_hazard_pointer_restore_for_signal_handler (int small_id)
 }
 
 static gboolean
-try_free_delayed_free_item (gboolean lock_free_context)
+try_free_delayed_free_item (HazardFreeContext context)
 {
        DelayedFreeItem item;
        gboolean popped = mono_lock_free_array_queue_pop (&delayed_free_queue, &item);
@@ -294,7 +296,8 @@ try_free_delayed_free_item (gboolean lock_free_context)
        if (!popped)
                return FALSE;
 
-       if ((lock_free_context && item.might_lock) || (is_pointer_hazardous (item.p))) {
+       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);
                return FALSE;
        }
@@ -304,39 +307,69 @@ try_free_delayed_free_item (gboolean lock_free_context)
        return TRUE;
 }
 
+/**
+ * mono_thread_hazardous_try_free:
+ * @p: the pointer to free
+ * @free_func: the function that can free the pointer
+ *
+ * If @p is not a hazardous pointer it will be immediately freed by calling @free_func.
+ * Otherwise it will be queued for later.
+ *
+ * Use this function if @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.
+ *
+ * Return: TRUE if @p was free or FALSE if it was queued.
+ */
+gboolean
+mono_thread_hazardous_try_free (gpointer p, MonoHazardousFreeFunc free_func)
+{
+       if (!is_pointer_hazardous (p)) {
+               free_func (p);
+               return TRUE;
+       } else {
+               mono_thread_hazardous_queue_free (p, free_func);
+               return FALSE;
+       }
+}
+
+/**
+ * mono_thread_hazardous_queue_free:
+ * @p: the pointer to free
+ * @free_func: the function that can free the pointer
+ *
+ * Queue @p to be freed later. @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 mono_thread_hazardous_try_free_some for when it's appropriate.
+ *
+ */
 void
-mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func,
-               gboolean free_func_might_lock, gboolean lock_free_context)
+mono_thread_hazardous_queue_free (gpointer p, MonoHazardousFreeFunc free_func)
 {
-       int i;
+       DelayedFreeItem item = { p, free_func, HAZARD_FREE_MAY_LOCK };
 
-       if (lock_free_context)
-               g_assert (!free_func_might_lock);
-       if (free_func_might_lock)
-               g_assert (!lock_free_context);
+       InterlockedIncrement (&hazardous_pointer_count);
 
-       /* First try to free a few entries in the delayed free
-          table. */
-       for (i = 0; i < 3; ++i)
-               try_free_delayed_free_item (lock_free_context);
+       mono_lock_free_array_queue_push (&delayed_free_queue, &item);
 
-       /* Now see if the pointer we're freeing is hazardous.  If it
-          isn't, free it.  Otherwise put it in the delay list. */
-       if (is_pointer_hazardous (p)) {
-               DelayedFreeItem item = { p, free_func, free_func_might_lock };
+       guint32 queue_size = delayed_free_queue.num_used_entries;
+       if (queue_size && queue_size_cb)
+               queue_size_cb (queue_size);
+}
 
-               ++hazardous_pointer_count;
 
-               mono_lock_free_array_queue_push (&delayed_free_queue, &item);
-       } else {
-               free_func (p);
-       }
+void
+mono_hazard_pointer_install_free_queue_size_callback (MonoHazardFreeQueueSizeCallback cb)
+{
+       queue_size_cb = cb;
 }
 
 void
 mono_thread_hazardous_try_free_all (void)
 {
-       while (try_free_delayed_free_item (FALSE))
+       while (try_free_delayed_free_item (HAZARD_FREE_SAFE_CTX))
                ;
 }
 
@@ -345,7 +378,7 @@ mono_thread_hazardous_try_free_some (void)
 {
        int i;
        for (i = 0; i < 10; ++i)
-               try_free_delayed_free_item (FALSE);
+               try_free_delayed_free_item (HAZARD_FREE_SAFE_CTX);
 }
 
 void