.TP
\fB(no-)lazy-sweep\fR
Enables or disables lazy sweep for the Mark&Sweep collector. If
-enabled, the sweep phase of the garbage collection is done piecemeal
-whenever the need arises, typically during nursery collections. Lazy
-sweeping is enabled by default.
+enabled, the sweeping of individual major heap blocks is done
+piecemeal whenever the need arises, typically during nursery
+collections. Lazy sweeping is enabled by default.
+.TP
+\fB(no-)concurrent-sweep\fR
+Enables or disables concurrent sweep for the Mark&Sweep collector. If
+enabled, the iteration of all major blocks to determine which ones can
+be freed and which ones have to be kept and swept, is done
+concurrently with the running program. Concurrent sweeping is enabled
+by default.
.TP
\fBstack-mark=\fImark-mode\fR
Specifies how application threads should be scanned. Options are
sgen-layout-stats.h \
sgen-qsort.c \
sgen-qsort.h \
+ sgen-thread-pool.c \
+ sgen-thread-pool.h \
sgen-tagged-pointer.h
libmonoruntime_la_SOURCES = $(common_sources) $(gc_dependent_sources) $(null_gc_sources) $(boehm_sources)
#include "metadata/sgen-pointer-queue.h"
#include "metadata/sgen-pinning.h"
#include "metadata/sgen-workers.h"
+#include "metadata/sgen-thread-pool.h"
#if defined(ARCH_MIN_MS_BLOCK_SIZE) && defined(ARCH_MIN_MS_BLOCK_SIZE_SHIFT)
#define MS_BLOCK_SIZE ARCH_MIN_MS_BLOCK_SIZE
static volatile int sweep_state = SWEEP_STATE_SWEPT;
static gboolean concurrent_mark;
+static gboolean concurrent_sweep = TRUE;
#define BLOCK_IS_TAGGED_HAS_REFERENCES(bl) SGEN_POINTER_IS_TAGGED_1 ((bl))
#define BLOCK_TAG_HAS_REFERENCES(bl) SGEN_POINTER_TAG_1 ((bl))
return !!tagged_block;
}
-static mono_native_thread_return_t
-sweep_loop_thread_func (void *dummy)
+static void
+sweep_job_func (SgenThreadPoolJob *job)
{
int block_index;
int num_blocks = num_major_sections_before_sweep;
- int small_id = mono_thread_info_register_small_id ();
SGEN_ASSERT (0, sweep_in_progress (), "Sweep thread called with wrong state");
SGEN_ASSERT (0, num_blocks <= allocated_blocks.next_slot, "How did we lose blocks?");
sgen_pointer_queue_remove_nulls (&allocated_blocks);
sweep_finish ();
-
- mono_thread_small_id_free (small_id);
-
- return NULL;
}
static void
set_sweep_state (SWEEP_STATE_SWEPT, SWEEP_STATE_COMPACTING);
}
-static MonoNativeThreadId sweep_loop_thread;
+static SgenThreadPoolJob sweep_job;
static void
major_sweep (void)
num_major_sections_before_sweep = num_major_sections;
num_major_sections_freed_in_sweep = 0;
- if (TRUE /*concurrent_mark*/) {
- /*
- * FIXME: We can't create a thread while the world is stopped because it
- * might deadlock. `finalizer-wait.exe` exposes this.
- */
- mono_native_thread_create (&sweep_loop_thread, sweep_loop_thread_func, NULL);
+ if (concurrent_sweep) {
+ sgen_thread_pool_job_init (&sweep_job, sweep_job_func);
+ sgen_thread_pool_job_enqueue (&sweep_job);
} else {
- sweep_loop_thread_func (NULL);
+ sweep_job_func (NULL);
}
}
} else if (!strcmp (opt, "no-lazy-sweep")) {
lazy_sweep = FALSE;
return TRUE;
+ } else if (!strcmp (opt, "concurrent-sweep")) {
+ concurrent_sweep = TRUE;
+ return TRUE;
+ } else if (!strcmp (opt, "no-concurrent-sweep")) {
+ concurrent_sweep = FALSE;
+ return TRUE;
}
return FALSE;
""
" evacuation-threshold=P (where P is a percentage, an integer in 0-100)\n"
" (no-)lazy-sweep\n"
+ " (no-)concurrent-sweep\n"
);
}
mono_mutex_init (&scanned_objects_list_lock);
#endif
+ if (concurrent_sweep)
+ sgen_thread_pool_init (1);
+
SGEN_ASSERT (0, SGEN_MAX_SMALL_OBJ_SIZE <= MS_BLOCK_FREE / 2, "MAX_SMALL_OBJ_SIZE must be at most MS_BLOCK_FREE / 2");
/*cardtable requires major pages to be 8 cards aligned*/
--- /dev/null
+/*
+ * sgen-thread-pool.c: Threadpool for all concurrent GC work.
+ *
+ * Copyright (C) 2015 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.
+ */
+
+#include "config.h"
+#ifdef HAVE_SGEN_GC
+
+#include "mono/metadata/sgen-gc.h"
+#include "mono/metadata/sgen-thread-pool.h"
+#include "mono/metadata/sgen-pointer-queue.h"
+#include "mono/utils/mono-mutex.h"
+#include "mono/utils/mono-threads.h"
+
+static mono_mutex_t lock;
+static mono_cond_t work_cond;
+static mono_cond_t done_cond;
+
+static MonoNativeThreadId thread;
+
+/* Only accessed with the lock held. */
+static SgenPointerQueue job_queue;
+
+enum {
+ STATE_WAITING,
+ STATE_IN_PROGRESS,
+ STATE_DONE
+};
+
+/* Assumes that the lock is held. */
+static SgenThreadPoolJob*
+get_job (void)
+{
+ for (size_t i = 0; i < job_queue.next_slot; ++i) {
+ SgenThreadPoolJob *job = job_queue.data [i];
+ if (job->state == STATE_WAITING)
+ return job;
+ }
+ return NULL;
+}
+
+/* Assumes that the lock is held. */
+static void
+remove_job (SgenThreadPoolJob *job)
+{
+ gboolean found = FALSE;
+ SGEN_ASSERT (0, job->state == STATE_DONE, "Why are we removing a job that's not done?");
+ for (size_t i = 0; i < job_queue.next_slot; ++i) {
+ if (job_queue.data [i] == job) {
+ job_queue.data [i] = NULL;
+ found = TRUE;
+ break;
+ }
+ }
+ SGEN_ASSERT (0, found, "Why is the job we're trying to remove not in the queue?");
+ sgen_pointer_queue_remove_nulls (&job_queue);
+}
+
+static mono_native_thread_return_t
+thread_func (void *arg)
+{
+ mono_thread_info_register_small_id ();
+
+ mono_mutex_lock (&lock);
+ for (;;) {
+ SgenThreadPoolJob *job;
+
+ while (!(job = get_job ()))
+ mono_cond_wait (&work_cond, &lock);
+ SGEN_ASSERT (0, job->state == STATE_WAITING, "The job we got is in the wrong state. Should be waiting.");
+ job->state = STATE_IN_PROGRESS;
+ mono_mutex_unlock (&lock);
+
+ job->func (job);
+
+ mono_mutex_lock (&lock);
+ SGEN_ASSERT (0, job->state == STATE_IN_PROGRESS, "The job should still be in progress.");
+ job->state = STATE_DONE;
+ remove_job (job);
+ /*
+ * Only the main GC thread will ever wait on the done condition, so we don't
+ * have to broadcast.
+ */
+ mono_cond_signal (&done_cond);
+ }
+}
+
+void
+sgen_thread_pool_init (int num_threads)
+{
+ SGEN_ASSERT (0, num_threads == 1, "We only support 1 thread pool thread for now.");
+
+ mono_mutex_init (&lock);
+ mono_cond_init (&work_cond, NULL);
+ mono_cond_init (&done_cond, NULL);
+
+ mono_native_thread_create (&thread, thread_func, NULL);
+}
+
+void
+sgen_thread_pool_job_init (SgenThreadPoolJob *job, SgenThreadPoolJobFunc func)
+{
+ job->state = STATE_WAITING;
+ job->func = func;
+}
+
+void
+sgen_thread_pool_job_enqueue (SgenThreadPoolJob *job)
+{
+ mono_mutex_lock (&lock);
+
+ sgen_pointer_queue_add (&job_queue, job);
+ /*
+ * FIXME: We could check whether there is a job in progress. If there is, there's
+ * no need to signal the condition, at least as long as we have only one thread.
+ */
+ mono_cond_signal (&work_cond);
+
+ mono_mutex_unlock (&lock);
+}
+
+void
+sgen_thread_pool_wait_for_all_jobs (void)
+{
+ mono_mutex_lock (&lock);
+
+ while (!sgen_pointer_queue_is_empty (&job_queue))
+ mono_cond_wait (&done_cond, &lock);
+
+ mono_mutex_unlock (&lock);
+}
+
+#endif
--- /dev/null
+/*
+ * sgen-thread-pool.h: Threadpool for all concurrent GC work.
+ *
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef __MONO_SGEN_THREAD_POOL_H__
+#define __MONO_SGEN_THREAD_POOL_H__
+
+typedef struct _SgenThreadPoolJob SgenThreadPoolJob;
+
+typedef void (*SgenThreadPoolJobFunc) (SgenThreadPoolJob *job);
+
+struct _SgenThreadPoolJob {
+ SgenThreadPoolJobFunc func;
+ volatile gint32 state;
+};
+
+void sgen_thread_pool_init (int num_threads);
+
+void sgen_thread_pool_job_init (SgenThreadPoolJob *job, SgenThreadPoolJobFunc func);
+void sgen_thread_pool_job_enqueue (SgenThreadPoolJob *job);
+
+void sgen_thread_pool_wait_for_all_jobs (void);
+
+#endif