2 * sgen-gray.c: Gray queue management.
4 * Copyright 2001-2003 Ximian, Inc
5 * Copyright 2003-2010 Novell, Inc.
6 * Copyright (C) 2012 Xamarin Inc
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License 2.0 as published by the Free Software Foundation;
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License 2.0 along with this library; if not, write to the Free
19 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "metadata/sgen-gc.h"
25 #include "utils/mono-counters.h"
26 #include "sgen-protocol.h"
28 #ifdef HEAVY_STATISTICS
29 guint64 stat_gray_queue_section_alloc;
30 guint64 stat_gray_queue_section_free;
31 guint64 stat_gray_queue_enqueue_fast_path;
32 guint64 stat_gray_queue_dequeue_fast_path;
33 guint64 stat_gray_queue_enqueue_slow_path;
34 guint64 stat_gray_queue_dequeue_slow_path;
37 #define GRAY_QUEUE_LENGTH_LIMIT 64
39 #ifdef SGEN_CHECK_GRAY_OBJECT_SECTIONS
40 #define STATE_TRANSITION(s,o,n) do { \
42 if (InterlockedCompareExchange ((volatile int*)&(s)->state, (n), __old) != __old) \
43 g_assert_not_reached (); \
45 #define STATE_SET(s,v) (s)->state = (v)
46 #define STATE_ASSERT(s,v) g_assert ((s)->state == (v))
48 #define STATE_TRANSITION(s,o,n)
49 #define STATE_SET(s,v)
50 #define STATE_ASSERT(s,v)
54 sgen_gray_object_alloc_queue_section (SgenGrayQueue *queue)
56 GrayQueueSection *section;
58 HEAVY_STAT (stat_gray_queue_section_alloc ++);
60 if (queue->alloc_prepare_func)
61 queue->alloc_prepare_func (queue);
63 if (queue->free_list) {
64 /* Use the previously allocated queue sections if possible */
65 section = queue->free_list;
66 queue->free_list = section->next;
67 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FREE_LIST, GRAY_QUEUE_SECTION_STATE_FLOATING);
69 /* Allocate a new section */
70 section = sgen_alloc_internal (INTERNAL_MEM_GRAY_QUEUE);
71 STATE_SET (section, GRAY_QUEUE_SECTION_STATE_FLOATING);
74 section->size = SGEN_GRAY_QUEUE_SECTION_SIZE;
76 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
78 /* Link it with the others */
79 section->next = queue->first;
80 queue->first = section;
81 queue->cursor = section->entries - 1;
85 sgen_gray_object_free_queue_section (GrayQueueSection *section)
87 HEAVY_STAT (stat_gray_queue_section_free ++);
89 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_FREED);
90 sgen_free_internal (section, INTERNAL_MEM_GRAY_QUEUE);
94 * The following two functions are called in the inner loops of the
95 * collector, so they need to be as fast as possible. We have macros
96 * for them in sgen-gc.h.
100 sgen_gray_object_enqueue (SgenGrayQueue *queue, char *obj, mword desc)
102 GrayQueueEntry entry = SGEN_GRAY_QUEUE_ENTRY (obj, desc);
104 HEAVY_STAT (stat_gray_queue_enqueue_slow_path ++);
106 SGEN_ASSERT (9, obj, "enqueueing a null object");
107 //sgen_check_objref (obj);
109 #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
110 if (queue->enqueue_check_func)
111 queue->enqueue_check_func (obj);
114 if (G_UNLIKELY (!queue->first || queue->cursor == GRAY_LAST_CURSOR_POSITION (queue->first))) {
116 /* Set the current section size back to default, might have been changed by sgen_gray_object_dequeue_section */
117 queue->first->size = SGEN_GRAY_QUEUE_SECTION_SIZE;
120 sgen_gray_object_alloc_queue_section (queue);
122 STATE_ASSERT (queue->first, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
123 SGEN_ASSERT (9, queue->cursor <= GRAY_LAST_CURSOR_POSITION (queue->first), "gray queue %p overflow, first %p, cursor %p", queue, queue->first, queue->cursor);
124 *++queue->cursor = entry;
126 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
127 binary_protocol_gray_enqueue (queue, queue->cursor, obj);
132 sgen_gray_object_dequeue (SgenGrayQueue *queue)
134 GrayQueueEntry entry;
136 HEAVY_STAT (stat_gray_queue_dequeue_slow_path ++);
138 if (sgen_gray_object_queue_is_empty (queue)) {
143 STATE_ASSERT (queue->first, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
144 SGEN_ASSERT (9, queue->cursor >= GRAY_FIRST_CURSOR_POSITION (queue->first), "gray queue %p underflow, first %p, cursor %d", queue, queue->first, queue->cursor);
146 entry = *queue->cursor--;
148 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
149 binary_protocol_gray_dequeue (queue, queue->cursor + 1, entry.obj);
152 if (G_UNLIKELY (queue->cursor < GRAY_FIRST_CURSOR_POSITION (queue->first))) {
153 GrayQueueSection *section = queue->first;
154 queue->first = section->next;
155 section->next = queue->free_list;
157 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FREE_LIST);
159 queue->free_list = section;
160 queue->cursor = queue->first ? queue->first->entries + queue->first->size - 1 : NULL;
167 sgen_gray_object_dequeue_section (SgenGrayQueue *queue)
169 GrayQueueSection *section;
174 section = queue->first;
175 queue->first = section->next;
177 section->next = NULL;
178 section->size = queue->cursor - section->entries + 1;
180 queue->cursor = queue->first ? queue->first->entries + queue->first->size - 1 : NULL;
182 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FLOATING);
188 sgen_gray_object_enqueue_section (SgenGrayQueue *queue, GrayQueueSection *section)
190 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
193 queue->first->size = queue->cursor - queue->first->entries + 1;
195 section->next = queue->first;
196 queue->first = section;
197 queue->cursor = queue->first->entries + queue->first->size - 1;
198 #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
199 if (queue->enqueue_check_func) {
201 for (i = 0; i < section->size; ++i)
202 queue->enqueue_check_func (section->entries [i].obj);
208 sgen_gray_object_queue_trim_free_list (SgenGrayQueue *queue)
210 GrayQueueSection *section, *next;
212 for (section = queue->free_list; section && i < GRAY_QUEUE_LENGTH_LIMIT - 1; section = section->next) {
213 STATE_ASSERT (section, GRAY_QUEUE_SECTION_STATE_FREE_LIST);
218 while (section->next) {
219 next = section->next;
220 section->next = next->next;
221 STATE_TRANSITION (next, GRAY_QUEUE_SECTION_STATE_FREE_LIST, GRAY_QUEUE_SECTION_STATE_FLOATING);
222 sgen_gray_object_free_queue_section (next);
227 sgen_gray_object_queue_init (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func)
229 g_assert (sgen_gray_object_queue_is_empty (queue));
231 queue->alloc_prepare_func = NULL;
232 queue->alloc_prepare_data = NULL;
233 #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
234 queue->enqueue_check_func = enqueue_check_func;
237 /* Free the extra sections allocated during the last collection */
238 sgen_gray_object_queue_trim_free_list (queue);
242 invalid_prepare_func (SgenGrayQueue *queue)
244 g_assert_not_reached ();
248 sgen_gray_object_queue_init_invalid (SgenGrayQueue *queue)
250 sgen_gray_object_queue_init (queue, FALSE);
251 queue->alloc_prepare_func = invalid_prepare_func;
252 queue->alloc_prepare_data = NULL;
256 sgen_gray_queue_set_alloc_prepare (SgenGrayQueue *queue, GrayQueueAllocPrepareFunc alloc_prepare_func, void *data)
258 SGEN_ASSERT (0, !queue->alloc_prepare_func && !queue->alloc_prepare_data, "Can't set gray queue alloc-prepare twice");
259 queue->alloc_prepare_func = alloc_prepare_func;
260 queue->alloc_prepare_data = data;
264 sgen_gray_object_queue_init_with_alloc_prepare (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func,
265 GrayQueueAllocPrepareFunc alloc_prepare_func, void *data)
267 sgen_gray_object_queue_init (queue, enqueue_check_func);
268 sgen_gray_queue_set_alloc_prepare (queue, alloc_prepare_func, data);
272 sgen_gray_object_queue_deinit (SgenGrayQueue *queue)
274 g_assert (!queue->first);
275 while (queue->free_list) {
276 GrayQueueSection *next = queue->free_list->next;
277 STATE_TRANSITION (queue->free_list, GRAY_QUEUE_SECTION_STATE_FREE_LIST, GRAY_QUEUE_SECTION_STATE_FLOATING);
278 sgen_gray_object_free_queue_section (queue->free_list);
279 queue->free_list = next;
284 sgen_gray_object_queue_disable_alloc_prepare (SgenGrayQueue *queue)
286 queue->alloc_prepare_func = NULL;
287 queue->alloc_prepare_data = NULL;
291 lock_section_queue (SgenSectionGrayQueue *queue)
296 mono_mutex_lock (&queue->lock);
300 unlock_section_queue (SgenSectionGrayQueue *queue)
305 mono_mutex_unlock (&queue->lock);
309 sgen_section_gray_queue_init (SgenSectionGrayQueue *queue, gboolean locked, GrayQueueEnqueueCheckFunc enqueue_check_func)
311 g_assert (sgen_section_gray_queue_is_empty (queue));
313 queue->locked = locked;
315 mono_mutex_init_recursive (&queue->lock);
318 #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
319 queue->enqueue_check_func = enqueue_check_func;
324 sgen_section_gray_queue_is_empty (SgenSectionGrayQueue *queue)
326 return !queue->first;
330 sgen_section_gray_queue_dequeue (SgenSectionGrayQueue *queue)
332 GrayQueueSection *section;
334 lock_section_queue (queue);
337 section = queue->first;
338 queue->first = section->next;
340 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FLOATING);
342 section->next = NULL;
347 unlock_section_queue (queue);
353 sgen_section_gray_queue_enqueue (SgenSectionGrayQueue *queue, GrayQueueSection *section)
355 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
357 lock_section_queue (queue);
359 section->next = queue->first;
360 queue->first = section;
361 #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
362 if (queue->enqueue_check_func) {
364 for (i = 0; i < section->size; ++i)
365 queue->enqueue_check_func (section->entries [i].obj);
369 unlock_section_queue (queue);
373 sgen_init_gray_queues (void)
375 #ifdef HEAVY_STATISTICS
376 mono_counters_register ("Gray Queue alloc section", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_section_alloc);
377 mono_counters_register ("Gray Queue free section", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_section_free);
378 mono_counters_register ("Gray Queue enqueue fast path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_enqueue_fast_path);
379 mono_counters_register ("Gray Queue dequeue fast path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_dequeue_fast_path);
380 mono_counters_register ("Gray Queue enqueue slow path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_enqueue_slow_path);
381 mono_counters_register ("Gray Queue dequeue slow path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_dequeue_slow_path);