X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Futils%2Fhazard-pointer.c;h=a89b7713714ae95aa2c20e4a00e90c4834ab4e61;hb=afd9c96b3f5c432a8d337dc134a59e09fa405a9a;hp=df1e4cdc8e96744b8aed11c16686b66af0064fdd;hpb=ac229973136cd80c6ff5c7fee05a63b24e1bcad3;p=mono.git diff --git a/mono/utils/hazard-pointer.c b/mono/utils/hazard-pointer.c index df1e4cdc8e9..a89b7713714 100644 --- a/mono/utils/hazard-pointer.c +++ b/mono/utils/hazard-pointer.c @@ -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 @@ -22,13 +24,11 @@ #include #include #include -#include #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