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 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include "mono/sgen/sgen-gc.h"
14 #include "mono/sgen/sgen-protocol.h"
16 #ifdef HEAVY_STATISTICS
17 guint64 stat_gray_queue_section_alloc;
18 guint64 stat_gray_queue_section_free;
19 guint64 stat_gray_queue_enqueue_fast_path;
20 guint64 stat_gray_queue_dequeue_fast_path;
21 guint64 stat_gray_queue_enqueue_slow_path;
22 guint64 stat_gray_queue_dequeue_slow_path;
25 #define GRAY_QUEUE_LENGTH_LIMIT 64
27 #ifdef SGEN_CHECK_GRAY_OBJECT_SECTIONS
28 #define STATE_TRANSITION(s,o,n) do { \
30 if (InterlockedCompareExchange ((volatile int*)&(s)->state, (n), __old) != __old) \
31 g_assert_not_reached (); \
33 #define STATE_SET(s,v) (s)->state = (v)
34 #define STATE_ASSERT(s,v) g_assert ((s)->state == (v))
36 #define STATE_TRANSITION(s,o,n)
37 #define STATE_SET(s,v)
38 #define STATE_ASSERT(s,v)
42 * Whenever we dispose a gray queue, we save its free list. Then, in the next collection,
43 * we reuse that free list for the new gray queue.
45 static GrayQueueSection *last_gray_queue_free_list;
48 sgen_gray_object_alloc_queue_section (SgenGrayQueue *queue)
50 GrayQueueSection *section;
52 if (queue->free_list) {
53 /* Use the previously allocated queue sections if possible */
54 section = queue->free_list;
55 queue->free_list = section->next;
56 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FREE_LIST, GRAY_QUEUE_SECTION_STATE_FLOATING);
58 HEAVY_STAT (stat_gray_queue_section_alloc ++);
60 /* Allocate a new section */
61 section = (GrayQueueSection *)sgen_alloc_internal (INTERNAL_MEM_GRAY_QUEUE);
62 STATE_SET (section, GRAY_QUEUE_SECTION_STATE_FLOATING);
65 section->size = SGEN_GRAY_QUEUE_SECTION_SIZE;
67 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
69 /* Link it with the others */
70 section->next = queue->first;
71 queue->first = section;
72 queue->cursor = section->entries - 1;
76 sgen_gray_object_free_queue_section (GrayQueueSection *section)
78 HEAVY_STAT (stat_gray_queue_section_free ++);
80 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_FREED);
81 sgen_free_internal (section, INTERNAL_MEM_GRAY_QUEUE);
85 * The following two functions are called in the inner loops of the
86 * collector, so they need to be as fast as possible. We have macros
87 * for them in sgen-gc.h.
91 sgen_gray_object_enqueue (SgenGrayQueue *queue, GCObject *obj, SgenDescriptor desc)
93 GrayQueueEntry entry = SGEN_GRAY_QUEUE_ENTRY (obj, desc);
95 HEAVY_STAT (stat_gray_queue_enqueue_slow_path ++);
97 SGEN_ASSERT (9, obj, "enqueueing a null object");
98 //sgen_check_objref (obj);
100 #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
101 if (queue->enqueue_check_func)
102 queue->enqueue_check_func (obj);
105 if (G_UNLIKELY (!queue->first || queue->cursor == GRAY_LAST_CURSOR_POSITION (queue->first))) {
107 /* Set the current section size back to default, might have been changed by sgen_gray_object_dequeue_section */
108 queue->first->size = SGEN_GRAY_QUEUE_SECTION_SIZE;
111 sgen_gray_object_alloc_queue_section (queue);
113 STATE_ASSERT (queue->first, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
114 SGEN_ASSERT (9, queue->cursor <= GRAY_LAST_CURSOR_POSITION (queue->first), "gray queue %p overflow, first %p, cursor %p", queue, queue->first, queue->cursor);
115 *++queue->cursor = entry;
117 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
118 binary_protocol_gray_enqueue (queue, queue->cursor, obj);
123 sgen_gray_object_dequeue (SgenGrayQueue *queue)
125 GrayQueueEntry entry;
127 HEAVY_STAT (stat_gray_queue_dequeue_slow_path ++);
129 if (sgen_gray_object_queue_is_empty (queue)) {
134 STATE_ASSERT (queue->first, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
135 SGEN_ASSERT (9, queue->cursor >= GRAY_FIRST_CURSOR_POSITION (queue->first), "gray queue %p underflow", queue);
137 entry = *queue->cursor--;
139 #ifdef SGEN_HEAVY_BINARY_PROTOCOL
140 binary_protocol_gray_dequeue (queue, queue->cursor + 1, entry.obj);
143 if (G_UNLIKELY (queue->cursor < GRAY_FIRST_CURSOR_POSITION (queue->first))) {
144 GrayQueueSection *section = queue->first;
145 queue->first = section->next;
146 section->next = queue->free_list;
148 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FREE_LIST);
150 queue->free_list = section;
151 queue->cursor = queue->first ? queue->first->entries + queue->first->size - 1 : NULL;
158 sgen_gray_object_dequeue_section (SgenGrayQueue *queue)
160 GrayQueueSection *section;
165 section = queue->first;
166 queue->first = section->next;
168 section->next = NULL;
169 section->size = queue->cursor - section->entries + 1;
171 queue->cursor = queue->first ? queue->first->entries + queue->first->size - 1 : NULL;
173 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FLOATING);
179 sgen_gray_object_enqueue_section (SgenGrayQueue *queue, GrayQueueSection *section)
181 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
184 queue->first->size = queue->cursor - queue->first->entries + 1;
186 section->next = queue->first;
187 queue->first = section;
188 queue->cursor = queue->first->entries + queue->first->size - 1;
189 #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
190 if (queue->enqueue_check_func) {
192 for (i = 0; i < section->size; ++i)
193 queue->enqueue_check_func (section->entries [i].obj);
199 sgen_gray_object_queue_trim_free_list (SgenGrayQueue *queue)
201 GrayQueueSection *section, *next;
203 for (section = queue->free_list; section && i < GRAY_QUEUE_LENGTH_LIMIT - 1; section = section->next) {
204 STATE_ASSERT (section, GRAY_QUEUE_SECTION_STATE_FREE_LIST);
209 while (section->next) {
210 next = section->next;
211 section->next = next->next;
212 STATE_TRANSITION (next, GRAY_QUEUE_SECTION_STATE_FREE_LIST, GRAY_QUEUE_SECTION_STATE_FLOATING);
213 sgen_gray_object_free_queue_section (next);
218 sgen_gray_object_queue_init (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func, gboolean reuse_free_list)
220 memset (queue, 0, sizeof (SgenGrayQueue));
222 #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
223 queue->enqueue_check_func = enqueue_check_func;
226 if (reuse_free_list) {
227 queue->free_list = last_gray_queue_free_list;
228 last_gray_queue_free_list = NULL;
233 sgen_gray_object_queue_dispose (SgenGrayQueue *queue)
235 SGEN_ASSERT (0, sgen_gray_object_queue_is_empty (queue), "Why are we disposing a gray queue that's not empty?");
237 /* Free the extra sections allocated during the last collection */
238 sgen_gray_object_queue_trim_free_list (queue);
240 SGEN_ASSERT (0, !last_gray_queue_free_list, "Are we disposing two gray queues after another?");
241 last_gray_queue_free_list = queue->free_list;
243 /* just to make sure */
244 memset (queue, 0, sizeof (SgenGrayQueue));
248 sgen_gray_object_queue_deinit (SgenGrayQueue *queue)
250 g_assert (!queue->first);
251 while (queue->free_list) {
252 GrayQueueSection *next = queue->free_list->next;
253 STATE_TRANSITION (queue->free_list, GRAY_QUEUE_SECTION_STATE_FREE_LIST, GRAY_QUEUE_SECTION_STATE_FLOATING);
254 sgen_gray_object_free_queue_section (queue->free_list);
255 queue->free_list = next;
260 lock_section_queue (SgenSectionGrayQueue *queue)
265 mono_os_mutex_lock (&queue->lock);
269 unlock_section_queue (SgenSectionGrayQueue *queue)
274 mono_os_mutex_unlock (&queue->lock);
278 sgen_section_gray_queue_init (SgenSectionGrayQueue *queue, gboolean locked, GrayQueueEnqueueCheckFunc enqueue_check_func)
280 g_assert (sgen_section_gray_queue_is_empty (queue));
282 queue->locked = locked;
284 mono_os_mutex_init_recursive (&queue->lock);
287 #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
288 queue->enqueue_check_func = enqueue_check_func;
293 sgen_section_gray_queue_is_empty (SgenSectionGrayQueue *queue)
295 return !queue->first;
299 sgen_section_gray_queue_dequeue (SgenSectionGrayQueue *queue)
301 GrayQueueSection *section;
303 lock_section_queue (queue);
306 section = queue->first;
307 queue->first = section->next;
309 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FLOATING);
311 section->next = NULL;
316 unlock_section_queue (queue);
322 sgen_section_gray_queue_enqueue (SgenSectionGrayQueue *queue, GrayQueueSection *section)
324 STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
326 lock_section_queue (queue);
328 section->next = queue->first;
329 queue->first = section;
330 #ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
331 if (queue->enqueue_check_func) {
333 for (i = 0; i < section->size; ++i)
334 queue->enqueue_check_func (section->entries [i].obj);
338 unlock_section_queue (queue);
342 sgen_init_gray_queues (void)
344 #ifdef HEAVY_STATISTICS
345 mono_counters_register ("Gray Queue alloc section", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_section_alloc);
346 mono_counters_register ("Gray Queue free section", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_section_free);
347 mono_counters_register ("Gray Queue enqueue fast path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_enqueue_fast_path);
348 mono_counters_register ("Gray Queue dequeue fast path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_dequeue_fast_path);
349 mono_counters_register ("Gray Queue enqueue slow path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_enqueue_slow_path);
350 mono_counters_register ("Gray Queue dequeue slow path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_dequeue_slow_path);