2010-07-25 Carlos Alberto Cortez <calberto.cortez@gmail.com>
[mono.git] / mono / metadata / sgen-major-copying.c
1 /*
2  * sgen-major-copying.c: Simple generational GC.
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *
7  * Copyright 2005-2010 Novell, Inc (http://www.novell.com)
8  *
9  * Thread start/stop adapted from Boehm's GC:
10  * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
11  * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
12  * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
13  * Copyright (c) 2000-2004 by Hewlett-Packard Company.  All rights reserved.
14  *
15  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
16  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
17  *
18  * Permission is hereby granted to use or copy this program
19  * for any purpose,  provided the above notices are retained on all copies.
20  * Permission to modify the code and to distribute modified code is granted,
21  * provided the above notices are retained, and a notice that the code was
22  * modified is included with the above copyright notice.
23  *
24  *
25  * Copyright 2001-2003 Ximian, Inc
26  * Copyright 2003-2010 Novell, Inc.
27  * 
28  * Permission is hereby granted, free of charge, to any person obtaining
29  * a copy of this software and associated documentation files (the
30  * "Software"), to deal in the Software without restriction, including
31  * without limitation the rights to use, copy, modify, merge, publish,
32  * distribute, sublicense, and/or sell copies of the Software, and to
33  * permit persons to whom the Software is furnished to do so, subject to
34  * the following conditions:
35  * 
36  * The above copyright notice and this permission notice shall be
37  * included in all copies or substantial portions of the Software.
38  * 
39  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
40  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
41  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
42  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
43  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
44  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
45  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
46  */
47
48 #ifdef SGEN_PARALLEL_MARK
49 #error Parallel mark not supported in copying major collector
50 #endif
51
52 #define MAJOR_SECTION_SIZE              PINNED_CHUNK_SIZE
53 #define BLOCK_FOR_OBJECT(o)             ((Block*)(((mword)(o)) & ~(MAJOR_SECTION_SIZE - 1)))
54 #define MAJOR_SECTION_FOR_OBJECT(o)     ((GCMemSection*)BLOCK_FOR_OBJECT ((o)))
55
56 #define MAJOR_OBJ_IS_IN_TO_SPACE(o)     (MAJOR_SECTION_FOR_OBJECT ((o))->is_to_space)
57
58 static int num_major_sections = 0;
59
60 static GCMemSection *section_list = NULL;
61
62 /* pinned_chunk_list is used for allocations of objects that are never moved */
63 static PinnedChunk *pinned_chunk_list = NULL;
64
65 /*
66  * used when moving the objects
67  */
68 static char *to_space_bumper = NULL;
69 static char *to_space_top = NULL;
70 static GCMemSection *to_space_section = NULL;
71
72 #ifdef HEAVY_STATISTICS
73 static long stat_major_copy_object_failed_forwarded = 0;
74 static long stat_major_copy_object_failed_pinned = 0;
75 static long stat_major_copy_object_failed_large_pinned = 0;
76 static long stat_major_copy_object_failed_to_space = 0;
77 #endif
78
79 static gboolean
80 obj_is_from_pinned_alloc (char *p)
81 {
82         return BLOCK_FOR_OBJECT (p)->role == MEMORY_ROLE_PINNED;
83 }
84
85 static void
86 free_pinned_object (char *obj, size_t size)
87 {
88         PinnedChunk *chunk = (PinnedChunk*) BLOCK_FOR_OBJECT (obj);
89         void **p = (void**)obj;
90         int slot = slot_for_size (size);
91
92         g_assert (obj >= (char*)chunk->start_data && obj < ((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE));
93         *p = chunk->free_list [slot];
94         chunk->free_list [slot] = p;
95 }
96
97 /*
98  * Allocate a new section of memory to be used as old generation.
99  */
100 static GCMemSection*
101 alloc_major_section (void)
102 {
103         GCMemSection *section;
104         int scan_starts;
105
106         section = get_os_memory_aligned (MAJOR_SECTION_SIZE, MAJOR_SECTION_SIZE, TRUE);
107         section->next_data = section->data = (char*)section + SIZEOF_GC_MEM_SECTION;
108         g_assert (!((mword)section->data & 7));
109         section->size = MAJOR_SECTION_SIZE - SIZEOF_GC_MEM_SECTION;
110         section->end_data = section->data + section->size;
111         update_heap_boundaries (section->data, section->end_data);
112         total_alloc += section->size;
113         DEBUG (3, fprintf (gc_debug_file, "New major heap section: (%p-%p), total: %zd\n", section->data, section->end_data, total_alloc));
114         scan_starts = (section->size + SCAN_START_SIZE - 1) / SCAN_START_SIZE;
115         section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts, INTERNAL_MEM_SCAN_STARTS);
116         section->num_scan_start = scan_starts;
117         section->block.role = MEMORY_ROLE_GEN1;
118         section->is_to_space = TRUE;
119
120         /* add to the section list */
121         section->block.next = section_list;
122         section_list = section;
123
124         ++num_major_sections;
125
126         return section;
127 }
128
129 static void
130 free_major_section (GCMemSection *section)
131 {
132         DEBUG (3, fprintf (gc_debug_file, "Freed major section %p (%p-%p)\n", section, section->data, section->end_data));
133         free_internal_mem (section->scan_starts, INTERNAL_MEM_SCAN_STARTS);
134         free_os_memory (section, MAJOR_SECTION_SIZE);
135         total_alloc -= MAJOR_SECTION_SIZE - SIZEOF_GC_MEM_SECTION;
136
137         --num_major_sections;
138 }
139
140 static void
141 new_to_space_section (void)
142 {
143         /* FIXME: if the current to_space_section is empty, we don't
144            have to allocate a new one */
145
146         to_space_section = alloc_major_section ();
147         to_space_bumper = to_space_section->next_data;
148         to_space_top = to_space_section->end_data;
149 }
150
151 static void
152 to_space_set_next_data (void)
153 {
154         g_assert (to_space_bumper >= to_space_section->next_data && to_space_bumper <= to_space_section->end_data);
155         to_space_section->next_data = to_space_bumper;
156 }
157
158 static void
159 to_space_expand (void)
160 {
161         if (to_space_section) {
162                 g_assert (to_space_top == to_space_section->end_data);
163                 to_space_set_next_data ();
164         }
165
166         new_to_space_section ();
167 }
168
169 #define MAJOR_GET_COPY_OBJECT_SPACE(dest, size, refs) do {              \
170                 (dest) = to_space_bumper;                               \
171                 /* Make sure we have enough space available */          \
172                 if ((dest) + (size) > to_space_top) {                   \
173                         to_space_expand ();                             \
174                         (dest) = to_space_bumper;                       \
175                         DEBUG (8, g_assert ((dest) + (objsize) <= to_space_top)); \
176                 }                                                       \
177                 to_space_bumper += objsize;                             \
178                 DEBUG (8, g_assert (to_space_bumper <= to_space_top));  \
179                 to_space_section->scan_starts [((dest) - (char*)to_space_section->data)/SCAN_START_SIZE] = (dest); \
180         } while (0)
181
182 static void
183 unset_to_space (void)
184 {
185         /* between collections the to_space_bumper is invalidated
186            because degraded allocations might occur, so we set it to
187            NULL, just to make it explicit */
188         to_space_bumper = NULL;
189
190         /* don't unset to_space_section if we implement the FIXME in
191            new_to_space_section */
192         to_space_section = NULL;
193 }
194
195 static gboolean
196 major_is_object_live (char *obj)
197 {
198         mword objsize;
199
200         /* nursery */
201         if (ptr_in_nursery (obj))
202                 return FALSE;
203
204         objsize = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
205
206         /* LOS */
207         if (objsize > MAX_SMALL_OBJ_SIZE)
208                 return FALSE;
209
210         /* pinned chunk */
211         if (obj_is_from_pinned_alloc (obj))
212                 return FALSE;
213
214         /* now we know it's in a major heap section */
215         return MAJOR_SECTION_FOR_OBJECT (obj)->is_to_space;
216 }
217
218 /* size is a multiple of ALLOC_ALIGN */
219 static void*
220 major_alloc_small_pinned_obj (size_t size, gboolean has_references)
221 {
222         int slot;
223         void *res = NULL;
224         PinnedChunk *pchunk;
225         slot = slot_for_size (size);
226         /*g_print ("using slot %d for size %d (slot size: %d)\n", slot, size, freelist_sizes [slot]);*/
227         g_assert (size <= freelist_sizes [slot]);
228         for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->block.next) {
229                 void **p = pchunk->free_list [slot];
230                 if (p) {
231                         /*g_print ("found freelist for slot %d in chunk %p, returning %p, next %p\n", slot, pchunk, p, *p);*/
232                         pchunk->free_list [slot] = *p;
233                         res = p;
234                         goto found;
235                 }
236         }
237         for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->block.next) {
238                 res = get_chunk_freelist (pchunk, slot);
239                 if (res)
240                         goto found;
241         }
242         LOCK_INTERNAL_ALLOCATOR;
243         pchunk = alloc_pinned_chunk ();
244         UNLOCK_INTERNAL_ALLOCATOR;
245         /* FIXME: handle OOM */
246         pchunk->block.next = pinned_chunk_list;
247         pinned_chunk_list = pchunk;
248         res = get_chunk_freelist (pchunk, slot);
249  found:
250         memset (res, 0, size);
251         return res;
252 }
253
254 /*
255  * size is already rounded up and we hold the GC lock.
256  */
257 static void*
258 major_alloc_degraded (MonoVTable *vtable, size_t size)
259 {
260         GCMemSection *section;
261         void **p = NULL;
262         g_assert (size <= MAX_SMALL_OBJ_SIZE);
263         HEAVY_STAT (++stat_objects_alloced_degraded);
264         HEAVY_STAT (stat_bytes_alloced_degraded += size);
265         for (section = section_list; section; section = section->block.next) {
266                 if ((section->end_data - section->next_data) >= size) {
267                         p = (void**)section->next_data;
268                         break;
269                 }
270         }
271         if (!p) {
272                 section = alloc_major_section ();
273                 section->is_to_space = FALSE;
274                 /* FIXME: handle OOM */
275                 p = (void**)section->next_data;
276                 ++minor_collection_sections_alloced;
277         }
278         section->next_data += size;
279         degraded_mode += size;
280         DEBUG (3, fprintf (gc_debug_file, "Allocated (degraded) object %p, vtable: %p (%s), size: %zd in section %p\n", p, vtable, vtable->klass->name, size, section));
281         *p = vtable;
282         return p;
283 }
284
285 static void
286 major_copy_or_mark_object (void **obj_slot, GrayQueue *queue)
287 {
288         char *forwarded;
289         char *obj = *obj_slot;
290         mword objsize;
291
292         DEBUG (9, g_assert (current_collection_generation == GENERATION_OLD));
293
294         HEAVY_STAT (++stat_copy_object_called_major);
295
296         DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p from %p", obj, obj_slot));
297
298         /*
299          * obj must belong to one of:
300          *
301          * 1. the nursery
302          * 2. the LOS
303          * 3. a pinned chunk
304          * 4. a non-to-space section of the major heap
305          * 5. a to-space section of the major heap
306          *
307          * In addition, objects in 1, 2 and 4 might also be pinned.
308          * Objects in 1 and 4 might be forwarded.
309          *
310          * Before we can copy the object we must make sure that we are
311          * allowed to, i.e. that the object not pinned, not already
312          * forwarded and doesn't belong to the LOS, a pinned chunk, or
313          * a to-space section.
314          *
315          * We are usually called for to-space objects (5) when we have
316          * two remset entries for the same reference.  The first entry
317          * copies the object and updates the reference and the second
318          * calls us with the updated reference that points into
319          * to-space.  There might also be other circumstances where we
320          * get to-space objects.
321          */
322
323         if ((forwarded = object_is_forwarded (obj))) {
324                 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
325                 DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
326                 HEAVY_STAT (++stat_major_copy_object_failed_forwarded);
327                 *obj_slot = forwarded;
328                 return;
329         }
330         if (object_is_pinned (obj)) {
331                 DEBUG (9, g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr));
332                 DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
333                 HEAVY_STAT (++stat_major_copy_object_failed_pinned);
334                 return;
335         }
336
337         if (ptr_in_nursery (obj))
338                 goto copy;
339
340         /*
341          * At this point we know obj is not pinned, not forwarded and
342          * belongs to 2, 3, 4, or 5.
343          *
344          * LOS object (2) are simple, at least until we always follow
345          * the rule: if objsize > MAX_SMALL_OBJ_SIZE, pin the object
346          * and return it.  At the end of major collections, we walk
347          * the los list and if the object is pinned, it is marked,
348          * otherwise it can be freed.
349          *
350          * Pinned chunks (3) and major heap sections (4, 5) both
351          * reside in blocks, which are always aligned, so once we've
352          * eliminated LOS objects, we can just access the block and
353          * see whether it's a pinned chunk or a major heap section.
354          */
355
356         objsize = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
357
358         if (G_UNLIKELY (objsize > MAX_SMALL_OBJ_SIZE || obj_is_from_pinned_alloc (obj))) {
359                 if (object_is_pinned (obj))
360                         return;
361                 DEBUG (9, fprintf (gc_debug_file, " (marked LOS/Pinned %p (%s), size: %zd)\n", obj, safe_name (obj), objsize));
362                 binary_protocol_pin (obj, (gpointer)LOAD_VTABLE (obj), safe_object_get_size ((MonoObject*)obj));
363                 pin_object (obj);
364                 GRAY_OBJECT_ENQUEUE (queue, obj);
365                 HEAVY_STAT (++stat_major_copy_object_failed_large_pinned);
366                 return;
367         }
368
369         /*
370          * Now we know the object is in a major heap section.  All we
371          * need to do is check whether it's already in to-space (5) or
372          * not (4).
373          */
374         if (MAJOR_OBJ_IS_IN_TO_SPACE (obj)) {
375                 DEBUG (9, g_assert (objsize <= MAX_SMALL_OBJ_SIZE));
376                 DEBUG (9, fprintf (gc_debug_file, " (already copied)\n"));
377                 HEAVY_STAT (++stat_major_copy_object_failed_to_space);
378                 return;
379         }
380
381  copy:
382         HEAVY_STAT (++stat_objects_copied_major);
383
384         *obj_slot = copy_object_no_checks (obj, queue);
385 }
386
387 /* FIXME: later reduce code duplication here with build_nursery_fragments().
388  * We don't keep track of section fragments for non-nursery sections yet, so
389  * just memset to 0.
390  */
391 static void
392 build_section_fragments (GCMemSection *section)
393 {
394         int i;
395         char *frag_start, *frag_end;
396         size_t frag_size;
397
398         /* clear scan starts */
399         memset (section->scan_starts, 0, section->num_scan_start * sizeof (gpointer));
400         frag_start = section->data;
401         section->next_data = section->data;
402         for (i = section->pin_queue_start; i < section->pin_queue_end; ++i) {
403                 frag_end = pin_queue [i];
404                 /* remove the pin bit from pinned objects */
405                 unpin_object (frag_end);
406                 if (frag_end >= section->data + section->size) {
407                         frag_end = section->data + section->size;
408                 } else {
409                         section->scan_starts [((char*)frag_end - (char*)section->data)/SCAN_START_SIZE] = frag_end;
410                 }
411                 frag_size = frag_end - frag_start;
412                 if (frag_size) {
413                         binary_protocol_empty (frag_start, frag_size);
414                         memset (frag_start, 0, frag_size);
415                 }
416                 frag_size = ALIGN_UP (safe_object_get_size ((MonoObject*)pin_queue [i]));
417                 frag_start = (char*)pin_queue [i] + frag_size;
418                 section->next_data = MAX (section->next_data, frag_start);
419         }
420         frag_end = section->end_data;
421         frag_size = frag_end - frag_start;
422         if (frag_size) {
423                 binary_protocol_empty (frag_start, frag_size);
424                 memset (frag_start, 0, frag_size);
425         }
426 }
427
428 static void
429 scan_pinned_objects (IterateObjectCallbackFunc callback, void *callback_data)
430 {
431         PinnedChunk *chunk;
432         int i, obj_size;
433         char *p, *endp;
434         void **ptr;
435         void *end_chunk;
436         for (chunk = pinned_chunk_list; chunk; chunk = chunk->block.next) {
437                 end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
438                 DEBUG (6, fprintf (gc_debug_file, "Scanning pinned chunk %p (range: %p-%p)\n", chunk, chunk->start_data, end_chunk));
439                 for (i = 0; i < chunk->num_pages; ++i) {
440                         obj_size = chunk->page_sizes [i];
441                         if (!obj_size)
442                                 continue;
443                         p = i? (char*)chunk + i * FREELIST_PAGESIZE: chunk->start_data;
444                         endp = i? p + FREELIST_PAGESIZE: (char*)chunk + FREELIST_PAGESIZE;
445                         DEBUG (6, fprintf (gc_debug_file, "Page %d (size: %d, range: %p-%p)\n", i, obj_size, p, endp));
446                         while (p + obj_size <= endp) {
447                                 ptr = (void**)p;
448                                 DEBUG (9, fprintf (gc_debug_file, "Considering %p (vtable: %p)\n", ptr, *ptr));
449                                 /* if the first word (the vtable) is outside the chunk we have an object */
450                                 if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk))
451                                         callback ((char*)ptr, obj_size, callback_data);
452                                 p += obj_size;
453                         }
454                 }
455         }
456 }
457
458 /*
459  * the array of pointers from @start to @end contains conservative
460  * pointers to objects inside @chunk: mark each referenced object
461  * with the PIN bit.
462  */
463 static void
464 mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end, GrayQueue *queue)
465 {
466         for (; start < end; start++) {
467                 char *addr = *start;
468                 int offset = (char*)addr - (char*)chunk;
469                 int page = offset / FREELIST_PAGESIZE;
470                 int obj_offset = page == 0? offset - ((char*)chunk->start_data - (char*)chunk): offset % FREELIST_PAGESIZE;
471                 int slot_size = chunk->page_sizes [page];
472                 void **ptr;
473                 /* the page is not allocated */
474                 if (!slot_size)
475                         continue;
476                 /* would be faster if we restrict the sizes to power of two,
477                  * but that's a waste of memory: need to measure. it could reduce
478                  * fragmentation since there are less pages needed, if for example
479                  * someone interns strings of each size we end up with one page per
480                  * interned string (still this is just ~40 KB): with more fine-grained sizes
481                  * this increases the number of used pages.
482                  */
483                 if (page == 0) {
484                         obj_offset /= slot_size;
485                         obj_offset *= slot_size;
486                         addr = (char*)chunk->start_data + obj_offset;
487                 } else {
488                         obj_offset /= slot_size;
489                         obj_offset *= slot_size;
490                         addr = (char*)chunk + page * FREELIST_PAGESIZE + obj_offset;
491                 }
492                 ptr = (void**)addr;
493                 /* if the vtable is inside the chunk it's on the freelist, so skip */
494                 if (*ptr && (*ptr < (void*)chunk->start_data || *ptr > (void*)((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE))) {
495                         binary_protocol_pin (addr, (gpointer)LOAD_VTABLE (addr), safe_object_get_size ((MonoObject*)addr));
496                         if (heap_dump_file && !object_is_pinned (addr))
497                                 pin_stats_register_object ((char*) addr, safe_object_get_size ((MonoObject*) addr));
498                         pin_object (addr);
499                         GRAY_OBJECT_ENQUEUE (queue, addr);
500                         DEBUG (6, fprintf (gc_debug_file, "Marked pinned object %p (%s) from roots\n", addr, safe_name (addr)));
501                 }
502         }
503 }
504
505 static void
506 sweep_pinned_objects_callback (char *ptr, size_t size, void *data)
507 {
508         if (object_is_pinned (ptr)) {
509                 unpin_object (ptr);
510                 DEBUG (6, fprintf (gc_debug_file, "Unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
511         } else {
512                 DEBUG (6, fprintf (gc_debug_file, "Freeing unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
513                 free_pinned_object (ptr, size);
514         }
515 }
516
517 static void
518 sweep_pinned_objects (void)
519 {
520         scan_pinned_objects (sweep_pinned_objects_callback, NULL);
521 }
522
523 static void
524 major_iterate_objects (gboolean non_pinned, gboolean pinned, IterateObjectCallbackFunc callback, void *data)
525 {
526         if (non_pinned) {
527                 GCMemSection *section;
528                 for (section = section_list; section; section = section->block.next)
529                         scan_area_with_callback (section->data, section->end_data, callback, data);
530         }
531         if (pinned)
532                 scan_pinned_objects (callback, data);
533 }
534
535 static void
536 major_free_non_pinned_object (char *obj, size_t size)
537 {
538         memset (obj, 0, size);
539 }
540
541 static void
542 major_find_pin_queue_start_ends (GrayQueue *queue)
543 {
544         GCMemSection *section;
545         PinnedChunk *chunk;
546
547         for (section = section_list; section; section = section->block.next)
548                 find_section_pin_queue_start_end (section);
549
550         /* look for pinned addresses for pinned-alloc objects */
551         DEBUG (6, fprintf (gc_debug_file, "Pinning from pinned-alloc objects\n"));
552         for (chunk = pinned_chunk_list; chunk; chunk = chunk->block.next) {
553                 int start, end;
554                 find_optimized_pin_queue_area (chunk->start_data, (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE, &start, &end);
555                 if (start != end)
556                         mark_pinned_from_addresses (chunk, pin_queue + start, pin_queue + end, queue);
557         }
558 }
559
560 static void
561 major_pin_objects (GrayQueue *queue)
562 {
563         GCMemSection *section;
564
565         for (section = section_list; section; section = section->block.next)
566                 pin_objects_in_section (section, queue);
567 }
568
569 static void
570 major_init_to_space (void)
571 {
572         new_to_space_section ();
573 }
574
575 static void
576 major_sweep (void)
577 {
578         GCMemSection *section, *prev_section;
579
580         to_space_set_next_data ();
581         unset_to_space ();
582
583         /* unpin objects from the pinned chunks and free the unmarked ones */
584         sweep_pinned_objects ();
585
586         /* free the unused sections */
587         prev_section = NULL;
588         for (section = section_list; section;) {
589                 /* to_space doesn't need handling here */
590                 if (section->is_to_space) {
591                         section->is_to_space = FALSE;
592                         prev_section = section;
593                         section = section->block.next;
594                         continue;
595                 }
596                 /* no pinning object, so the section is free */
597                 if (section->pin_queue_start == section->pin_queue_end) {
598                         GCMemSection *to_free;
599                         if (prev_section)
600                                 prev_section->block.next = section->block.next;
601                         else
602                                 section_list = section->block.next;
603                         to_free = section;
604                         section = section->block.next;
605                         free_major_section (to_free);
606                         continue;
607                 } else {
608                         DEBUG (6, fprintf (gc_debug_file, "Section %p has still pinned objects (%d)\n", section, section->pin_queue_end - section->pin_queue_start));
609                         build_section_fragments (section);
610                 }
611                 prev_section = section;
612                 section = section->block.next;
613         }
614 }
615
616 static void
617 major_check_scan_starts (void)
618 {
619         GCMemSection *section;
620         for (section = section_list; section; section = section->block.next)
621                 check_section_scan_starts (section);
622 }
623
624 static void
625 major_dump_heap (void)
626 {
627         GCMemSection *section;
628         for (section = section_list; section; section = section->block.next)
629                 dump_section (section, "old");
630         /* FIXME: dump pinned sections, too */
631 }
632
633 static gint64
634 major_get_used_size (void)
635 {
636         gint64 tot = 0;
637         GCMemSection *section;
638         for (section = section_list; section; section = section->block.next) {
639                 /* this is approximate... */
640                 tot += section->next_data - section->data;
641         }
642         return tot;
643 }
644
645 static void
646 major_init (void)
647 {
648 #ifdef HEAVY_STATISTICS
649         mono_counters_register ("# major copy_object() failed forwarded", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_copy_object_failed_forwarded);
650         mono_counters_register ("# major copy_object() failed pinned", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_copy_object_failed_pinned);
651         mono_counters_register ("# major copy_object() failed large or pinned chunk", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_copy_object_failed_large_pinned);
652         mono_counters_register ("# major copy_object() failed to space", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_copy_object_failed_to_space);
653 #endif
654 }
655
656 /* only valid during minor collections */
657 static int old_num_major_sections;
658
659 static void
660 major_start_nursery_collection (void)
661 {
662         old_num_major_sections = num_major_sections;
663
664         if (!to_space_section) {
665                 new_to_space_section ();
666         } else {
667                 /* we might have done degraded allocation since the
668                    last collection */
669                 g_assert (to_space_bumper <= to_space_section->next_data);
670                 to_space_bumper = to_space_section->next_data;
671
672                 to_space_section->is_to_space = TRUE;
673         }
674 }
675
676 static void
677 major_finish_nursery_collection (void)
678 {
679         GCMemSection *section;
680         int sections_alloced;
681
682         to_space_set_next_data ();
683
684         for (section = section_list; section; section = section->block.next)
685                 section->is_to_space = FALSE;
686
687         sections_alloced = num_major_sections - old_num_major_sections;
688         minor_collection_sections_alloced += sections_alloced;
689 }
690
691 static void
692 major_finish_major_collection (void)
693 {
694 }
695
696 static gboolean
697 major_ptr_is_in_non_pinned_space (char *ptr)
698 {
699         GCMemSection *section;
700         for (section = section_list; section;) {
701                 if (ptr >= section->data && ptr < section->data + section->size)
702                         return TRUE;
703                 section = section->block.next;
704         }
705         return FALSE;
706 }
707
708 static void
709 major_report_pinned_memory_usage (void)
710 {
711         PinnedChunk *chunk;
712         int i = 0;
713         for (chunk = pinned_chunk_list; chunk; chunk = chunk->block.next)
714                 report_pinned_chunk (chunk, i++);
715 }