-/*
- * 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;
- gboolean might_lock;
} DelayedFreeItem;
/* The hazard table */
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
/* 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;
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);
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;
overflow_busy [small_id] = 0;
}
-static gboolean
-try_free_delayed_free_item (gboolean lock_free_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 ((lock_free_context && item.might_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;
}
+}
+
+/**
+ * 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 };
+
+ InterlockedIncrement (&hazardous_pointer_count);
- item.free_func (item.p);
+ mono_lock_free_array_queue_push (&delayed_free_queue, &item);
- return TRUE;
+ 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,
- gboolean free_func_might_lock, gboolean lock_free_context)
+mono_hazard_pointer_install_free_queue_size_callback (MonoHazardFreeQueueSizeCallback cb)
{
- int i;
+ queue_size_cb = cb;
+}
- if (lock_free_context)
- g_assert (!free_func_might_lock);
- if (free_func_might_lock)
- g_assert (!lock_free_context);
+static void
+try_free_delayed_free_items (guint32 limit)
+{
+ GArray *hazardous = NULL;
+ DelayedFreeItem item;
+ guint32 freed = 0;
- /* 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);
+ // 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);
- /* 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 };
+ g_array_append_val (hazardous, item);
+ continue;
+ }
- ++hazardous_pointer_count;
+ item.free_func (item.p);
+ freed++;
- mono_lock_free_array_queue_push (&delayed_free_queue, &item);
- } else {
- free_func (p);
+ 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 (FALSE))
- ;
+ 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 (FALSE);
+ try_free_delayed_free_items (10);
}
void