Merge pull request #216 from ilkerde/master
[mono.git] / mono / metadata / sgen-nursery-allocator.c
1 /*
2  * sgen-nursery-allocator.c: Nursery allocation code.
3  *
4  *
5  * Copyright 2009-2010 Novell, Inc.
6  *           2011 Rodrigo Kumpera
7  * 
8  * Copyright 2011 Xamarin Inc  (http://www.xamarin.com)
9  *
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining
12  * a copy of this software and associated documentation files (the
13  * "Software"), to deal in the Software without restriction, including
14  * without limitation the rights to use, copy, modify, merge, publish,
15  * distribute, sublicense, and/or sell copies of the Software, and to
16  * permit persons to whom the Software is furnished to do so, subject to
17  * the following conditions:
18  * 
19  * The above copyright notice and this permission notice shall be
20  * included in all copies or substantial portions of the Software.
21  * 
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29  */
30
31 /*
32  * The young generation is divided into fragments. This is because
33  * we can hand one fragments to a thread for lock-less fast alloc and
34  * because the young generation ends up fragmented anyway by pinned objects.
35  * Once a collection is done, a list of fragments is created. When doing
36  * thread local alloc we use smallish nurseries so we allow new threads to
37  * allocate memory from gen0 without triggering a collection. Threads that
38  * are found to allocate lots of memory are given bigger fragments. This
39  * should make the finalizer thread use little nursery memory after a while.
40  * We should start assigning threads very small fragments: if there are many
41  * threads the nursery will be full of reserved space that the threads may not
42  * use at all, slowing down allocation speed.
43  * Thread local allocation is done from areas of memory Hotspot calls Thread Local 
44  * Allocation Buffers (TLABs).
45  */
46 #include "config.h"
47 #ifdef HAVE_SGEN_GC
48
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52 #ifdef HAVE_PTHREAD_H
53 #include <pthread.h>
54 #endif
55 #ifdef HAVE_SEMAPHORE_H
56 #include <semaphore.h>
57 #endif
58 #include <stdio.h>
59 #include <string.h>
60 #include <signal.h>
61 #include <errno.h>
62 #include <assert.h>
63 #ifdef __MACH__
64 #undef _XOPEN_SOURCE
65 #endif
66 #ifdef __MACH__
67 #define _XOPEN_SOURCE
68 #endif
69
70 /*
71 TODO:
72 cleanup the code that readies the nursery for pinning/collection
73         this means removing all remaining memseting and use phony objects
74         all around. Have a separate fragments head so we can process all
75         of them together.
76 */
77 #include "metadata/sgen-gc.h"
78 #include "metadata/metadata-internals.h"
79 #include "metadata/class-internals.h"
80 #include "metadata/gc-internal.h"
81 #include "metadata/object-internals.h"
82 #include "metadata/threads.h"
83 #include "metadata/sgen-cardtable.h"
84 #include "metadata/sgen-protocol.h"
85 #include "metadata/sgen-archdep.h"
86 #include "metadata/sgen-bridge.h"
87 #include "metadata/mono-gc.h"
88 #include "metadata/method-builder.h"
89 #include "metadata/profiler-private.h"
90 #include "metadata/monitor.h"
91 #include "metadata/threadpool-internals.h"
92 #include "metadata/mempool-internals.h"
93 #include "metadata/marshal.h"
94 #include "utils/mono-mmap.h"
95 #include "utils/mono-time.h"
96 #include "utils/mono-semaphore.h"
97 #include "utils/mono-counters.h"
98 #include "utils/mono-proclib.h"
99 #include "utils/mono-threads.h"
100
101
102 typedef struct _Fragment Fragment;
103
104 struct _Fragment {
105         Fragment *next;
106         char *fragment_start;
107         char *fragment_next; /* the current soft limit for allocation */
108         char *fragment_end;
109         Fragment *next_in_order; /* We use a different entry for all active fragments so we can avoid SMR. */
110 };
111
112 typedef struct {
113         Fragment *alloc_head; /* List head to be used when allocating memory. Walk with fragment_next. */
114         Fragment *region_head; /* List head of the region used by this allocator. Walk with next_in_order. */
115 } FragmentAllocator;
116
117 /* Enable it so nursery allocation diagnostic data is collected */
118 //#define NALLOC_DEBUG 1
119
120 /* fragments that are free and ready to be used for allocation */
121 static FragmentAllocator nursery_allocator;
122
123 /* freeelist of fragment structures */
124 static Fragment *fragment_freelist = NULL;
125
126 /* Allocator cursors */
127 static char *nursery_last_pinned_end = NULL;
128
129 char *sgen_nursery_start;
130 char *sgen_nursery_end;
131
132 #ifdef USER_CONFIG
133 int sgen_nursery_size = (1 << 22);
134 #ifdef SGEN_ALIGN_NURSERY
135 int sgen_nursery_bits = 22;
136 #endif
137 #endif
138
139
140 static void sgen_clear_range (char *start, char *end);
141
142 #ifdef HEAVY_STATISTICS
143
144 static gint32 stat_wasted_bytes_trailer = 0;
145 static gint32 stat_wasted_bytes_small_areas = 0;
146 static gint32 stat_wasted_bytes_discarded_fragments = 0;
147 static gint32 stat_nursery_alloc_requests = 0;
148 static gint32 stat_alloc_iterations = 0;
149 static gint32 stat_alloc_retries = 0;
150
151 static gint32 stat_nursery_alloc_range_requests = 0;
152 static gint32 stat_alloc_range_iterations = 0;
153 static gint32 stat_alloc_range_retries = 0;
154
155 #endif
156
157 /************************************Nursery allocation debugging *********************************************/
158
159 #ifdef NALLOC_DEBUG
160
161 enum {
162         FIXED_ALLOC = 1,
163         RANGE_ALLOC,
164         PINNING,
165         BLOCK_ZEROING,
166         CLEAR_NURSERY_FRAGS
167 };
168
169 typedef struct {
170         char *address;
171         size_t size;
172         int reason;
173         int seq;
174         MonoNativeThreadId tid;
175 } AllocRecord;
176
177 #define ALLOC_RECORD_COUNT 128000
178
179
180 static AllocRecord *alloc_records;
181 static volatile int next_record;
182 static volatile int alloc_count;
183
184
185 static const char*
186 get_reason_name (AllocRecord *rec)
187 {
188         switch (rec->reason) {
189         case FIXED_ALLOC: return "fixed-alloc";
190         case RANGE_ALLOC: return "range-alloc";
191         case PINNING: return "pinning";
192         case BLOCK_ZEROING: return "block-zeroing";
193         case CLEAR_NURSERY_FRAGS: return "clear-nursery-frag";
194         default: return "invalid";
195         }
196 }
197
198 static void
199 reset_alloc_records (void)
200 {
201         next_record = 0;
202         alloc_count = 0;
203 }
204
205 static void
206 add_alloc_record (char *addr, size_t size, int reason)
207 {
208         int idx = InterlockedIncrement (&next_record) - 1;
209         alloc_records [idx].address = addr;
210         alloc_records [idx].size = size;
211         alloc_records [idx].reason = reason;
212         alloc_records [idx].seq = idx;
213         alloc_records [idx].tid = mono_native_thread_id_get ();
214 }
215
216 static int
217 comp_alloc_record (const void *_a, const void *_b)
218 {
219         const AllocRecord *a = _a;
220         const AllocRecord *b = _b;
221         if (a->address == b->address)
222                 return a->seq - b->seq;
223         return a->address - b->address;
224 }
225
226 #define rec_end(REC) ((REC)->address + (REC)->size)
227
228 void
229 dump_alloc_records (void)
230 {
231         int i;
232         qsort (alloc_records, next_record, sizeof (AllocRecord), comp_alloc_record);
233
234         printf ("------------------------------------DUMP RECORDS----------------------------\n");
235         for (i = 0; i < next_record; ++i) {
236                 AllocRecord *rec = alloc_records + i;
237                 printf ("obj [%p, %p] size %zd reason %s seq %d tid %zx\n", rec->address, rec_end (rec), rec->size, get_reason_name (rec), rec->seq, (size_t)rec->tid);
238         }
239 }
240
241 void
242 verify_alloc_records (void)
243 {
244         int i;
245         int total = 0;
246         int holes = 0;
247         int max_hole = 0;
248         AllocRecord *prev = NULL;
249
250         qsort (alloc_records, next_record, sizeof (AllocRecord), comp_alloc_record);
251         printf ("------------------------------------DUMP RECORDS- %d %d---------------------------\n", next_record, alloc_count);
252         for (i = 0; i < next_record; ++i) {
253                 AllocRecord *rec = alloc_records + i;
254                 int hole_size = 0;
255                 total += rec->size;
256                 if (prev) {
257                         if (rec_end (prev) > rec->address)
258                                 printf ("WE GOT OVERLAPPING objects %p and %p\n", prev->address, rec->address);
259                         if ((rec->address - rec_end (prev)) >= 8)
260                                 ++holes;
261                         hole_size = rec->address - rec_end (prev);
262                         max_hole = MAX (max_hole, hole_size);
263                 }
264                 printf ("obj [%p, %p] size %zd hole to prev %d reason %s seq %d tid %zx\n", rec->address, rec_end (rec), rec->size, hole_size, get_reason_name (rec), rec->seq, (size_t)rec->tid);
265                 prev = rec;
266         }
267         printf ("SUMMARY total alloc'd %d holes %d max_hole %d\n", total, holes, max_hole);
268 }
269
270 #endif
271
272 /*********************************************************************************/
273
274
275 static inline gpointer
276 mask (gpointer n, uintptr_t bit)
277 {
278         return (gpointer)(((uintptr_t)n) | bit);
279 }
280
281 static inline gpointer
282 unmask (gpointer p)
283 {
284         return (gpointer)((uintptr_t)p & ~(uintptr_t)0x3);
285 }
286
287 static inline uintptr_t
288 get_mark (gpointer n)
289 {
290         return (uintptr_t)n & 0x1;
291 }
292
293 /*MUST be called with world stopped*/
294 static Fragment*
295 alloc_fragment (void)
296 {
297         Fragment *frag = fragment_freelist;
298         if (frag) {
299                 fragment_freelist = frag->next_in_order;
300                 frag->next = frag->next_in_order = NULL;
301                 return frag;
302         }
303         frag = sgen_alloc_internal (INTERNAL_MEM_FRAGMENT);
304         frag->next = frag->next_in_order = NULL;
305         return frag;
306 }
307
308 static void
309 add_fragment (FragmentAllocator *allocator, char *start, char *end)
310 {
311         Fragment *fragment;
312
313         fragment = alloc_fragment ();
314         fragment->fragment_start = start;
315         fragment->fragment_next = start;
316         fragment->fragment_end = end;
317         fragment->next_in_order = fragment->next = unmask (allocator->region_head);
318
319         allocator->region_head = allocator->alloc_head = fragment;
320 }
321
322 static void
323 release_fragment_list (FragmentAllocator *allocator)
324 {
325         Fragment *last = allocator->region_head;
326         if (!last)
327                 return;
328
329         /* Find the last fragment in insert order */
330         for (; last->next_in_order; last = last->next_in_order) ;
331
332         last->next_in_order = fragment_freelist;
333         fragment_freelist = allocator->region_head;
334         allocator->alloc_head = allocator->region_head = NULL;
335 }
336
337 static Fragment**
338 find_previous_pointer_fragment (FragmentAllocator *allocator, Fragment *frag)
339 {
340         Fragment **prev;
341         Fragment *cur, *next;
342 #ifdef NALLOC_DEBUG
343         int count = 0;
344 #endif
345
346 try_again:
347         prev = &allocator->alloc_head;
348 #ifdef NALLOC_DEBUG
349         if (count++ > 5)
350                 printf ("retry count for fppf is %d\n", count);
351 #endif
352
353         cur = unmask (*prev);
354
355         while (1) {
356                 if (cur == NULL)
357                         return NULL;
358                 next = cur->next;
359
360                 /*
361                  * We need to make sure that we dereference prev below
362                  * after reading cur->next above, so we need a read
363                  * barrier.
364                  */
365                 mono_memory_read_barrier ();
366
367                 if (*prev != cur)
368                         goto try_again;
369
370                 if (!get_mark (next)) {
371                         if (cur == frag)
372                                 return prev;
373                         prev = &cur->next;
374                 } else {
375                         next = unmask (next);
376                         if (InterlockedCompareExchangePointer ((volatile gpointer*)prev, next, cur) != cur)
377                                 goto try_again;
378                         /*we must make sure that the next from cur->next happens after*/
379                         mono_memory_write_barrier ();
380                 }
381
382                 cur = mono_lls_pointer_unmask (next);
383         }
384         return NULL;
385 }
386
387 static gboolean
388 claim_remaining_size (Fragment *frag, char *alloc_end)
389 {
390         /* All space used, nothing to claim. */
391         if (frag->fragment_end <= alloc_end)
392                 return FALSE;
393
394         /* Try to alloc all the remaining space. */
395         return InterlockedCompareExchangePointer ((volatile gpointer*)&frag->fragment_next, frag->fragment_end, alloc_end) == alloc_end;
396 }
397
398 static void*
399 par_alloc_from_fragment (FragmentAllocator *allocator, Fragment *frag, size_t size)
400 {
401         char *p = frag->fragment_next;
402         char *end = p + size;
403
404         if (end > frag->fragment_end)
405                 return NULL;
406
407         /* p = frag->fragment_next must happen before */
408         mono_memory_barrier ();
409
410         if (InterlockedCompareExchangePointer ((volatile gpointer*)&frag->fragment_next, end, p) != p)
411                 return NULL;
412
413         if (frag->fragment_end - end < SGEN_MAX_NURSERY_WASTE) {
414                 Fragment *next, **prev_ptr;
415                 
416                 /*
417                  * Before we clean the remaining nursery, we must claim the remaining space
418                  * as it could end up been used by the range allocator since it can end up
419                  * allocating from this dying fragment as it doesn't respect SGEN_MAX_NURSERY_WASTE
420                  * when doing second chance allocation.
421                  */
422                 if (sgen_get_nursery_clear_policy () == CLEAR_AT_TLAB_CREATION && claim_remaining_size (frag, end)) {
423                         sgen_clear_range (end, frag->fragment_end);
424                         HEAVY_STAT (InterlockedExchangeAdd (&stat_wasted_bytes_trailer, frag->fragment_end - end));
425 #ifdef NALLOC_DEBUG
426                         add_alloc_record (end, frag->fragment_end - end, BLOCK_ZEROING);
427 #endif
428                 }
429
430                 prev_ptr = find_previous_pointer_fragment (allocator, frag);
431
432                 /*Use Michaels linked list remove*/
433
434                 /*prev_ptr will be null if the fragment was removed concurrently */
435                 while (prev_ptr) {
436                         next = frag->next;
437
438                         /*already deleted*/
439                         if (!get_mark (next)) {
440                                 /*frag->next read must happen before the first CAS*/
441                                 mono_memory_write_barrier ();
442
443                                 /*Fail if the next done is removed concurrently and its CAS wins */
444                                 if (InterlockedCompareExchangePointer ((volatile gpointer*)&frag->next, mask (next, 1), next) != next) {
445                                         continue;
446                                 }
447                         }
448
449                         /* The second CAS must happen after the first CAS or frag->next. */
450                         mono_memory_write_barrier ();
451
452                         /* Fail if the previous node was deleted and its CAS wins */
453                         if (InterlockedCompareExchangePointer ((volatile gpointer*)prev_ptr, next, frag) != frag) {
454                                 prev_ptr = find_previous_pointer_fragment (allocator, frag);
455                                 continue;
456                         }
457                         break;
458                 }
459         }
460
461         return p;
462 }
463
464 static void*
465 serial_alloc_from_fragment (Fragment **previous, Fragment *frag, size_t size)
466 {
467         char *p = frag->fragment_next;
468         char *end = p + size;
469
470         if (end > frag->fragment_end)
471                 return NULL;
472
473         frag->fragment_next = end;
474
475         if (frag->fragment_end - end < SGEN_MAX_NURSERY_WASTE) {
476                 *previous = frag->next;
477                 
478                 /* Clear the remaining space, pinning depends on this. FIXME move this to use phony arrays */
479                 memset (end, 0, frag->fragment_end - end);
480
481                 *previous = frag->next;
482         }
483
484         return p;
485 }
486
487 static void*
488 par_alloc (FragmentAllocator *allocator, size_t size)
489 {
490         Fragment *frag;
491
492 #ifdef NALLOC_DEBUG
493         InterlockedIncrement (&alloc_count);
494 #endif
495
496 restart:
497         for (frag = unmask (allocator->alloc_head); unmask (frag); frag = unmask (frag->next)) {
498                 HEAVY_STAT (InterlockedIncrement (&stat_alloc_iterations));
499
500                 if (size <= (frag->fragment_end - frag->fragment_next)) {
501                         void *p = par_alloc_from_fragment (allocator, frag, size);
502                         if (!p) {
503                                 HEAVY_STAT (InterlockedIncrement (&stat_alloc_retries));
504                                 goto restart;
505                         }
506 #ifdef NALLOC_DEBUG
507                         add_alloc_record (p, size, FIXED_ALLOC);
508 #endif
509                         return p;
510                 }
511         }
512         return NULL;
513 }
514
515 static void*
516 serial_alloc (FragmentAllocator *allocator, size_t size)
517 {
518         Fragment *frag;
519         Fragment **previous;
520 #ifdef NALLOC_DEBUG
521         InterlockedIncrement (&alloc_count);
522 #endif
523
524         previous = &allocator->alloc_head;
525
526         for (frag = *previous; frag; frag = *previous) {
527                 char *p = serial_alloc_from_fragment (previous, frag, size);
528
529                 HEAVY_STAT (InterlockedIncrement (&stat_alloc_iterations));
530
531                 if (p) {
532 #ifdef NALLOC_DEBUG
533                         add_alloc_record (p, size, FIXED_ALLOC);
534 #endif
535                         return p;
536                 }
537                 previous = &frag->next;
538         }
539         return NULL;
540 }
541
542 static void*
543 par_range_alloc (FragmentAllocator *allocator, size_t desired_size, size_t minimum_size, int *out_alloc_size)
544 {
545         Fragment *frag, *min_frag;
546 restart:
547         min_frag = NULL;
548
549 #ifdef NALLOC_DEBUG
550         InterlockedIncrement (&alloc_count);
551 #endif
552
553         for (frag = unmask (allocator->alloc_head); frag; frag = unmask (frag->next)) {
554                 int frag_size = frag->fragment_end - frag->fragment_next;
555
556                 HEAVY_STAT (InterlockedIncrement (&stat_alloc_range_iterations));
557
558                 if (desired_size <= frag_size) {
559                         void *p;
560                         *out_alloc_size = desired_size;
561
562                         p = par_alloc_from_fragment (allocator, frag, desired_size);
563                         if (!p) {
564                                 HEAVY_STAT (InterlockedIncrement (&stat_alloc_range_retries));
565                                 goto restart;
566                         }
567 #ifdef NALLOC_DEBUG
568                         add_alloc_record (p, desired_size, RANGE_ALLOC);
569 #endif
570                         return p;
571                 }
572                 if (minimum_size <= frag_size)
573                         min_frag = frag;
574         }
575
576         /* The second fragment_next read should be ordered in respect to the first code block */
577         mono_memory_barrier ();
578
579         if (min_frag) {
580                 void *p;
581                 int frag_size;
582
583                 frag_size = min_frag->fragment_end - min_frag->fragment_next;
584                 if (frag_size < minimum_size)
585                         goto restart;
586
587                 *out_alloc_size = frag_size;
588
589                 mono_memory_barrier ();
590                 p = par_alloc_from_fragment (allocator, min_frag, frag_size);
591
592                 /*XXX restarting here is quite dubious given this is already second chance allocation. */
593                 if (!p) {
594                         HEAVY_STAT (InterlockedIncrement (&stat_alloc_retries));
595                         goto restart;
596                 }
597 #ifdef NALLOC_DEBUG
598                 add_alloc_record (p, frag_size, RANGE_ALLOC);
599 #endif
600                 return p;
601         }
602
603         return NULL;
604 }
605
606 static void
607 clear_allocator_fragments (FragmentAllocator *allocator)
608 {
609         Fragment *frag;
610
611         for (frag = unmask (allocator->alloc_head); frag; frag = unmask (frag->next)) {
612                 DEBUG (4, fprintf (gc_debug_file, "Clear nursery frag %p-%p\n", frag->fragment_next, frag->fragment_end));
613                 sgen_clear_range (frag->fragment_next, frag->fragment_end);
614 #ifdef NALLOC_DEBUG
615                 add_alloc_record (frag->fragment_next, frag->fragment_end - frag->fragment_next, CLEAR_NURSERY_FRAGS);
616 #endif
617         }       
618 }
619
620 /* Clear all remaining nursery fragments */
621 void
622 sgen_clear_nursery_fragments (void)
623 {
624         if (sgen_get_nursery_clear_policy () == CLEAR_AT_TLAB_CREATION) {
625                 clear_allocator_fragments (&nursery_allocator);
626         }
627 }
628
629 static void
630 sgen_clear_range (char *start, char *end)
631 {
632         MonoArray *o;
633         size_t size = end - start;
634
635         if (size < sizeof (MonoArray)) {
636                 memset (start, 0, size);
637                 return;
638         }
639
640         o = (MonoArray*)start;
641         o->obj.vtable = sgen_get_array_fill_vtable ();
642         /* Mark this as not a real object */
643         o->obj.synchronisation = GINT_TO_POINTER (-1);
644         o->bounds = NULL;
645         o->max_length = size - sizeof (MonoArray);
646         sgen_set_nursery_scan_start (start);
647         g_assert (start + sgen_safe_object_get_size ((MonoObject*)o) == end);
648 }
649
650 void
651 sgen_nursery_allocator_prepare_for_pinning (void)
652 {
653         clear_allocator_fragments (&nursery_allocator);
654 }
655
656 static mword fragment_total = 0;
657 /*
658  * We found a fragment of free memory in the nursery: memzero it and if
659  * it is big enough, add it to the list of fragments that can be used for
660  * allocation.
661  */
662 static void
663 add_nursery_frag (FragmentAllocator *allocator, size_t frag_size, char* frag_start, char* frag_end)
664 {
665         DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %zd\n", frag_start, frag_end, frag_size));
666         binary_protocol_empty (frag_start, frag_size);
667         /* Not worth dealing with smaller fragments: need to tune */
668         if (frag_size >= SGEN_MAX_NURSERY_WASTE) {
669                 /* memsetting just the first chunk start is bound to provide better cache locality */
670                 if (sgen_get_nursery_clear_policy () == CLEAR_AT_GC)
671                         memset (frag_start, 0, frag_size);
672
673 #ifdef NALLOC_DEBUG
674                 /* XXX convert this into a flight record entry
675                 printf ("\tfragment [%p %p] size %zd\n", frag_start, frag_end, frag_size);
676                 */
677 #endif
678                 add_fragment (allocator, frag_start, frag_end);
679                 fragment_total += frag_size;
680         } else {
681                 /* Clear unused fragments, pinning depends on this */
682                 sgen_clear_range (frag_start, frag_end);
683                 HEAVY_STAT (InterlockedExchangeAdd (&stat_wasted_bytes_small_areas, frag_size));
684         }
685 }
686
687 mword
688 sgen_build_nursery_fragments (GCMemSection *nursery_section, void **start, int num_entries)
689 {
690         char *frag_start, *frag_end;
691         size_t frag_size;
692         int i;
693
694 #ifdef NALLOC_DEBUG
695         reset_alloc_records ();
696 #endif
697
698         release_fragment_list (&nursery_allocator);
699
700         frag_start = sgen_nursery_start;
701         fragment_total = 0;
702         /* clear scan starts */
703         memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
704         for (i = 0; i < num_entries; ++i) {
705                 frag_end = start [i];
706                 /* remove the pin bit from pinned objects */
707                 SGEN_UNPIN_OBJECT (frag_end);
708                 sgen_set_nursery_scan_start (frag_end);
709                 frag_size = frag_end - frag_start;
710                 if (frag_size)
711                         add_nursery_frag (&nursery_allocator, frag_size, frag_start, frag_end);
712                 frag_size = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)start [i]));
713 #ifdef NALLOC_DEBUG
714                 add_alloc_record (start [i], frag_size, PINNING);
715 #endif
716                 frag_start = (char*)start [i] + frag_size;
717         }
718         nursery_last_pinned_end = frag_start;
719         frag_end = sgen_nursery_end;
720         frag_size = frag_end - frag_start;
721         if (frag_size)
722                 add_nursery_frag (&nursery_allocator, frag_size, frag_start, frag_end);
723         if (!unmask (nursery_allocator.alloc_head)) {
724                 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", num_entries));
725                 for (i = 0; i < num_entries; ++i) {
726                         DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", start [i], sgen_safe_name (start [i]), sgen_safe_object_get_size (start [i])));
727                 }
728         }
729         return fragment_total;
730 }
731
732 char *
733 sgen_nursery_alloc_get_upper_alloc_bound (void)
734 {
735         char *p = NULL;
736         Fragment *frag;
737
738         for (frag = unmask (nursery_allocator.alloc_head); frag; frag = unmask (frag->next))
739                 p = MAX (p, frag->fragment_next);
740
741         return MAX (p, nursery_last_pinned_end);
742 }
743
744 /*** Nursery memory allocation ***/
745 void
746 sgen_nursery_retire_region (void *address, ptrdiff_t size)
747 {
748         HEAVY_STAT (InterlockedExchangeAdd (&stat_wasted_bytes_discarded_fragments, size));
749 }
750
751 gboolean
752 sgen_can_alloc_size (size_t size)
753 {
754         Fragment *frag;
755         size = SGEN_ALIGN_UP (size);
756
757         for (frag = unmask (nursery_allocator.alloc_head); frag; frag = unmask (frag->next)) {
758                 if ((frag->fragment_end - frag->fragment_next) >= size)
759                         return TRUE;
760         }
761         return FALSE;
762 }
763
764 void*
765 sgen_nursery_alloc (size_t size)
766 {
767         DEBUG (4, fprintf (gc_debug_file, "Searching nursery for size: %zd\n", size));
768         size = SGEN_ALIGN_UP (size);
769
770         HEAVY_STAT (InterlockedIncrement (&stat_nursery_alloc_requests));
771
772         return par_alloc (&nursery_allocator, size);
773 }
774
775 void*
776 sgen_nursery_alloc_range (size_t desired_size, size_t minimum_size, int *out_alloc_size)
777 {
778         DEBUG (4, fprintf (gc_debug_file, "Searching for byte range desired size: %zd minimum size %zd\n", desired_size, minimum_size));
779
780         HEAVY_STAT (InterlockedIncrement (&stat_nursery_alloc_range_requests));
781
782         return par_range_alloc (&nursery_allocator, desired_size, minimum_size, out_alloc_size);
783 }
784
785 /*** Initialization ***/
786
787 #ifdef HEAVY_STATISTICS
788
789 void
790 sgen_nursery_allocator_init_heavy_stats (void)
791 {
792         mono_counters_register ("bytes wasted trailer fragments", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wasted_bytes_trailer);
793         mono_counters_register ("bytes wasted small areas", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wasted_bytes_small_areas);
794         mono_counters_register ("bytes wasted discarded fragments", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_wasted_bytes_discarded_fragments);
795
796         mono_counters_register ("# nursery alloc requests", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_nursery_alloc_requests);
797         mono_counters_register ("# nursery alloc iterations", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_alloc_iterations);
798         mono_counters_register ("# nursery alloc retries", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_alloc_retries);
799
800         mono_counters_register ("# nursery alloc range requests", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_nursery_alloc_range_requests);
801         mono_counters_register ("# nursery alloc range iterations", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_alloc_range_iterations);
802         mono_counters_register ("# nursery alloc range restries", MONO_COUNTER_GC | MONO_COUNTER_INT, &stat_alloc_range_retries);
803 }
804
805 #endif
806
807 void
808 sgen_init_nursery_allocator (void)
809 {
810         sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FRAGMENT, sizeof (Fragment));
811 #ifdef NALLOC_DEBUG
812         alloc_records = sgen_alloc_os_memory (sizeof (AllocRecord) * ALLOC_RECORD_COUNT, TRUE);
813 #endif
814 }
815
816 void
817 sgen_nursery_allocator_set_nursery_bounds (char *start, char *end)
818 {
819         /* Setup the single first large fragment */
820         add_fragment (&nursery_allocator, start, end);
821         sgen_nursery_start = start;
822         sgen_nursery_end = end;
823 }
824
825 #endif