* Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
* Copyright 2004-2009 Novell, Inc (http://www.novell.com)
* Copyright 2012 Xamarin Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include <config.h>
#include <mono/utils/mono-threads.h>
#include <mono/utils/atomic.h>
#include <mono/utils/mono-coop-semaphore.h>
+ #include <mono/utils/hazard-pointer.h>
#ifndef HOST_WIN32
#include <pthread.h>
mono_coop_sem_post (&finalizer_sem);
}
+ /*
+ This is the number of entries allowed in the hazard free queue before
+ we explicitly cycle the finalizer thread to trigger pumping the queue.
+
+ It was picked empirically by running the corlib test suite in a stress
+ scenario where all hazard entries are queued.
+
+ In this extreme scenario we double the number of times we cycle the finalizer
+ thread compared to just GC calls.
+
+ Entries are usually in the order of 100's of bytes each, so we're limiting
+ floating garbage to be in the order of a dozen kb.
+ */
+ static gboolean finalizer_thread_pulsed;
+ #define HAZARD_QUEUE_OVERFLOW_SIZE 20
+
+ static void
+ hazard_free_queue_is_too_big (size_t size)
+ {
+ if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
+ return;
+
+ if (finalizer_thread_pulsed || InterlockedCompareExchange (&finalizer_thread_pulsed, TRUE, FALSE))
+ return;
+
+ mono_gc_finalize_notify ();
+ }
+
+ static void
+ hazard_free_queue_pump (void)
+ {
+ mono_thread_hazardous_try_free_all ();
+ finalizer_thread_pulsed = FALSE;
+ }
+
#ifdef HAVE_BOEHM_GC
static void
{
gboolean wait = TRUE;
+ /* Register a hazard free queue pump callback */
+ mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
+
while (!finished) {
/* Wait to be notified that there's at least one
* finaliser to run
reference_queue_proccess_all ();
+ hazard_free_queue_pump ();
+
/* Avoid posting the pending done event until there are pending finalizers */
if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == 0) {
/* Don't wait again at the start of the loop */
* Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
* Copyright 2004-2009 Novell, Inc (http://www.novell.com)
* Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include <config.h>
*table_ptr = new_table;
mono_memory_barrier ();
domain->num_jit_info_tables++;
- mono_thread_hazardous_free_or_queue (table, (MonoHazardousFreeFunc)mono_jit_info_table_free, HAZARD_FREE_MAY_LOCK, HAZARD_FREE_SAFE_CTX);
+ mono_thread_hazardous_try_free (table, (MonoHazardousFreeFunc)mono_jit_info_table_free);
table = new_table;
goto restart;
if (domain->num_jit_info_tables <= 1) {
/* Can it actually happen that we only have one table
but ji is still hazardous? */
- mono_thread_hazardous_free_or_queue (ji, g_free, HAZARD_FREE_MAY_LOCK, HAZARD_FREE_SAFE_CTX);
+ mono_thread_hazardous_try_free (ji, g_free);
} else {
domain->jit_info_free_queue = g_slist_prepend (domain->jit_info_free_queue, ji);
}
* Copyright 2011 Xamarin, Inc.
* Copyright (C) 2012 Xamarin Inc
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License 2.0 as published by the Free Software Foundation;
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License 2.0 along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*
* Important: allocation provides always zeroed memory, having to do
* a memset after allocation is deadly for performance.
* ######################################################################
*/
MonoCoopMutex gc_mutex;
- gboolean sgen_try_free_some_memory;
#define SCAN_START_SIZE SGEN_SCAN_START_SIZE
sgen_client_pre_collection_checks ();
- if (!concurrent) {
+ if (mode != COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
/* Remsets are not useful for a major collection */
remset.clear_cards ();
}
sgen_init_pinning ();
SGEN_LOG (6, "Collecting pinned addresses");
pin_from_roots ((void*)lowest_heap_address, (void*)highest_heap_address, ctx);
-
+ if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
+ /* Pin cemented objects that were forced */
+ sgen_pin_cemented_objects ();
+ }
sgen_optimize_pin_queue ();
+ if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
+ /*
+ * Cemented objects that are in the pinned list will be marked. When
+ * marking concurrently we won't mark mod-union cards for these objects.
+ * Instead they will remain cemented until the next major collection,
+ * when we will recheck if they are still pinned in the roots.
+ */
+ sgen_cement_force_pinned ();
+ }
sgen_client_collecting_major_1 ();
major_collector.init_to_space ();
SGEN_ASSERT (0, sgen_workers_all_done (), "Why are the workers not done when we start or finish a major collection?");
- /*
- * The concurrent collector doesn't move objects, neither on
- * the major heap nor in the nursery, so we can mark even
- * before pinning has finished. For the non-concurrent
- * collector we start the workers after pinning.
- */
- if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
- if (precleaning_enabled) {
- ScanJob *sj;
- /* Mod union preclean job */
- sj = (ScanJob*)sgen_thread_pool_job_alloc ("preclean mod union cardtable", job_mod_union_preclean, sizeof (ScanJob));
- sj->ops = object_ops;
- sgen_workers_start_all_workers (object_ops, &sj->job);
- } else {
- sgen_workers_start_all_workers (object_ops, NULL);
- }
- gray_queue_enable_redirect (WORKERS_DISTRIBUTE_GRAY_QUEUE);
- } else if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
+ if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
if (sgen_workers_have_idle_work ()) {
+ /*
+ * We force the finish of the worker with the new object ops context
+ * which can also do copying. We need to have finished pinning.
+ */
sgen_workers_start_all_workers (object_ops, NULL);
sgen_workers_join ();
}
sgen_client_collecting_major_3 (&fin_ready_queue, &critical_fin_queue);
- /*
- * FIXME: is this the right context? It doesn't seem to contain a copy function
- * unless we're concurrent.
- */
- enqueue_scan_from_roots_jobs (heap_start, heap_end, object_ops, mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT);
+ enqueue_scan_from_roots_jobs (heap_start, heap_end, object_ops, FALSE);
TV_GETTIME (btv);
time_major_scan_roots += TV_ELAPSED (atv, btv);
+ /*
+ * We start the concurrent worker after pinning and after we scanned the roots
+ * in order to make sure that the worker does not finish before handling all
+ * the roots.
+ */
+ if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
+ if (precleaning_enabled) {
+ ScanJob *sj;
+ /* Mod union preclean job */
+ sj = (ScanJob*)sgen_thread_pool_job_alloc ("preclean mod union cardtable", job_mod_union_preclean, sizeof (ScanJob));
+ sj->ops = object_ops;
+ sgen_workers_start_all_workers (object_ops, &sj->job);
+ } else {
+ sgen_workers_start_all_workers (object_ops, NULL);
+ }
+ gray_queue_enable_redirect (WORKERS_DISTRIBUTE_GRAY_QUEUE);
+ }
+
if (mode == COPY_OR_MARK_FROM_ROOTS_FINISH_CONCURRENT) {
ScanJob *sj;
major_finish_copy_or_mark (CopyOrMarkFromRootsMode mode)
{
if (mode == COPY_OR_MARK_FROM_ROOTS_START_CONCURRENT) {
- /*
- * Prepare the pin queue for the next collection. Since pinning runs on the worker
- * threads we must wait for the jobs to finish before we can reset it.
- */
- sgen_workers_wait_for_jobs_finished ();
sgen_finish_pinning ();
sgen_pin_stats_reset ();
void
sgen_gc_unlock (void)
{
- gboolean try_free = sgen_try_free_some_memory;
- sgen_try_free_some_memory = FALSE;
mono_coop_mutex_unlock (&gc_mutex);
- if (try_free)
- mono_thread_hazardous_try_free_some ();
}
void
binary_protocol_world_restarted (generation, sgen_timestamp ());
- sgen_try_free_some_memory = TRUE;
-
if (sgen_client_bridge_need_processing ())
sgen_client_bridge_processing_finish (generation);
* 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>
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
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,
- HazardFreeLocking locking, HazardFreeContext context)
+ mono_thread_hazardous_queue_free (gpointer p, MonoHazardousFreeFunc free_func)
{
- int i;
+ DelayedFreeItem item = { p, free_func, HAZARD_FREE_MAY_LOCK };
- /* First try to free a few entries in the delayed free
- table. */
- for (i = 0; i < 3; ++i)
- try_free_delayed_free_item (context);
+ InterlockedIncrement (&hazardous_pointer_count);
- /* 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 };
+ mono_lock_free_array_queue_push (&delayed_free_queue, &item);
- ++hazardous_pointer_count;
+ guint32 queue_size = delayed_free_queue.num_used_entries;
+ if (queue_size && queue_size_cb)
+ queue_size_cb (queue_size);
+ }
- 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
* hazard-pointer.h: 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.
*/
#ifndef __MONO_HAZARD_POINTER_H__
#define __MONO_HAZARD_POINTER_H__
HAZARD_FREE_ASYNC_CTX,
} HazardFreeContext;
- void mono_thread_hazardous_free_or_queue (gpointer p, MonoHazardousFreeFunc free_func,
- HazardFreeLocking locking, HazardFreeContext context);
+ gboolean mono_thread_hazardous_try_free (gpointer p, MonoHazardousFreeFunc free_func);
+ void mono_thread_hazardous_queue_free (gpointer p, MonoHazardousFreeFunc free_func);
+
void mono_thread_hazardous_try_free_all (void);
void mono_thread_hazardous_try_free_some (void);
MonoThreadHazardPointers* mono_hazard_pointer_get (void);
int mono_hazard_pointer_save_for_signal_handler (void);
void mono_hazard_pointer_restore_for_signal_handler (int small_id);
+ typedef void (*MonoHazardFreeQueueSizeCallback)(size_t size);
+ void mono_hazard_pointer_install_free_queue_size_callback (MonoHazardFreeQueueSizeCallback cb);
+
void mono_thread_smr_init (void);
void mono_thread_smr_cleanup (void);
#endif /*__MONO_HAZARD_POINTER_H__*/
*
* (C) Copyright 2011 Novell, Inc
*
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
/*
g_assert (desc->in_use);
desc->in_use = FALSE;
free_sb (desc->sb, desc->block_size);
- mono_thread_hazardous_free_or_queue (desc, desc_enqueue_avail, HAZARD_FREE_NO_LOCK, HAZARD_FREE_ASYNC_CTX);
+ mono_thread_hazardous_try_free (desc, desc_enqueue_avail);
}
#else
MonoLockFreeQueue available_descs;
list_put_partial (Descriptor *desc)
{
g_assert (desc->anchor.data.state != STATE_FULL);
- mono_thread_hazardous_free_or_queue (desc, desc_put_partial, HAZARD_FREE_NO_LOCK, HAZARD_FREE_ASYNC_CTX);
+ mono_thread_hazardous_try_free (desc, desc_put_partial);
}
static void
desc_retire (desc);
} else {
g_assert (desc->heap->sc == sc);
- mono_thread_hazardous_free_or_queue (desc, desc_put_partial, HAZARD_FREE_NO_LOCK, HAZARD_FREE_ASYNC_CTX);
+ mono_thread_hazardous_try_free (desc, desc_put_partial);
if (++num_non_empty >= 2)
return;
}
*
* Copyright 2011 Novell, Inc (http://www.novell.com)
* Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include <config.h>
g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
/*now it's safe to free the thread info.*/
- mono_thread_hazardous_free_or_queue (info, free_thread_info, HAZARD_FREE_MAY_LOCK, HAZARD_FREE_SAFE_CTX);
+ mono_thread_hazardous_try_free (info, free_thread_info);
+ /* Pump the HP queue */
+ mono_thread_hazardous_try_free_some ();
+
mono_thread_small_id_free (small_id);
}