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