8fec30f9190f98939887ae1565d7c57b8560f8f1
[mono.git] / mono / metadata / sgen-gc.c
1 /*
2  * sgen-gc.c: Simple generational GC.
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *
7  * Copyright (C) 2005-2006 Novell, Inc
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  * All the rest of the code is LGPL.
25  *
26  * Important: allocation provides always zeroed memory, having to do
27  * a memset after allocation is deadly for performance.
28  * Memory usage at startup is currently as follows:
29  * 64 KB pinned space
30  * 64 KB internal space
31  * size of nursery
32  * We should provide a small memory config with half the sizes
33  *
34  * We currently try to make as few mono assumptions as possible:
35  * 1) 2-word header with no GC pointers in it (firts vtable, second to store the
36  *    forwarding ptr)
37  * 2) gc descriptor is the second word in the vtable (first word in the class)
38  * 3) 8 byte alignment is the minimum and enough (not true for special structures, FIXME)
39  * 4) there is a function to get an object's size and the number of
40  *    elements in an array.
41  * 5) we know the special way bounds are allocated for complex arrays
42  *
43  * Always try to keep stack usage to a minimum: no recursive behaviour
44  * and no large stack allocs.
45  *
46  * General description.
47  * Objects are initially allocated in a nursery using a fast bump-pointer technique.
48  * When the nursery is full we start a nursery collection: this is performed with a
49  * copying GC.
50  * When the old generation is full we start a copying GC of the old generation as well:
51  * this will be changed to mark/compact in the future.
52  * The things that complicate this description are:
53  * *) pinned objects: we can't move them so we need to keep track of them
54  * *) no precise info of the thread stacks and registers: we need to be able to
55  *    quickly find the objects that may be referenced conservatively and pin them
56  *    (this makes the first issues more important)
57  * *) large objects are too expensive to be dealt with using copying GC: we handle them
58  *    with mark/sweep during major collections
59  * *) some objects need to not move even if they are small (interned strings, Type handles):
60  *    we use mark/sweep for them, too: they are not allocated in the nursery, but inside
61  *    PinnedChunks regions
62  */
63
64 /*
65  * TODO:
66  *) change the jit to emit write barrier calls when needed (we
67   can have specialized write barriers): done with icalls, still need to
68   use some specialized barriers
69  *) we could have a function pointer in MonoClass to implement
70   customized write barriers for value types
71  *) the write barrier code could be isolated in a couple of functions: when a
72   thread is stopped if it's inside the barrier it is let go again
73   until we stop outside of them (not really needed, see below GC-safe points)
74  *) investigate the stuff needed to advance a thread to a GC-safe
75   point (single-stepping, read from unmapped memory etc) and implement it
76   Not needed yet: since we treat the objects reachable from the stack/regs as
77   roots, we store the ptr and exec the write barrier so there is no race.
78   We may need this to solve the issue with setting the length of arrays and strings.
79   We may need this also for handling precise info on stacks, even simple things
80   as having uninitialized data on the stack and having to wait for the prolog
81   to zero it. Not an issue for the last frame that we scan conservatively.
82   We could always not trust the value in the slots anyway.
83  *) make the jit info table lock free
84  *) modify the jit to save info about references in stack locations:
85   this can be done just for locals as a start, so that at least
86   part of the stack is handled precisely.
87  *) Make the debug printf stuff thread and signal safe.
88  *) test/fix 64 bit issues
89  *) test/fix endianess issues
90  *) port to non-Linux
91  *) add batch moving profile info
92  *) add more timing info
93  *) there is a possible race when an array or string is created: the vtable is set,
94     but the length is set only later so if the GC needs to scan the object in that window,
95     it won't get the correct size for the object. The object can't have references and it will
96     be pinned, but a free memory fragment may be created that overlaps with it.
97     We should change the array max_length field to be at the same offset as the string length:
98     this way we can have a single special alloc function for them that sets the length.
99     Multi-dim arrays have the same issue for rank == 1 for the bounds data.
100  *) implement a card table as the write barrier instead of remembered sets?
101  *) some sort of blacklist support?
102  *) fin_ready_list is part of the root set, too
103  *) consider lowering the large object min size to 16/32KB or so and benchmark
104  *) once mark-compact is implemented we could still keep the
105     copying collector for the old generation and use it if we think
106     it is better (small heaps and no pinning object in the old
107     generation)
108   *) avoid the memory store from copy_object when not needed.
109   *) optimize the write barriers fastpath to happen in managed code
110   *) add an option to mmap the whole heap in one chunk: it makes for many
111      simplifications in the checks (put the nursery at the top and just use a single
112      check for inclusion/exclusion): the issue this has is that on 32 bit systems it's
113      not flexible (too much of the address space may be used by default or we can't
114      increase the heap as needed) and we'd need a race-free mechanism to return memory
115      back to the system (mprotect(PROT_NONE) will still keep the memory allocated if it
116      was written to, munmap is needed, but the following mmap may not find the same segment
117      free...)
118    *) memzero the fragments after restarting the world and optionally a smaller chunk at a time
119    *) an additional strategy to realloc/expand the nursery when fully pinned is to start
120       allocating objects in the old generation. This means that we can't optimize away write
121       barrier calls in ctors (but that is not valid for other reasons, too).
122    *) add write barriers to the Clone methods
123  */
124 #include "config.h"
125 #ifdef HAVE_SGEN_GC
126
127 #include <unistd.h>
128 #include <stdio.h>
129 #include <string.h>
130 #include <semaphore.h>
131 #include <signal.h>
132 #include <errno.h>
133 #include <assert.h>
134 #include <sys/types.h>
135 #include <sys/stat.h>
136 #include <sys/mman.h>
137 #include <sys/time.h>
138 #include <time.h>
139 #include <fcntl.h>
140 #include "metadata/metadata-internals.h"
141 #include "metadata/class-internals.h"
142 #include "metadata/gc-internal.h"
143 #include "metadata/object-internals.h"
144 #include "metadata/threads.h"
145 #include "metadata/sgen-gc.h"
146 #include "metadata/mono-gc.h"
147 #include "utils/mono-mmap.h"
148
149
150 /*
151  * ######################################################################
152  * ########  Types and constants used by the GC.
153  * ######################################################################
154  */
155 #if SIZEOF_VOID_P == 4
156 typedef guint32 mword;
157 #else
158 typedef guint64 mword;
159 #endif
160
161 static int gc_initialized = 0;
162 static int gc_debug_level = 0;
163 static FILE* gc_debug_file;
164
165 void
166 mono_gc_flush_info (void)
167 {
168         fflush (gc_debug_file);
169 }
170
171 #define MAX_DEBUG_LEVEL 9
172 #define DEBUG(level,a) do {if ((level) <= MAX_DEBUG_LEVEL && (level) <= gc_debug_level) a;} while (0)
173
174 #define TV_DECLARE(name) struct timeval name
175 #define TV_GETTIME(tv) gettimeofday (&(tv), NULL)
176 #define TV_ELAPSED(start,end) (int)((((end).tv_sec - (start).tv_sec) * 1000000) + end.tv_usec - start.tv_usec)
177
178 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
179
180 enum {
181         MEMORY_ROLE_GEN0,
182         MEMORY_ROLE_GEN1,
183         MEMORY_ROLE_GEN2,
184         MEMORY_ROLE_FIXED,
185         MEMORY_ROLE_INTERNAL
186 };
187
188 /* each request from the OS ends up in a GCMemSection */
189 typedef struct _GCMemSection GCMemSection;
190 struct _GCMemSection {
191         GCMemSection *next;
192         char *data;
193         mword size;
194         /* pointer where more data could be allocated if it fits */
195         char *next_data;
196         char *end_data;
197         /* 
198          * scan starts is an array of pointers to objects equally spaced in the allocation area
199          * They let use quickly find pinned objects from pinning pointers.
200          */
201         char **scan_starts;
202         /* in major collections indexes in the pin_queue for objects that pin this section */
203         int pin_queue_start;
204         int pin_queue_end;
205         unsigned short num_scan_start;
206         unsigned char role;
207 };
208
209 /* large object space struct: 64+ KB */
210 /* we could make this limit much smaller to avoid memcpy copy
211  * and potentially have more room in the GC descriptor: need to measure
212  * This also means that such small OS objects will need to be
213  * allocated in a different way (using pinned chunks).
214  * We may want to put large but smaller than 64k objects in the fixed space
215  * when we move the object from one generation to another (to limit the
216  * pig in the snake effect).
217  * Note: it may be worth to have an optimized copy function, since we can
218  * assume that objects are aligned and have a multiple of 8 size.
219  * FIXME: This structure needs to be a multiple of 8 bytes in size: this is not
220  * true if MONO_ZERO_LEN_ARRAY is nonzero.
221  */
222 typedef struct _LOSObject LOSObject;
223 struct _LOSObject {
224         LOSObject *next;
225         mword size; /* this is the object size */
226         int dummy; /* to have a sizeof (LOSObject) a multiple of ALLOC_ALIGN  and data starting at same alignment */
227         guint16 role;
228         guint16 scanned;
229         char data [MONO_ZERO_LEN_ARRAY];
230 };
231
232 /* Pinned objects are allocated in the LOS space if bigger than half a page
233  * or from freelists otherwise. We assume that pinned objects are relatively few
234  * and they have a slow dying speed (like interned strings, thread objects).
235  * As such they will be collected only at major collections.
236  * free lists are not global: when we need memory we allocate a PinnedChunk.
237  * Each pinned chunk is made of several pages, the first of wich is used
238  * internally for bookeeping (here think of a page as 4KB). The bookeeping
239  * includes the freelists vectors and info about the object size of each page
240  * in the pinned chunk. So, when needed, a free page is found in a pinned chunk,
241  * a size is assigned to it, the page is divided in the proper chunks and each
242  * chunk is added to the freelist. To not waste space, the remaining space in the
243  * first page is used as objects of size 16 or 32 (need to measure which are more
244  * common).
245  * We use this same structure to allocate memory used internally by the GC, so
246  * we never use malloc/free if we need to alloc during collection: the world is stopped
247  * and malloc/free will deadlock.
248  * When we want to iterate over pinned objects, we just scan a page at a time
249  * linearly according to the size of objects in the page: the next pointer used to link
250  * the items in the freelist uses the same word as the vtable. Since we keep freelists
251  * for each pinned chunk, if the word points outside the pinned chunk it means
252  * it is an object.
253  * We could avoid this expensive scanning in creative ways. We could have a policy
254  * of putting in the pinned space only objects we know about that have no struct fields
255  * with references and we can easily use a even expensive write barrier for them,
256  * since pointer writes on such objects should be rare.
257  * The best compromise is to just alloc interned strings and System.MonoType in them.
258  * It would be nice to allocate MonoThread in it, too: must check that we properly
259  * use write barriers so we don't have to do any expensive scanning of the whole pinned
260  * chunk list during minor collections. We can avoid it now because we alloc in it only
261  * reference-free objects.
262  */
263 #define PINNED_FIRST_SLOT_SIZE (sizeof (gpointer) * 4)
264 #define MAX_FREELIST_SIZE 2048
265 #define PINNED_PAGE_SIZE (4096)
266 #define PINNED_CHUNK_MIN_SIZE (4096*8)
267 typedef struct _PinnedChunk PinnedChunk;
268 struct _PinnedChunk {
269         PinnedChunk *next;
270         int num_pages;
271         int *page_sizes; /* a 0 means the page is still unused */
272         void **free_list;
273         void *start_data;
274         void *data [1]; /* page sizes and free lists are stored here */
275 };
276
277 /*
278  * The young generation is divided into fragments. This is because
279  * we can hand one fragments to a thread for lock-less fast alloc and
280  * because the young generation ends up fragmented anyway by pinned objects.
281  * Once a collection is done, a list of fragments is created. When doing
282  * thread local alloc we use smallish nurseries so we allow new threads to
283  * allocate memory from gen0 without triggering a collection. Threads that
284  * are found to allocate lots of memory are given bigger fragments. This
285  * should make the finalizer thread use little nursery memory after a while.
286  * We should start assigning threads very small fragments: if there are many
287  * threads the nursery will be full of reserved space that the threads may not
288  * use at all, slowing down allocation speed.
289  */
290 typedef struct _Fragment Fragment;
291
292 struct _Fragment {
293         Fragment *next;
294         char *fragment_start;
295         char *fragment_limit; /* the current soft limit for allocation */
296         char *fragment_end;
297 };
298
299 /* the runtime can register areas of memory as roots: we keep two lists of roots,
300  * a pinned root set for conservatively scanned roots and a normal one for
301  * precisely scanned roots (currently implemented as a single list).
302  */
303 typedef struct _RootRecord RootRecord;
304 struct _RootRecord {
305         RootRecord *next;
306         char *start_root;
307         char *end_root;
308         mword root_desc;
309 };
310
311 /* for use with write barriers */
312 typedef struct _RememberedSet RememberedSet;
313 struct _RememberedSet {
314         mword *store_next;
315         mword *end_set;
316         RememberedSet *next;
317         mword data [MONO_ZERO_LEN_ARRAY];
318 };
319
320 /* we have 4 possible values in the low 2 bits */
321 enum {
322         REMSET_LOCATION, /* just a pointer to the exact location */
323         REMSET_RANGE,    /* range of pointer fields */
324         REMSET_OBJECT,    /* mark all the object for scanning */
325         REMSET_TYPE_MASK = 0x3
326 };
327
328 static __thread RememberedSet *remembered_set MONO_TLS_FAST;
329 static pthread_key_t remembered_set_key;
330 static RememberedSet *global_remset;
331 static int store_to_global_remset = 0;
332
333 /* FIXME: later choose a size that takes into account the RememberedSet struct
334  * and doesn't waste any alloc paddin space.
335  */
336 #define DEFAULT_REMSET_SIZE 1024
337 static RememberedSet* alloc_remset (int size, gpointer id);
338
339 /* Structure that corresponds to a MonoVTable: desc is a mword so requires
340  * no cast from a pointer to an integer
341  */
342 typedef struct {
343         MonoClass *klass;
344         mword desc;
345 } GCVTable;
346
347 /* these bits are set in the object vtable: we could merge them since an object can be
348  * either pinned or forwarded but not both.
349  * We store them in the vtable slot because the bits are used in the sync block for
350  * other purpouses: if we merge them and alloc the sync blocks aligned to 8 bytes, we can change
351  * this and use bit 3 in the syncblock (with the lower two bits both set for forwarded, that
352  * would be an invalid combination for the monitor and hash code).
353  * The values are already shifted.
354  * The forwarding address is stored in the sync block.
355  */
356 #define FORWARDED_BIT 1
357 #define PINNED_BIT 2
358 #define VTABLE_BITS_MASK 0x3
359
360 /* returns NULL if not forwarded, or the forwarded address */
361 #define object_is_forwarded(obj) (((mword*)(obj))[0] & FORWARDED_BIT? (void*)(((mword*)(obj))[1]): NULL)
362 /* set the forwarded address fw_addr for object obj */
363 #define forward_object(obj,fw_addr) do {        \
364                 ((mword*)(obj))[0] |= FORWARDED_BIT;    \
365                 ((mword*)(obj))[1] = (mword)(fw_addr);  \
366         } while (0)
367
368 #define object_is_pinned(obj) (((mword*)(obj))[0] & PINNED_BIT)
369 #define pin_object(obj) do {    \
370                 ((mword*)(obj))[0] |= PINNED_BIT;       \
371         } while (0)
372 #define unpin_object(obj) do {  \
373                 ((mword*)(obj))[0] &= ~PINNED_BIT;      \
374         } while (0)
375
376
377 /*
378  * Since we set bits in the vtable, use the macro to load it from the pointer to
379  * an object that is potentially pinned.
380  */
381 #define LOAD_VTABLE(addr) ((*(mword*)(addr)) & ~VTABLE_BITS_MASK)
382
383 static const char*
384 safe_name (void* obj)
385 {
386         MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
387         return vt->klass->name;
388 }
389
390 static guint
391 safe_object_get_size (MonoObject* o)
392 {
393         MonoClass *klass = ((MonoVTable*)LOAD_VTABLE (o))->klass;
394         if (klass == mono_defaults.string_class) {
395                 return sizeof (MonoString) + 2 * mono_string_length ((MonoString*) o) + 2;
396         } else if (klass->rank) {
397                 MonoArray *array = (MonoArray*)o;
398                 size_t size = sizeof (MonoArray) + mono_array_element_size (klass) * mono_array_length (array);
399                 if (array->bounds) {
400                         size += 3;
401                         size &= ~3;
402                         size += sizeof (MonoArrayBounds) * klass->rank;
403                 }
404                 return size;
405         } else {
406                 /* from a created object: the class must be inited already */
407                 return klass->instance_size;
408         }
409 }
410
411 /*
412  * ######################################################################
413  * ########  Global data.
414  * ######################################################################
415  */
416 static LOCK_DECLARE (gc_mutex);
417 static int gc_disabled = 0;
418 static int num_minor_gcs = 0;
419 static int num_major_gcs = 0;
420
421 /* good sizes are 512KB-1MB: larger ones increase a lot memzeroing time */
422 //#define DEFAULT_NURSERY_SIZE (1024*512*125+4096*118)
423 #define DEFAULT_NURSERY_SIZE (1024*512*2)
424 #define DEFAULT_MAX_SECTION (DEFAULT_NURSERY_SIZE * 16)
425 #define DEFAULT_LOS_COLLECTION_TARGET (DEFAULT_NURSERY_SIZE * 2)
426 /* to quickly find the heard of an object pinned by a conservative address
427  * we keep track of the objects allocated for each SCAN_START_SIZE memory
428  * chunk in the nursery or other memory sections. Larger values have less
429  * memory overhead and bigger runtime cost. 4-8 KB are reasonable values.
430  */
431 #define SCAN_START_SIZE (4096*2)
432 /* the minimum size of a fragment that we consider useful for allocation */
433 #define FRAGMENT_MIN_SIZE (512)
434 /* This is a fixed value used for pinned chunks, not the system pagesize */
435 #define FREELIST_PAGESIZE 4096
436
437 static mword pagesize = 4096;
438 static mword nursery_size = DEFAULT_NURSERY_SIZE;
439 static mword next_section_size = DEFAULT_NURSERY_SIZE * 4;
440 static mword max_section_size = DEFAULT_MAX_SECTION;
441 static int section_size_used = 0;
442 static int degraded_mode = 0;
443
444 static LOSObject *los_object_list = NULL;
445 static mword los_memory_usage = 0;
446 static mword los_num_objects = 0;
447 static mword next_los_collection = 2*1024*1024; /* 2 MB, need to tune */
448 static mword total_alloc = 0;
449 /* use this to tune when to do a major/minor collection */
450 static mword memory_pressure = 0;
451
452 static GCMemSection *section_list = NULL;
453 static GCMemSection *nursery_section = NULL;
454 static mword lowest_heap_address = ~(mword)0;
455 static mword highest_heap_address = 0;
456
457 typedef struct _FinalizeEntry FinalizeEntry;
458 struct _FinalizeEntry {
459         FinalizeEntry *next;
460         void *object;
461         void *data; /* can be a disappearing link or the data for the finalizer */
462         /* Note we could use just one pointer if we don't support multiple callbacks
463          * for finalizers and per-finalizer data and if we store the obj pointers
464          * in the link like libgc does
465          */
466 };
467
468 /*
469  * The finalizable hash has the object as the key, the 
470  * disappearing_link hash, has the link address as key.
471  */
472 static FinalizeEntry **finalizable_hash = NULL;
473 /* objects that are ready to be finalized */
474 static FinalizeEntry *fin_ready_list = NULL;
475 /* disappearing links use the same structure but a different list */
476 static FinalizeEntry **disappearing_link_hash = NULL;
477 static mword disappearing_link_hash_size = 0;
478 static mword finalizable_hash_size = 0;
479
480 static mword num_registered_finalizers = 0;
481 static mword num_ready_finalizers = 0;
482 static mword num_disappearing_links = 0;
483 static int no_finalize = 0;
484
485 /* keep each size a multiple of ALLOC_ALIGN */
486 /* on 64 bit systems 8 is likely completely unused. */
487 static const int freelist_sizes [] = {
488         8, 16, 24, 32, 40, 48, 64, 80,
489         96, 128, 160, 192, 224, 256, 320, 384,
490         448, 512, 584, 680, 816, 1024, 1360, 2048};
491 #define FREELIST_NUM_SLOTS (sizeof (freelist_sizes) / sizeof (freelist_sizes [0]))
492
493 static char* max_pinned_chunk_addr = NULL;
494 static char* min_pinned_chunk_addr = (char*)-1;
495 /* pinned_chunk_list is used for allocations of objects that are never moved */
496 static PinnedChunk *pinned_chunk_list = NULL;
497 /* internal_chunk_list is used for allocating structures needed by the GC */
498 static PinnedChunk *internal_chunk_list = NULL;
499
500 static gboolean
501 obj_is_from_pinned_alloc (char *p)
502 {
503         PinnedChunk *chunk = pinned_chunk_list;
504         for (; chunk; chunk = chunk->next) {
505                 if (p >= (char*)chunk->start_data && p < ((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE))
506                         return TRUE;
507         }
508         return FALSE;
509 }
510
511 /* registered roots: the key to the hash is the root start address */
512 static RootRecord **roots_hash = NULL;
513 static int roots_hash_size = 0;
514 static mword roots_size = 0; /* amount of memory in the root set */
515 static mword num_roots_entries = 0;
516
517 /* 
518  * The current allocation cursors
519  * We allocate objects in the nursery.
520  * The nursery is the area between nursery_start and nursery_real_end.
521  * nursery_next is the pointer to the space where the next object will be allocated.
522  * nursery_temp_end is the pointer to the end of the temporary space reserved for
523  * the allocation: this allows us to allow allocations inside the fragments of the
524  * nursery (the empty holes between pinned objects) and it allows us to set the
525  * scan starts at reasonable intervals.
526  * nursery_next and nursery_temp_end will become per-thread vars to allow lock-free
527  * allocations.
528  * nursery_first_pinned_start points to the start of the first pinned object in the nursery
529  * nursery_last_pinned_end points to the end of the last pinned object in the nursery
530  * At the next allocation, the area of the nursery where objects can be present is
531  * between MIN(nursery_first_pinned_start, first_fragment_start) and
532  * MAX(nursery_last_pinned_end, nursery_temp_end)
533  */
534 static char *nursery_start = NULL;
535 static char *nursery_next = NULL;
536 static char *nursery_temp_end = NULL;
537 static char *nursery_real_end = NULL;
538 static char *nursery_frag_real_end = NULL;
539 static char *nursery_first_pinned_start = NULL;
540 static char *nursery_last_pinned_end = NULL;
541
542 /* fragments that are free and ready to be used for allocation */
543 static Fragment *nursery_fragments = NULL;
544 /* freeelist of fragment structures */
545 static Fragment *fragment_freelist = NULL;
546
547 /* 
548  * used when moving the objects
549  * When the nursery is collected, objects are copied to to_space.
550  * The area between to_space and gray_objects is used as a stack
551  * of objects that need their fields checked for more references
552  * to be copied.
553  * We should optimize somehow this mechanism to avoid rescanning
554  * ptr-free objects. The order is also probably not optimal: need to
555  * test cache misses and other graph traversal orders.
556  */
557 static char *to_space = NULL;
558 static char *gray_objects = NULL;
559 static char *to_space_end = NULL;
560 static GCMemSection *to_space_section = NULL;
561
562 /* objects bigger then this go into the large object space */
563 #define MAX_SMALL_OBJ_SIZE 0xffff
564
565 /*
566  * ######################################################################
567  * ########  Macros and function declarations.
568  * ######################################################################
569  */
570
571 #define UPDATE_HEAP_BOUNDARIES(low,high) do {   \
572                 if ((mword)(low) < lowest_heap_address) \
573                         lowest_heap_address = (mword)(low);     \
574                 if ((mword)(high) > highest_heap_address)       \
575                         highest_heap_address = (mword)(high);   \
576         } while (0)
577
578 inline static void*
579 align_pointer (void *ptr)
580 {
581         mword p = (mword)ptr;
582         p += sizeof (gpointer) - 1;
583         p &= ~ (sizeof (gpointer) - 1);
584         return (void*)p;
585 }
586
587 /* forward declarations */
588 static void* get_internal_mem          (size_t size);
589 static void  free_internal_mem         (void *addr);
590 static void* get_os_memory             (size_t size, int activate);
591 static void  free_os_memory            (void *addr, size_t size);
592 static void  report_internal_mem_usage (void);
593
594 static int stop_world (void);
595 static int restart_world (void);
596 static void pin_thread_data (void *start_nursery, void *end_nursery);
597 static void scan_from_remsets (void *start_nursery, void *end_nursery);
598 static void find_pinning_ref_from_thread (char *obj, size_t size);
599 static void update_current_thread_stack (void *start);
600 static GCMemSection* alloc_section (size_t size);
601 static void finalize_in_range (char *start, char *end);
602 static void null_link_in_range (char *start, char *end);
603 static gboolean search_fragment_for_size (size_t size);
604 static void mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end);
605 static void clear_remsets (void);
606 static void sweep_pinned_objects (void);
607 static void free_large_object (LOSObject *obj);
608 static void free_mem_section (GCMemSection *section);
609
610 /*
611  * ######################################################################
612  * ########  GC descriptors
613  * ######################################################################
614  * Used to quickly get the info the GC needs about an object: size and
615  * where the references are held.
616  */
617 /* objects are aligned to 8 bytes boundaries
618  * A descriptor is a pointer in MonoVTable, so 32 or 64 bits of size.
619  * The low 3 bits define the type of the descriptor. The other bits
620  * depend on the type.
621  * As a general rule the 13 remaining low bits define the size, either
622  * of the whole object or of the elements in the arrays. While for objects
623  * the size is already in bytes, for arrays we need to shift, because
624  * array elements might be smaller than 8 bytes. In case of arrays, we
625  * use two bits to describe what the additional high bits represents,
626  * so the default behaviour can handle element sizes less than 2048 bytes.
627  * The high 16 bits, if 0 it means the object is pointer-free.
628  * This design should make it easy and fast to skip over ptr-free data.
629  * The first 4 types should cover >95% of the objects.
630  * Note that since the size of objects is limited to 64K, larger objects
631  * will be allocated in the large object heap.
632  * If we want 4-bytes alignment, we need to put vector and small bitmap
633  * inside complex.
634  */
635 enum {
636         DESC_TYPE_RUN_LENGTH,   /* 16 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
637         DESC_TYPE_SMALL_BITMAP, /* 16 bits aligned byte size | 16-48 bit bitmap */
638         DESC_TYPE_STRING,       /* nothing */
639         DESC_TYPE_COMPLEX,      /* index for bitmap into complex_descriptors */
640         DESC_TYPE_VECTOR,       /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
641         DESC_TYPE_ARRAY,        /* 10 bits element size | 1 bit array | 2 bits desc | element desc */
642         DESC_TYPE_LARGE_BITMAP, /* | 29-61 bitmap bits */
643         DESC_TYPE_COMPLEX_ARR,  /* index for bitmap into complex_descriptors */
644         /* subtypes for arrays and vectors */
645         DESC_TYPE_V_PTRFREE = 0,/* there are no refs: keep first so it has a zero value  */
646         DESC_TYPE_V_REFS,       /* all the array elements are refs */
647         DESC_TYPE_V_RUN_LEN,    /* elements are run-length encoded as DESC_TYPE_RUN_LENGTH */
648         DESC_TYPE_V_BITMAP      /* elements are as the bitmap in DESC_TYPE_SMALL_BITMAP */
649 };
650
651 #define OBJECT_HEADER_WORDS (sizeof(MonoObject)/sizeof(gpointer))
652 #define LOW_TYPE_BITS 3
653 #define SMALL_BITMAP_SHIFT 16
654 #define SMALL_BITMAP_SIZE (GC_BITS_PER_WORD - SMALL_BITMAP_SHIFT)
655 #define VECTOR_INFO_SHIFT 14
656 #define VECTOR_ELSIZE_SHIFT 3
657 #define LARGE_BITMAP_SIZE (GC_BITS_PER_WORD - LOW_TYPE_BITS)
658 #define MAX_SMALL_SIZE ((1 << SMALL_BITMAP_SHIFT) - 1)
659 #define SMALL_SIZE_MASK 0xfff8
660 #define MAX_ELEMENT_SIZE 0x3ff
661 #define ELEMENT_SIZE_MASK (0x3ff << LOW_TYPE_BITS)
662 #define VECTOR_SUBTYPE_PTRFREE (DESC_TYPE_V_PTRFREE << VECTOR_INFO_SHIFT)
663 #define VECTOR_SUBTYPE_REFS    (DESC_TYPE_V_REFS << VECTOR_INFO_SHIFT)
664 #define VECTOR_SUBTYPE_RUN_LEN (DESC_TYPE_V_RUN_LEN << VECTOR_INFO_SHIFT)
665 #define VECTOR_SUBTYPE_BITMAP  (DESC_TYPE_V_BITMAP << VECTOR_INFO_SHIFT)
666
667 #define ALLOC_ALIGN 8
668
669
670 /* Root bitmap descriptors are simpler: the lower two bits describe the type
671  * and we either have 30/62 bitmap bits or nibble-based run-length,
672  * or a complex descriptor
673  */
674 enum {
675         ROOT_DESC_CONSERVATIVE, /* 0, so matches NULL value */
676         ROOT_DESC_BITMAP,
677         ROOT_DESC_RUN_LEN,
678         ROOT_DESC_LARGE_BITMAP,
679         ROOT_DESC_TYPE_MASK = 0x3,
680         ROOT_DESC_TYPE_SHIFT = 2,
681 };
682
683 static gsize* complex_descriptors = NULL;
684 static int complex_descriptors_size = 0;
685 static int complex_descriptors_next = 0;
686
687 static int
688 alloc_complex_descriptor (gsize *bitmap, int numbits)
689 {
690         int nwords = numbits/GC_BITS_PER_WORD + 2;
691         int res;
692         int i;
693
694         LOCK_GC;
695         res = complex_descriptors_next;
696         /* linear search, so we don't have duplicates with domain load/unload
697          * this should not be performance critical or we'd have bigger issues
698          * (the number and size of complex descriptors should be small).
699          */
700         for (i = 0; i < complex_descriptors_next; ) {
701                 if (complex_descriptors [i] == nwords) {
702                         int j, found = TRUE;
703                         for (j = 0; j < nwords - 1; ++j) {
704                                 if (complex_descriptors [i + 1 + j] != bitmap [j]) {
705                                         found = FALSE;
706                                         break;
707                                 }
708                         }
709                         if (found) {
710                                 UNLOCK_GC;
711                                 return i;
712                         }
713                 }
714                 i += complex_descriptors [i];
715         }
716         if (complex_descriptors_next + nwords > complex_descriptors_size) {
717                 int new_size = complex_descriptors_size * 2 + nwords;
718                 complex_descriptors = g_realloc (complex_descriptors, new_size * sizeof (gsize));
719                 complex_descriptors_size = new_size;
720         }
721         DEBUG (6, fprintf (gc_debug_file, "Complex descriptor %d, size: %d (total desc memory: %d)\n", res, nwords, complex_descriptors_size));
722         complex_descriptors_next += nwords;
723         complex_descriptors [res] = nwords;
724         for (i = 0; i < nwords - 1; ++i) {
725                 complex_descriptors [res + 1 + i] = bitmap [i];
726                 DEBUG (6, fprintf (gc_debug_file, "\tvalue: %p\n", (void*)complex_descriptors [res + 1 + i]));
727         }
728         UNLOCK_GC;
729         return res;
730 }
731
732 /*
733  * Descriptor builders.
734  */
735 void*
736 mono_gc_make_descr_for_string (void)
737 {
738         return (void*) DESC_TYPE_STRING;
739 }
740
741 void*
742 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
743 {
744         int first_set = -1, num_set = 0, last_set = -1, i;
745         mword desc = 0;
746         size_t stored_size = obj_size;
747         stored_size += ALLOC_ALIGN - 1;
748         stored_size &= ~(ALLOC_ALIGN - 1);
749         for (i = 0; i < numbits; ++i) {
750                 if (bitmap [i / GC_BITS_PER_WORD] & (1 << (i % GC_BITS_PER_WORD))) {
751                         if (first_set < 0)
752                                 first_set = i;
753                         last_set = i;
754                         num_set++;
755                 }
756         }
757         if (stored_size <= MAX_SMALL_OBJ_SIZE) {
758                 /* check run-length encoding first: one byte offset, one byte number of pointers
759                  * on 64 bit archs, we can have 3 runs, just one on 32.
760                  * It may be better to use nibbles.
761                  */
762                 if (first_set < 0) {
763                         desc = DESC_TYPE_RUN_LENGTH | stored_size;
764                         DEBUG (6, fprintf (gc_debug_file, "Ptrfree descriptor %p, size: %d\n", (void*)desc, stored_size));
765                         return (void*) desc;
766                 } else if (first_set < 256 && num_set < 256 && (first_set + num_set == last_set + 1)) {
767                         desc = DESC_TYPE_RUN_LENGTH | stored_size | (first_set << 16) | (num_set << 24);
768                         DEBUG (6, fprintf (gc_debug_file, "Runlen descriptor %p, size: %d, first set: %d, num set: %d\n", (void*)desc, stored_size, first_set, num_set));
769                         return (void*) desc;
770                 }
771                 /* we know the 2-word header is ptr-free */
772                 if (last_set < SMALL_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
773                         desc = DESC_TYPE_SMALL_BITMAP | stored_size | ((*bitmap >> OBJECT_HEADER_WORDS) << SMALL_BITMAP_SHIFT);
774                         DEBUG (6, fprintf (gc_debug_file, "Smallbitmap descriptor %p, size: %d, last set: %d\n", (void*)desc, stored_size, last_set));
775                         return (void*) desc;
776                 }
777         }
778         /* we know the 2-word header is ptr-free */
779         if (last_set < LARGE_BITMAP_SIZE + OBJECT_HEADER_WORDS) {
780                 desc = DESC_TYPE_LARGE_BITMAP | ((*bitmap >> OBJECT_HEADER_WORDS) << LOW_TYPE_BITS);
781                 DEBUG (6, fprintf (gc_debug_file, "Largebitmap descriptor %p, size: %d, last set: %d\n", (void*)desc, stored_size, last_set));
782                 return (void*) desc;
783         }
784         /* it's a complex object ... */
785         desc = DESC_TYPE_COMPLEX | (alloc_complex_descriptor (bitmap, last_set + 1) << LOW_TYPE_BITS);
786         return (void*) desc;
787 }
788
789 /* If the array holds references, numbits == 1 and the first bit is set in elem_bitmap */
790 void*
791 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
792 {
793         int first_set = -1, num_set = 0, last_set = -1, i;
794         mword desc = vector? DESC_TYPE_VECTOR: DESC_TYPE_ARRAY;
795         for (i = 0; i < numbits; ++i) {
796                 if (elem_bitmap [i / GC_BITS_PER_WORD] & (1 << (i % GC_BITS_PER_WORD))) {
797                         if (first_set < 0)
798                                 first_set = i;
799                         last_set = i;
800                         num_set++;
801                 }
802         }
803         if (elem_size <= MAX_ELEMENT_SIZE) {
804                 desc |= elem_size << VECTOR_ELSIZE_SHIFT;
805                 if (!num_set) {
806                         return (void*)(desc | VECTOR_SUBTYPE_PTRFREE);
807                 }
808                 /* Note: we also handle structs with just ref fields */
809                 if (num_set * sizeof (gpointer) == elem_size) {
810                         return (void*)(desc | VECTOR_SUBTYPE_REFS | ((gssize)(-1) << 16));
811                 }
812                 /* FIXME: try run-len first */
813                 /* Note: we can't skip the object header here, because it's not present */
814                 if (last_set <= SMALL_BITMAP_SIZE) {
815                         return (void*)(desc | VECTOR_SUBTYPE_BITMAP | (*elem_bitmap << 16));
816                 }
817         }
818         /* it's am array of complex structs ... */
819         desc = DESC_TYPE_COMPLEX_ARR;
820         desc |= alloc_complex_descriptor (elem_bitmap, last_set + 1) << LOW_TYPE_BITS;
821         return (void*) desc;
822 }
823
824 /* helper macros to scan and traverse objects, macros because we resue them in many functions */
825 #define STRING_SIZE(size,str) do {      \
826                 (size) = sizeof (MonoString) + 2 * (mono_string_length ((MonoString*)(str)) + 1);       \
827                 (size) += (ALLOC_ALIGN - 1);    \
828                 (size) &= ~(ALLOC_ALIGN - 1);   \
829         } while (0)
830
831 #define OBJ_RUN_LEN_SIZE(size,vt,obj) do {      \
832                 (size) = (vt)->desc & 0xfff8;   \
833         } while (0)
834
835 #define OBJ_BITMAP_SIZE(size,vt,obj) do {       \
836                 (size) = (vt)->desc & 0xfff8;   \
837         } while (0)
838
839 //#define PREFETCH(addr) __asm__ __volatile__ ("     prefetchnta     %0": : "m"(*(char *)(addr)))
840 #define PREFETCH(addr)
841
842 /* code using these macros must define a HANDLE_PTR(ptr) macro that does the work */
843 #define OBJ_RUN_LEN_FOREACH_PTR(vt,obj) do {    \
844                 if ((vt)->desc & 0xffff0000) {  \
845                         /* there are pointers */        \
846                         void **_objptr_end;     \
847                         void **_objptr = (void**)(obj); \
848                         _objptr += ((vt)->desc >> 16) & 0xff;   \
849                         _objptr_end = _objptr + (((vt)->desc >> 24) & 0xff);    \
850                         while (_objptr < _objptr_end) { \
851                                 HANDLE_PTR (_objptr, (obj));    \
852                                 _objptr++;      \
853                         }       \
854                 }       \
855         } while (0)
856
857 /* a bitmap desc means that there are pointer references or we'd have
858  * choosen run-length, instead: add an assert to check.
859  */
860 #define OBJ_BITMAP_FOREACH_PTR(vt,obj)  do {    \
861                 /* there are pointers */        \
862                 void **_objptr = (void**)(obj); \
863                 gsize _bmap = (vt)->desc >> 16; \
864                 _objptr += OBJECT_HEADER_WORDS; \
865                 while (_bmap) { \
866                         if ((_bmap & 1)) {      \
867                                 HANDLE_PTR (_objptr, (obj));    \
868                         }       \
869                         _bmap >>= 1;    \
870                         ++_objptr;      \
871                 }       \
872         } while (0)
873
874 #define OBJ_LARGE_BITMAP_FOREACH_PTR(vt,obj)    do {    \
875                 /* there are pointers */        \
876                 void **_objptr = (void**)(obj); \
877                 gsize _bmap = (vt)->desc >> LOW_TYPE_BITS;      \
878                 _objptr += OBJECT_HEADER_WORDS; \
879                 while (_bmap) { \
880                         if ((_bmap & 1)) {      \
881                                 HANDLE_PTR (_objptr, (obj));    \
882                         }       \
883                         _bmap >>= 1;    \
884                         ++_objptr;      \
885                 }       \
886         } while (0)
887
888 #define OBJ_COMPLEX_FOREACH_PTR(vt,obj) do {    \
889                 /* there are pointers */        \
890                 void **_objptr = (void**)(obj); \
891                 gsize *bitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS);       \
892                 int bwords = (*bitmap_data) - 1;        \
893                 void **start_run = _objptr;     \
894                 bitmap_data++;  \
895                 if (0) {        \
896                         MonoObject *myobj = (MonoObject*)obj;   \
897                         g_print ("found %d at %p (0x%x): %s.%s\n", bwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name);    \
898                 }       \
899                 while (bwords-- > 0) {  \
900                         gsize _bmap = *bitmap_data++;   \
901                         _objptr = start_run;    \
902                         /*g_print ("bitmap: 0x%x/%d at %p\n", _bmap, bwords, _objptr);*/        \
903                         while (_bmap) { \
904                                 if ((_bmap & 1)) {      \
905                                         HANDLE_PTR (_objptr, (obj));    \
906                                 }       \
907                                 _bmap >>= 1;    \
908                                 ++_objptr;      \
909                         }       \
910                         start_run += GC_BITS_PER_WORD;  \
911                 }       \
912         } while (0)
913
914 /* this one is untested */
915 #define OBJ_COMPLEX_ARR_FOREACH_PTR(vt,obj)     do {    \
916                 /* there are pointers */        \
917                 gsize *mbitmap_data = complex_descriptors + ((vt)->desc >> LOW_TYPE_BITS);      \
918                 int mbwords = (*mbitmap_data++) - 1;    \
919                 int el_size = mono_array_element_size (((MonoObject*)(obj))->vtable->klass);    \
920                 char *e_start = (char*)(obj) +  G_STRUCT_OFFSET (MonoArray, vector);    \
921                 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
922                 if (0) {        \
923                         MonoObject *myobj = (MonoObject*)start; \
924                         g_print ("found %d at %p (0x%x): %s.%s\n", mbwords, (obj), (vt)->desc, myobj->vtable->klass->name_space, myobj->vtable->klass->name);   \
925                 }       \
926                 while (e_start < e_end) {       \
927                         void **_objptr = (void**)e_start;       \
928                         gsize *bitmap_data = mbitmap_data;      \
929                         unsigned int bwords = mbwords;  \
930                         while (bwords-- > 0) {  \
931                                 gsize _bmap = *bitmap_data++;   \
932                                 void **start_run = _objptr;     \
933                                 /*g_print ("bitmap: 0x%x\n", _bmap);*/  \
934                                 while (_bmap) { \
935                                         if ((_bmap & 1)) {      \
936                                                 HANDLE_PTR (_objptr, (obj));    \
937                                         }       \
938                                         _bmap >>= 1;    \
939                                         ++_objptr;      \
940                                 }       \
941                                 _objptr = start_run + GC_BITS_PER_WORD; \
942                         }       \
943                         e_start += el_size;     \
944                 }       \
945         } while (0)
946
947 #define OBJ_VECTOR_FOREACH_PTR(vt,obj)  do {    \
948                 /* note: 0xffffc000 excludes DESC_TYPE_V_PTRFREE */     \
949                 if ((vt)->desc & 0xffffc000) {  \
950                         int el_size = ((vt)->desc >> 3) & MAX_ELEMENT_SIZE;     \
951                         /* there are pointers */        \
952                         int etype = (vt)->desc & 0xc000;        \
953                         if (etype == (DESC_TYPE_V_REFS << 14)) {        \
954                                 void **p = (void**)((char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector));        \
955                                 void **end_refs = (void**)((char*)p + el_size * mono_array_length ((MonoArray*)(obj))); \
956                                 /* Note: this code can handle also arrays of struct with only references in them */     \
957                                 while (p < end_refs) {  \
958                                         HANDLE_PTR (p, (obj));  \
959                                         ++p;    \
960                                 }       \
961                         } else if (etype == DESC_TYPE_V_RUN_LEN << 14) {        \
962                                 int offset = ((vt)->desc >> 16) & 0xff; \
963                                 int num_refs = ((vt)->desc >> 24) & 0xff;       \
964                                 char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector);     \
965                                 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
966                                 while (e_start < e_end) {       \
967                                         void **p = (void**)e_start;     \
968                                         int i;  \
969                                         p += offset;    \
970                                         for (i = 0; i < num_refs; ++i) {        \
971                                                 HANDLE_PTR (p + i, (obj));      \
972                                         }       \
973                                         e_start += el_size;     \
974                                 }       \
975                         } else if (etype == DESC_TYPE_V_BITMAP << 14) { \
976                                 char *e_start = (char*)(obj) +  G_STRUCT_OFFSET (MonoArray, vector);    \
977                                 char *e_end = e_start + el_size * mono_array_length ((MonoArray*)(obj));        \
978                                 while (e_start < e_end) {       \
979                                         void **p = (void**)e_start;     \
980                                         gsize _bmap = (vt)->desc >> 16; \
981                                         /* Note: there is no object header here to skip */      \
982                                         while (_bmap) { \
983                                                 if ((_bmap & 1)) {      \
984                                                         HANDLE_PTR (p, (obj));  \
985                                                 }       \
986                                                 _bmap >>= 1;    \
987                                                 ++p;    \
988                                         }       \
989                                         e_start += el_size;     \
990                                 }       \
991                         }       \
992                 }       \
993         } while (0)
994
995 static mword new_obj_references = 0;
996 static mword obj_references_checked = 0;
997
998 #undef HANDLE_PTR
999 #define HANDLE_PTR(ptr,obj)     do {    \
1000                 if (*(ptr) && (char*)*(ptr) >= nursery_start && (char*)*(ptr) < nursery_next) { \
1001                         new_obj_references++;   \
1002                         /*printf ("bogus ptr %p found at %p in object %p (%s.%s)\n", *(ptr), (ptr), o, o->vtable->klass->name_space, o->vtable->klass->name);*/ \
1003                 } else {        \
1004                         obj_references_checked++;       \
1005                 }       \
1006         } while (0)
1007
1008 /*
1009  * ######################################################################
1010  * ########  Detecting and removing garbage.
1011  * ######################################################################
1012  * This section of code deals with detecting the objects no longer in use
1013  * and reclaiming the memory.
1014  */
1015 static void __attribute__((noinline))
1016 scan_area (char *start, char *end)
1017 {
1018         GCVTable *vt;
1019         size_t skip_size;
1020         int type;
1021         int type_str = 0, type_rlen = 0, type_bitmap = 0, type_vector = 0, type_lbit = 0, type_complex = 0;
1022         new_obj_references = 0;
1023         obj_references_checked = 0;
1024         while (start < end) {
1025                 if (!*(void**)start) {
1026                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1027                         continue;
1028                 }
1029                 vt = (GCVTable*)LOAD_VTABLE (start);
1030                 DEBUG (8, fprintf (gc_debug_file, "Scanning object %p, vtable: %p (%s)\n", start, vt, vt->klass->name));
1031                 if (0) {
1032                         MonoObject *obj = (MonoObject*)start;
1033                         g_print ("found at %p (0x%x): %s.%s\n", start, vt->desc, obj->vtable->klass->name_space, obj->vtable->klass->name);
1034                 }
1035                 type = vt->desc & 0x7;
1036                 if (type == DESC_TYPE_STRING) {
1037                         STRING_SIZE (skip_size, start);
1038                         start += skip_size;
1039                         type_str++;
1040                         continue;
1041                 } else if (type == DESC_TYPE_RUN_LENGTH) {
1042                         OBJ_RUN_LEN_SIZE (skip_size, vt, start);
1043                         g_assert (skip_size);
1044                         OBJ_RUN_LEN_FOREACH_PTR (vt,start);
1045                         start += skip_size;
1046                         type_rlen++;
1047                         continue;
1048                 } else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
1049                         skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
1050                         skip_size *= mono_array_length ((MonoArray*)start);
1051                         skip_size += sizeof (MonoArray);
1052                         skip_size += (ALLOC_ALIGN - 1);
1053                         skip_size &= ~(ALLOC_ALIGN - 1);
1054                         OBJ_VECTOR_FOREACH_PTR (vt, start);
1055                         if (type == DESC_TYPE_ARRAY) {
1056                                 /* account for the bounds */
1057                         }
1058                         start += skip_size;
1059                         type_vector++;
1060                         continue;
1061                 } else if (type == DESC_TYPE_SMALL_BITMAP) {
1062                         OBJ_BITMAP_SIZE (skip_size, vt, start);
1063                         g_assert (skip_size);
1064                         OBJ_BITMAP_FOREACH_PTR (vt,start);
1065                         start += skip_size;
1066                         type_bitmap++;
1067                         continue;
1068                 } else if (type == DESC_TYPE_LARGE_BITMAP) {
1069                         skip_size = safe_object_get_size ((MonoObject*)start);
1070                         skip_size += (ALLOC_ALIGN - 1);
1071                         skip_size &= ~(ALLOC_ALIGN - 1);
1072                         OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
1073                         start += skip_size;
1074                         type_lbit++;
1075                         continue;
1076                 } else if (type == DESC_TYPE_COMPLEX) {
1077                         /* this is a complex object */
1078                         skip_size = safe_object_get_size ((MonoObject*)start);
1079                         skip_size += (ALLOC_ALIGN - 1);
1080                         skip_size &= ~(ALLOC_ALIGN - 1);
1081                         OBJ_COMPLEX_FOREACH_PTR (vt, start);
1082                         start += skip_size;
1083                         type_complex++;
1084                         continue;
1085                 } else if (type == DESC_TYPE_COMPLEX_ARR) {
1086                         /* this is an array of complex structs */
1087                         skip_size = mono_array_element_size (((MonoVTable*)vt)->klass);
1088                         skip_size *= mono_array_length ((MonoArray*)start);
1089                         skip_size += sizeof (MonoArray);
1090                         skip_size += (ALLOC_ALIGN - 1);
1091                         skip_size &= ~(ALLOC_ALIGN - 1);
1092                         OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
1093                         if (type == DESC_TYPE_ARRAY) {
1094                                 /* account for the bounds */
1095                         }
1096                         start += skip_size;
1097                         type_complex++;
1098                         continue;
1099                 } else {
1100                         g_assert (0);
1101                 }
1102         }
1103         /*printf ("references to new nursery %p-%p (size: %dk): %d, checked: %d\n", old_start, end, (end-old_start)/1024, new_obj_references, obj_references_checked);
1104         printf ("\tstrings: %d, runl: %d, vector: %d, bitmaps: %d, lbitmaps: %d, complex: %d\n",
1105                 type_str, type_rlen, type_vector, type_bitmap, type_lbit, type_complex);*/
1106 }
1107
1108 static void __attribute__((noinline))
1109 scan_area_for_domain (MonoDomain *domain, char *start, char *end)
1110 {
1111         GCVTable *vt;
1112         size_t skip_size;
1113         int type, remove;
1114         while (start < end) {
1115                 if (!*(void**)start) {
1116                         start += sizeof (void*); /* should be ALLOC_ALIGN, really */
1117                         continue;
1118                 }
1119                 vt = (GCVTable*)LOAD_VTABLE (start);
1120                 /* handle threads someway (maybe insert the root domain vtable?) */
1121                 if (mono_object_domain (start) == domain && vt->klass != mono_defaults.thread_class) {
1122                         DEBUG (1, fprintf (gc_debug_file, "Need to cleanup object %p, (%s)\n", start, safe_name (start)));
1123                         remove = 1;
1124                 } else {
1125                         remove = 0;
1126                 }
1127                 type = vt->desc & 0x7;
1128                 if (type == DESC_TYPE_STRING) {
1129                         STRING_SIZE (skip_size, start);
1130                         if (remove) memset (start, 0, skip_size);
1131                         start += skip_size;
1132                         continue;
1133                 } else if (type == DESC_TYPE_RUN_LENGTH) {
1134                         OBJ_RUN_LEN_SIZE (skip_size, vt, start);
1135                         g_assert (skip_size);
1136                         if (remove) memset (start, 0, skip_size);
1137                         start += skip_size;
1138                         continue;
1139                 } else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
1140                         skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
1141                         skip_size *= mono_array_length ((MonoArray*)start);
1142                         skip_size += sizeof (MonoArray);
1143                         skip_size += (ALLOC_ALIGN - 1);
1144                         skip_size &= ~(ALLOC_ALIGN - 1);
1145                         if (type == DESC_TYPE_ARRAY) {
1146                                 /* account for the bounds */
1147                         }
1148                         if (remove) memset (start, 0, skip_size);
1149                         start += skip_size;
1150                         continue;
1151                 } else if (type == DESC_TYPE_SMALL_BITMAP) {
1152                         OBJ_BITMAP_SIZE (skip_size, vt, start);
1153                         g_assert (skip_size);
1154                         if (remove) memset (start, 0, skip_size);
1155                         start += skip_size;
1156                         continue;
1157                 } else if (type == DESC_TYPE_LARGE_BITMAP) {
1158                         skip_size = safe_object_get_size ((MonoObject*)start);
1159                         skip_size += (ALLOC_ALIGN - 1);
1160                         skip_size &= ~(ALLOC_ALIGN - 1);
1161                         if (remove) memset (start, 0, skip_size);
1162                         start += skip_size;
1163                         continue;
1164                 } else if (type == DESC_TYPE_COMPLEX) {
1165                         /* this is a complex object */
1166                         skip_size = safe_object_get_size ((MonoObject*)start);
1167                         skip_size += (ALLOC_ALIGN - 1);
1168                         skip_size &= ~(ALLOC_ALIGN - 1);
1169                         if (remove) memset (start, 0, skip_size);
1170                         start += skip_size;
1171                         continue;
1172                 } else if (type == DESC_TYPE_COMPLEX_ARR) {
1173                         /* this is an array of complex structs */
1174                         skip_size = mono_array_element_size (((MonoVTable*)vt)->klass);
1175                         skip_size *= mono_array_length ((MonoArray*)start);
1176                         skip_size += sizeof (MonoArray);
1177                         skip_size += (ALLOC_ALIGN - 1);
1178                         skip_size &= ~(ALLOC_ALIGN - 1);
1179                         if (type == DESC_TYPE_ARRAY) {
1180                                 /* account for the bounds */
1181                         }
1182                         if (remove) memset (start, 0, skip_size);
1183                         start += skip_size;
1184                         continue;
1185                 } else {
1186                         g_assert (0);
1187                 }
1188         }
1189 }
1190
1191 /*
1192  * When appdomains are unloaded we can easily remove objects that have finalizers,
1193  * but all the others could still be present in random places on the heap.
1194  * We need a sweep to get rid of them even though it's going to be costly
1195  * with big heaps.
1196  * The reason we need to remove them is because we access the vtable and class
1197  * structures to know the object size and the reference bitmap: once the domain is
1198  * unloaded the point to random memory.
1199  */
1200 void
1201 mono_gc_clear_domain (MonoDomain * domain)
1202 {
1203         GCMemSection *section;
1204         LOCK_GC;
1205         for (section = section_list; section; section = section->next) {
1206                 scan_area_for_domain (domain, section->data, section->end_data);
1207         }
1208         /* FIXME: handle big and fixed objects (we remove, don't clear in this case) */
1209         UNLOCK_GC;
1210 }
1211
1212 static void
1213 add_to_global_remset (gpointer ptr)
1214 {
1215         RememberedSet *rs;
1216         DEBUG (8, fprintf (gc_debug_file, "Adding global remset for %p\n", ptr));
1217         if (global_remset->store_next < global_remset->end_set) {
1218                 *(global_remset->store_next++) = (mword)ptr;
1219                 return;
1220         }
1221         rs = alloc_remset (global_remset->end_set - global_remset->data, NULL);
1222         rs->next = global_remset;
1223         global_remset = rs;
1224         *(global_remset->store_next++) = (mword)ptr;
1225 }
1226
1227 /*
1228  * This is how the copying happens from the nursery to the old generation.
1229  * We assume that at this time all the pinned objects have been identified and
1230  * marked as such.
1231  * We run scan_object() for each pinned object so that each referenced
1232  * objects if possible are copied. The new gray objects created can have
1233  * scan_object() run on them right away, too.
1234  * Then we run copy_object() for the precisely tracked roots. At this point
1235  * all the roots are either gray or black. We run scan_object() on the gray
1236  * objects until no more gray objects are created.
1237  * At the end of the process we walk again the pinned list and we unmark
1238  * the pinned flag. As we go we also create the list of free space for use
1239  * in the next allocation runs.
1240  *
1241  * We need to remember objects from the old generation that point to the new one
1242  * (or just addresses?).
1243  *
1244  * copy_object could be made into a macro once debugged (use inline for now).
1245  */
1246
1247 static char* __attribute__((noinline))
1248 copy_object (char *obj, char *from_space_start, char *from_space_end)
1249 {
1250         if (obj >= from_space_start && obj < from_space_end && (obj < to_space || obj >= to_space_end)) {
1251                 MonoVTable *vt;
1252                 char *forwarded;
1253                 mword objsize;
1254                 DEBUG (9, fprintf (gc_debug_file, "Precise copy of %p", obj));
1255                 if ((forwarded = object_is_forwarded (obj))) {
1256                         g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr);
1257                         DEBUG (9, fprintf (gc_debug_file, " (already forwarded to %p)\n", forwarded));
1258                         return forwarded;
1259                 }
1260                 if (object_is_pinned (obj)) {
1261                         g_assert (((MonoVTable*)LOAD_VTABLE(obj))->gc_descr);
1262                         DEBUG (9, fprintf (gc_debug_file, " (pinned, no change)\n"));
1263                         return obj;
1264                 }
1265                 objsize = safe_object_get_size ((MonoObject*)obj);
1266                 objsize += ALLOC_ALIGN - 1;
1267                 objsize &= ~(ALLOC_ALIGN - 1);
1268                 DEBUG (9, fprintf (gc_debug_file, " (to %p, %s size: %d)\n", gray_objects, ((MonoObject*)obj)->vtable->klass->name, objsize));
1269                 /* FIXME: handle pinned allocs:
1270                  * Large objects are simple, at least until we always follow the rule:
1271                  * if objsize >= MAX_SMALL_OBJ_SIZE, pin the object and return it.
1272                  * At the end of major collections, we walk the los list and if
1273                  * the object is pinned, it is marked, otherwise it can be freed.
1274                  */
1275                 if (objsize >= MAX_SMALL_OBJ_SIZE || (obj >= min_pinned_chunk_addr && obj < max_pinned_chunk_addr && obj_is_from_pinned_alloc (obj))) {
1276                         DEBUG (9, fprintf (gc_debug_file, "Marked LOS/Pinned %p (%s), size: %d\n", obj, safe_name (obj), objsize));
1277                         pin_object (obj);
1278                         return obj;
1279                 }
1280                 /* ok, the object is not pinned, we can move it */
1281                 /* use a optimized memcpy here */
1282 #if 0
1283                 {
1284                         int ecx;
1285                         char* esi = obj;
1286                         char* edi = gray_objects;
1287                         __asm__ __volatile__(
1288                                 "rep; movsl"
1289                                 : "=&c" (ecx), "=&D" (edi), "=&S" (esi)
1290                                 : "0" (objsize/4), "1" (edi),"2" (esi)
1291                                 : "memory"
1292                         );
1293                 }
1294 #else
1295                 memcpy (gray_objects, obj, objsize);
1296 #endif
1297                 /* adjust array->bounds */
1298                 vt = ((MonoObject*)obj)->vtable;
1299                 g_assert (vt->gc_descr);
1300                 if (vt->rank && ((MonoArray*)obj)->bounds) {
1301                         MonoArray *array = (MonoArray*)gray_objects;
1302                         array->bounds = (MonoArrayBounds*)((char*)gray_objects + ((char*)((MonoArray*)obj)->bounds - (char*)obj));
1303                         DEBUG (9, fprintf (gc_debug_file, "Array instance %p: size: %d, rank: %d, length: %d\n", array, objsize, vt->rank, mono_array_length (array)));
1304                 }
1305                 /* set the forwarding pointer */
1306                 forward_object (obj, gray_objects);
1307                 obj = gray_objects;
1308                 to_space_section->scan_starts [((char*)obj - (char*)to_space_section->data)/SCAN_START_SIZE] = obj;
1309                 gray_objects += objsize;
1310                 DEBUG (8, g_assert (gray_objects <= to_space_end));
1311                 return obj;
1312         }
1313         return obj;
1314 }
1315
1316 #undef HANDLE_PTR
1317 #define HANDLE_PTR(ptr,obj)     do {    \
1318                 if (*(ptr)) {   \
1319                         void *__old = *(ptr);   \
1320                         *(ptr) = copy_object (*(ptr), from_start, from_end);    \
1321                         DEBUG (9, if (__old != *(ptr)) fprintf (gc_debug_file, "Overwrote field at %p with %p (was: %p)\n", (ptr), *(ptr), __old));     \
1322                         if (*(ptr) >= (void*)from_start && *(ptr) < (void*)from_end)    \
1323                                 add_to_global_remset ((ptr));   \
1324                 }       \
1325         } while (0)
1326
1327 /*
1328  * Scan the object pointed to by @start for references to
1329  * other objects between @from_start and @from_end and copy
1330  * them to the gray_objects area.
1331  * Returns a pointer to the end of the object.
1332  */
1333 static char*
1334 scan_object (char *start, char* from_start, char* from_end)
1335 {
1336         GCVTable *vt;
1337         size_t skip_size;
1338
1339         vt = (GCVTable*)LOAD_VTABLE (start);
1340         //type = vt->desc & 0x7;
1341
1342         /* gcc should be smart enough to remove the bounds check, but it isn't:( */
1343         switch (vt->desc & 0x7) {
1344         //if (type == DESC_TYPE_STRING) {
1345         case DESC_TYPE_STRING:
1346                 STRING_SIZE (skip_size, start);
1347                 return start + skip_size;
1348         //} else if (type == DESC_TYPE_RUN_LENGTH) {
1349         case DESC_TYPE_RUN_LENGTH:
1350                 OBJ_RUN_LEN_FOREACH_PTR (vt,start);
1351                 OBJ_RUN_LEN_SIZE (skip_size, vt, start);
1352                 g_assert (skip_size);
1353                 return start + skip_size;
1354         //} else if (type == DESC_TYPE_VECTOR) { // includes ARRAY, too
1355         case DESC_TYPE_ARRAY:
1356         case DESC_TYPE_VECTOR:
1357                 OBJ_VECTOR_FOREACH_PTR (vt, start);
1358                 skip_size = safe_object_get_size ((MonoObject*)start);
1359 #if 0
1360                 skip_size = (vt->desc >> LOW_TYPE_BITS) & MAX_ELEMENT_SIZE;
1361                 skip_size *= mono_array_length ((MonoArray*)start);
1362                 skip_size += sizeof (MonoArray);
1363 #endif
1364                 skip_size += (ALLOC_ALIGN - 1);
1365                 skip_size &= ~(ALLOC_ALIGN - 1);
1366                 return start + skip_size;
1367         //} else if (type == DESC_TYPE_SMALL_BITMAP) {
1368         case DESC_TYPE_SMALL_BITMAP:
1369                 OBJ_BITMAP_FOREACH_PTR (vt,start);
1370                 OBJ_BITMAP_SIZE (skip_size, vt, start);
1371                 return start + skip_size;
1372         //} else if (type == DESC_TYPE_LARGE_BITMAP) {
1373         case DESC_TYPE_LARGE_BITMAP:
1374                 OBJ_LARGE_BITMAP_FOREACH_PTR (vt,start);
1375                 skip_size = safe_object_get_size ((MonoObject*)start);
1376                 skip_size += (ALLOC_ALIGN - 1);
1377                 skip_size &= ~(ALLOC_ALIGN - 1);
1378                 return start + skip_size;
1379         //} else if (type == DESC_TYPE_COMPLEX) {
1380         case DESC_TYPE_COMPLEX:
1381                 OBJ_COMPLEX_FOREACH_PTR (vt, start);
1382                 /* this is a complex object */
1383                 skip_size = safe_object_get_size ((MonoObject*)start);
1384                 skip_size += (ALLOC_ALIGN - 1);
1385                 skip_size &= ~(ALLOC_ALIGN - 1);
1386                 return start + skip_size;
1387         //} else if (type == DESC_TYPE_COMPLEX_ARR) {
1388         case DESC_TYPE_COMPLEX_ARR:
1389                 OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start);
1390                 /* this is an array of complex structs */
1391                 skip_size = safe_object_get_size ((MonoObject*)start);
1392 #if 0
1393                 skip_size = mono_array_element_size (((MonoObject*)start)->vtable->klass);
1394                 skip_size *= mono_array_length ((MonoArray*)start);
1395                 skip_size += sizeof (MonoArray);
1396 #endif
1397                 skip_size += (ALLOC_ALIGN - 1);
1398                 skip_size &= ~(ALLOC_ALIGN - 1);
1399                 return start + skip_size;
1400         }
1401         g_assert_not_reached ();
1402         return NULL;
1403 }
1404
1405 /*
1406  * Addresses from start to end are already sorted. This function finds the object header
1407  * for each address and pins the object. The addresses must be inside the passed section.
1408  * Return the number of pinned objects.
1409  */
1410 static int
1411 pin_objects_from_addresses (GCMemSection *section, void **start, void **end, void *start_nursery, void *end_nursery)
1412 {
1413         void *last = NULL;
1414         int count = 0;
1415         void *search_start;
1416         void *last_obj = NULL;
1417         size_t last_obj_size = 0;
1418         void *addr;
1419         int idx;
1420         void **definitely_pinned = start;
1421         while (start < end) {
1422                 addr = *start;
1423                 /* the range check should be reduntant */
1424                 if (addr != last && addr >= start_nursery && addr < end_nursery) {
1425                         DEBUG (5, fprintf (gc_debug_file, "Considering pinning addr %p\n", addr));
1426                         /* multiple pointers to the same object */
1427                         if (addr >= last_obj && (char*)addr < (char*)last_obj + last_obj_size) {
1428                                 start++;
1429                                 continue;
1430                         }
1431                         idx = ((char*)addr - (char*)section->data) / SCAN_START_SIZE;
1432                         search_start = (void*)section->scan_starts [idx];
1433                         if (!search_start || search_start > addr) {
1434                                 while (idx) {
1435                                         --idx;
1436                                         search_start = section->scan_starts [idx];
1437                                         if (search_start && search_start <= addr)
1438                                                 break;
1439                                 }
1440                                 if (!search_start || search_start > addr)
1441                                         search_start = start_nursery;
1442                         }
1443                         if (search_start < last_obj)
1444                                 search_start = (char*)last_obj + last_obj_size;
1445                         /* now addr should be in an object a short distance from search_start
1446                          * Note that search_start must point to zeroed mem or point to an object.
1447                          */
1448                         do {
1449                                 if (!*(void**)search_start) {
1450                                         mword p = (mword)search_start;
1451                                         p += sizeof (gpointer);
1452                                         p += ALLOC_ALIGN - 1;
1453                                         p &= ~(ALLOC_ALIGN - 1);
1454                                         search_start = (void*)p;
1455                                         continue;
1456                                 }
1457                                 last_obj = search_start;
1458                                 last_obj_size = safe_object_get_size ((MonoObject*)search_start);
1459                                 last_obj_size += ALLOC_ALIGN - 1;
1460                                 last_obj_size &= ~(ALLOC_ALIGN - 1);
1461                                 DEBUG (8, fprintf (gc_debug_file, "Pinned try match %p (%s), size %d\n", last_obj, safe_name (last_obj), last_obj_size));
1462                                 if (addr >= search_start && (char*)addr < (char*)last_obj + last_obj_size) {
1463                                         DEBUG (4, fprintf (gc_debug_file, "Pinned object %p, vtable %p (%s), count %d\n", search_start, *(void**)search_start, safe_name (search_start), count));
1464                                         pin_object (search_start);
1465                                         definitely_pinned [count] = search_start;
1466                                         count++;
1467                                         break;
1468                                 }
1469                                 /* skip to the next object */
1470                                 search_start = (void*)((char*)search_start + last_obj_size);
1471                         } while (search_start <= addr);
1472                         /* we either pinned the correct object or we ignored the addr because
1473                          * it points to unused zeroed memory.
1474                          */
1475                         last = addr;
1476                 }
1477                 start++;
1478         }
1479         //printf ("effective pinned: %d (at the end: %d)\n", count, (char*)end_nursery - (char*)last);
1480         return count;
1481 }
1482
1483 static void** pin_queue;
1484 static int pin_queue_size = 0;
1485 static int next_pin_slot = 0;
1486
1487 static int
1488 new_gap (int gap)
1489 {
1490         gap = (gap * 10) / 13;
1491         if (gap == 9 || gap == 10)
1492                 return 11;
1493         if (gap < 1)
1494                 return 1;
1495         return gap;
1496 }
1497
1498 #if 0
1499 static int
1500 compare_addr (const void *a, const void *b)
1501 {
1502         return *(const void **)a - *(const void **)b;
1503 }
1504 #endif
1505
1506 /* sort the addresses in array in increasing order */
1507 static void
1508 sort_addresses (void **array, int size)
1509 {
1510         /*
1511          * qsort is slower as predicted.
1512          * qsort (array, size, sizeof (gpointer), compare_addr);
1513          * return;
1514          */
1515         int gap = size;
1516         int swapped, end;
1517         while (TRUE) {
1518                 int i;
1519                 gap = new_gap (gap);
1520                 swapped = FALSE;
1521                 end = size - gap;
1522                 for (i = 0; i < end; i++) {
1523                         int j = i + gap;
1524                         if (array [i] > array [j]) {
1525                                 void* val = array [i];
1526                                 array [i] = array [j];
1527                                 array [j] = val;
1528                                 swapped = TRUE;
1529                         }
1530                 }
1531                 if (gap == 1 && !swapped)
1532                         break;
1533         }
1534 }
1535
1536 static void
1537 print_nursery_gaps (void* start_nursery, void *end_nursery)
1538 {
1539         int i;
1540         gpointer first = start_nursery;
1541         gpointer next;
1542         for (i = 0; i < next_pin_slot; ++i) {
1543                 next = pin_queue [i];
1544                 fprintf (gc_debug_file, "Nursery range: %p-%p, size: %d\n", first, next, (char*)next-(char*)first);
1545                 first = next;
1546         }
1547         next = end_nursery;
1548         fprintf (gc_debug_file, "Nursery range: %p-%p, size: %d\n", first, next, (char*)next-(char*)first);
1549 }
1550
1551 /* reduce the info in the pin queue, removing duplicate pointers and sorting them */
1552 static void
1553 optimize_pin_queue (int start_slot)
1554 {
1555         void **start, **cur, **end;
1556         /* sort and uniq pin_queue: we just sort and we let the rest discard multiple values */
1557         /* it may be better to keep ranges of pinned memory instead of individually pinning objects */
1558         DEBUG (5, fprintf (gc_debug_file, "Sorting pin queue, size: %d\n", next_pin_slot));
1559         if ((next_pin_slot - start_slot) > 1)
1560                 sort_addresses (pin_queue + start_slot, next_pin_slot - start_slot);
1561         start = cur = pin_queue + start_slot;
1562         end = pin_queue + next_pin_slot;
1563         while (cur < end) {
1564                 *start = *cur++;
1565                 while (*start == *cur && cur < end)
1566                         cur++;
1567                 start++;
1568         };
1569         next_pin_slot = start - pin_queue;
1570         DEBUG (5, fprintf (gc_debug_file, "Pin queue reduced to size: %d\n", next_pin_slot));
1571         //DEBUG (6, print_nursery_gaps (start_nursery, end_nursery));
1572         
1573 }
1574
1575 static void
1576 realloc_pin_queue (void)
1577 {
1578         int new_size = pin_queue_size? pin_queue_size + pin_queue_size/2: 1024;
1579         void **new_pin = get_internal_mem (sizeof (void*) * new_size);
1580         memcpy (new_pin, pin_queue, sizeof (void*) * next_pin_slot);
1581         free_internal_mem (pin_queue);
1582         pin_queue = new_pin;
1583         pin_queue_size = new_size;
1584         DEBUG (4, fprintf (gc_debug_file, "Reallocated pin queue to size: %d\n", new_size));
1585 }
1586
1587 /* 
1588  * Scan the memory between start and end and queue values which could be pointers
1589  * to the area between start_nursery and end_nursery for later consideration.
1590  * Typically used for thread stacks.
1591  */
1592 static void
1593 conservatively_pin_objects_from (void **start, void **end, void *start_nursery, void *end_nursery)
1594 {
1595         int count = 0;
1596         while (start < end) {
1597                 if (*start >= start_nursery && *start < end_nursery) {
1598                         /*
1599                          * *start can point to the middle of an object
1600                          * note: should we handle pointing at the end of an object?
1601                          * pinning in C# code disallows pointing at the end of an object
1602                          * but there is some small chance that an optimizing C compiler
1603                          * may keep the only reference to an object by pointing
1604                          * at the end of it. We ignore this small chance for now.
1605                          * Pointers to the end of an object are indistinguishable
1606                          * from pointers to the start of the next object in memory
1607                          * so if we allow that we'd need to pin two objects...
1608                          * We queue the pointer in an array, the
1609                          * array will then be sorted and uniqued. This way
1610                          * we can coalesce several pinning pointers and it should
1611                          * be faster since we'd do a memory scan with increasing
1612                          * addresses. Note: we can align the address to the allocation
1613                          * alignment, so the unique process is more effective.
1614                          */
1615                         mword addr = (mword)*start;
1616                         addr &= ~(ALLOC_ALIGN - 1);
1617                         if (next_pin_slot >= pin_queue_size)
1618                                 realloc_pin_queue ();
1619                         pin_queue [next_pin_slot++] = (void*)addr;
1620                         DEBUG (6, if (count) fprintf (gc_debug_file, "Pinning address %p\n", (void*)addr));
1621                         count++;
1622                 }
1623                 start++;
1624         }
1625         DEBUG (7, if (count) fprintf (gc_debug_file, "found %d potential pinned heap pointers\n", count));
1626 }
1627
1628 /* 
1629  * If generation is 0, just mark objects in the nursery, the others we don't care,
1630  * since they are not going to move anyway.
1631  * There are different areas that are scanned for pinned pointers:
1632  * *) the thread stacks (when jit support is ready only the unmanaged frames)
1633  * *) the pinned handle table
1634  * *) the pinned roots
1635  *
1636  * Note: when we'll use a write barrier for old to new gen references, we need to
1637  * keep track of old gen objects that point to pinned new gen objects because in that
1638  * case the referenced object will be moved maybe at the next collection, but there
1639  * is no write in the old generation area where the pinned object is referenced
1640  * and we may not consider it as reachable.
1641  */
1642 static void
1643 mark_pinned_objects (int generation)
1644 {
1645 }
1646
1647 /*
1648  * Debugging function: find in the conservative roots where @obj is being pinned.
1649  */
1650 static void
1651 find_pinning_reference (char *obj, size_t size)
1652 {
1653         RootRecord *root;
1654         int i;
1655         char *endobj = obj + size;
1656         for (i = 0; i < roots_hash_size; ++i) {
1657                 for (root = roots_hash [i]; root; root = root->next) {
1658                         /* if desc is non-null it has precise info */
1659                         if (!root->root_desc) {
1660                                 char ** start = (char**)root->start_root;
1661                                 while (start < (char**)root->end_root) {
1662                                         if (*start >= obj && *start < endobj) {
1663                                                 DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in pinned roots %p-%p (at %p in record %p)\n", obj, root->start_root, root->end_root, start, root));
1664                                         }
1665                                         start++;
1666                                 }
1667                         }
1668                 }
1669         }
1670         find_pinning_ref_from_thread (obj, size);
1671 }
1672
1673 /*
1674  * The first thing we do in a collection is to identify pinned objects.
1675  * This function considers all the areas of memory that need to be
1676  * conservatively scanned.
1677  */
1678 static void
1679 pin_from_roots (void *start_nursery, void *end_nursery)
1680 {
1681         RootRecord *root;
1682         int i;
1683         DEBUG (3, fprintf (gc_debug_file, "Scanning pinned roots (%d bytes, %d entries)\n", (int)roots_size, num_roots_entries));
1684         /* objects pinned from the API are inside these roots */
1685         for (i = 0; i < roots_hash_size; ++i) {
1686                 for (root = roots_hash [i]; root; root = root->next) {
1687                         /* if desc is non-null it has precise info */
1688                         if (root->root_desc)
1689                                 continue;
1690                         DEBUG (6, fprintf (gc_debug_file, "Pinned roots %p-%p\n", root->start_root, root->end_root));
1691                         conservatively_pin_objects_from ((void**)root->start_root, (void**)root->end_root, start_nursery, end_nursery);
1692                 }
1693         }
1694         /* now deal with the thread stacks
1695          * in the future we should be able to conservatively scan only:
1696          * *) the cpu registers
1697          * *) the unmanaged stack frames
1698          * *) the _last_ managed stack frame
1699          * *) pointers slots in managed frames
1700          */
1701         pin_thread_data (start_nursery, end_nursery);
1702 }
1703
1704 /*
1705  * The memory area from start_root to end_root contains pointers to objects.
1706  * Their position is precisely described by @desc (this means that the pointer
1707  * can be either NULL or the pointer to the start of an object).
1708  * This functions copies them to to_space updates them.
1709  */
1710 static void
1711 precisely_scan_objects_from (void** start_root, void** end_root, char* n_start, char *n_end, mword desc)
1712 {
1713         switch (desc & ROOT_DESC_TYPE_MASK) {
1714         case ROOT_DESC_BITMAP:
1715                 desc >>= ROOT_DESC_TYPE_SHIFT;
1716                 while (desc) {
1717                         if ((desc & 1) && *start_root) {
1718                                 *start_root = copy_object (*start_root, n_start, n_end);
1719                                 DEBUG (9, fprintf (gc_debug_file, "Overwrote root at %p with %p\n", start_root, *start_root));  \
1720                         }
1721                         desc >>= 1;
1722                         start_root++;
1723                 }
1724                 return;
1725         case ROOT_DESC_RUN_LEN:
1726         case ROOT_DESC_LARGE_BITMAP:
1727                 g_assert_not_reached ();
1728         }
1729 }
1730
1731 static Fragment*
1732 alloc_fragment (void)
1733 {
1734         Fragment *frag = fragment_freelist;
1735         if (frag) {
1736                 fragment_freelist = frag->next;
1737                 frag->next = NULL;
1738                 return frag;
1739         }
1740         frag = get_internal_mem (sizeof (Fragment));
1741         frag->next = NULL;
1742         return frag;
1743 }
1744
1745 /*
1746  * Allocate and setup the data structures needed to be able to allocate objects
1747  * in the nursery. The nursery is stored in nursery_section.
1748  */
1749 static void
1750 alloc_nursery (void)
1751 {
1752         GCMemSection *section;
1753         char *data;
1754         int scan_starts;
1755         Fragment *frag;
1756
1757         if (nursery_section)
1758                 return;
1759         DEBUG (2, fprintf (gc_debug_file, "Allocating nursery size: %d\n", nursery_size));
1760         /* later we will alloc a larger area for the nursery but only activate
1761          * what we need. The rest will be used as expansion if we have too many pinned
1762          * objects in the existing nursery.
1763          */
1764         /* FIXME: handle OOM */
1765         section = get_internal_mem (sizeof (GCMemSection));
1766         data = get_os_memory (nursery_size, TRUE);
1767         nursery_start = nursery_next = data;
1768         nursery_real_end = data + nursery_size;
1769         nursery_temp_end = data + SCAN_START_SIZE;
1770         UPDATE_HEAP_BOUNDARIES (nursery_start, nursery_real_end);
1771         total_alloc += nursery_size;
1772         DEBUG (4, fprintf (gc_debug_file, "Expanding heap size: %d, total: %d\n", nursery_size, total_alloc));
1773         section->data = section->next_data = data;
1774         section->size = nursery_size;
1775         section->end_data = nursery_real_end;
1776         scan_starts = nursery_size / SCAN_START_SIZE;
1777         section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts);
1778         section->num_scan_start = scan_starts;
1779
1780         /* add to the section list */
1781         section->next = section_list;
1782         section_list = section;
1783
1784         nursery_section = section;
1785
1786         /* Setup the single first large fragment */
1787         frag = alloc_fragment ();
1788         frag->fragment_start = nursery_start;
1789         frag->fragment_limit = nursery_start;
1790         frag->fragment_end = nursery_real_end;
1791         nursery_frag_real_end = nursery_real_end;
1792         /* FIXME: frag here is lost */
1793 }
1794
1795 /*
1796  * Update roots in the old generation. Since we currently don't have the
1797  * info from the write barriers, we just scan all the objects.
1798  */
1799 static void
1800 scan_old_generation (char *start, char* end)
1801 {
1802         GCMemSection *section;
1803         FinalizeEntry *fin;
1804         LOSObject *big_object;
1805         char *p;
1806         
1807         for (section = section_list; section; section = section->next) {
1808                 if (section == nursery_section)
1809                         continue;
1810                 DEBUG (2, fprintf (gc_debug_file, "Scan of old section: %p-%p, size: %d\n", section->data, section->next_data, (int)(section->next_data - section->data)));
1811                 /* we have to deal with zeroed holes in old generation (truncated strings ...) */
1812                 p = section->data;
1813                 while (p < section->next_data) {
1814                         if (!*(void**)p) {
1815                                 p += ALLOC_ALIGN;
1816                                 continue;
1817                         }
1818                         DEBUG (8, fprintf (gc_debug_file, "Precise old object scan of %p (%s)\n", p, safe_name (p)));
1819                         p = scan_object (p, start, end);
1820                 }
1821         }
1822         /* scan the old object space, too */
1823         for (big_object = los_object_list; big_object; big_object = big_object->next) {
1824                 DEBUG (5, fprintf (gc_debug_file, "Scan of big object: %p (%s), size: %d\n", big_object->data, safe_name (big_object->data), big_object->size));
1825                 scan_object (big_object->data, start, end);
1826         }
1827         /* scan the list of objects ready for finalization */
1828         for (fin = fin_ready_list; fin; fin = fin->next) {
1829                 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
1830                 fin->object = copy_object (fin->object, start, end);
1831         }
1832 }
1833
1834 static mword fragment_total = 0;
1835 /*
1836  * We found a fragment of free memory in the nursery: memzero it and if
1837  * it is big enough, add it to the list of fragments that can be used for
1838  * allocation.
1839  */
1840 static void
1841 add_nursery_frag (size_t frag_size, char* frag_start, char* frag_end)
1842 {
1843         Fragment *fragment;
1844         DEBUG (4, fprintf (gc_debug_file, "Found empty fragment: %p-%p, size: %d\n", frag_start, frag_end, frag_size));
1845         /* memsetting just the first chunk start is bound to provide better cache locality */
1846         memset (frag_start, 0, frag_size);
1847         /* Not worth dealing with smaller fragments: need to tune */
1848         if (frag_size >= FRAGMENT_MIN_SIZE) {
1849                 fragment = alloc_fragment ();
1850                 fragment->fragment_start = frag_start;
1851                 fragment->fragment_limit = frag_start;
1852                 fragment->fragment_end = frag_end;
1853                 fragment->next = nursery_fragments;
1854                 nursery_fragments = fragment;
1855                 fragment_total += frag_size;
1856         }
1857 }
1858
1859 static int
1860 scan_needed_big_objects (char *start_addr, char *end_addr)
1861 {
1862         LOSObject *big_object;
1863         int count = 0;
1864         for (big_object = los_object_list; big_object; big_object = big_object->next) {
1865                 if (!big_object->scanned && object_is_pinned (big_object->data)) {
1866                         DEBUG (5, fprintf (gc_debug_file, "Scan of big object: %p (%s), size: %d\n", big_object->data, safe_name (big_object->data), big_object->size));
1867                         scan_object (big_object->data, start_addr, end_addr);
1868                         big_object->scanned = TRUE;
1869                         count++;
1870                 }
1871         }
1872         return count;
1873 }
1874
1875 static void
1876 drain_gray_stack (char *start_addr, char *end_addr)
1877 {
1878         TV_DECLARE (atv);
1879         TV_DECLARE (btv);
1880         int fin_ready, bigo_scanned_num;
1881         char *gray_start;
1882
1883         /*
1884          * We copied all the reachable objects. Now it's the time to copy
1885          * the objects that were not referenced by the roots, but by the copied objects.
1886          * we built a stack of objects pointed to by gray_start: they are
1887          * additional roots and we may add more items as we go.
1888          * We loop until gray_start == gray_objects which means no more objects have
1889          * been added. Note this is iterative: no recursion is involved.
1890          * We need to walk the LO list as well in search of marked big objects
1891          * (use a flag since this is needed only on major collections). We need to loop
1892          * here as well, so keep a counter of marked LO (increasing it in copy_object).
1893          */
1894         TV_GETTIME (btv);
1895         gray_start = to_space;
1896         DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area: %p-%p, size: %d\n", gray_start, gray_objects, (int)(gray_objects - gray_start)));
1897         while (gray_start < gray_objects) {
1898                 DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", gray_start, safe_name (gray_start)));
1899                 gray_start = scan_object (gray_start, start_addr, end_addr);
1900         }
1901         TV_GETTIME (atv);
1902         DEBUG (2, fprintf (gc_debug_file, "Gray stack scan: %d usecs\n", TV_ELAPSED (btv, atv)));
1903         //scan_old_generation (start_addr, end_addr);
1904         DEBUG (2, fprintf (gc_debug_file, "Old generation done\n"));
1905         /* walk the finalization queue and move also the objects that need to be
1906          * finalized: use the finalized objects as new roots so the objects they depend
1907          * on are also not reclaimed. As with the roots above, only objects in the nursery
1908          * are marked/copied.
1909          * We need a loop here, since objects ready for finalizers may reference other objects
1910          * that are fin-ready. Speedup with a flag?
1911          */
1912         do {
1913                 fin_ready = num_ready_finalizers;
1914                 finalize_in_range (start_addr, end_addr);
1915                 bigo_scanned_num = scan_needed_big_objects (start_addr, end_addr);
1916
1917                 /* drain the new stack that might have been created */
1918                 DEBUG (6, fprintf (gc_debug_file, "Precise scan of gray area post fin: %p-%p, size: %d\n", gray_start, gray_objects, (int)(gray_objects - gray_start)));
1919                 while (gray_start < gray_objects) {
1920                         DEBUG (9, fprintf (gc_debug_file, "Precise gray object scan %p (%s)\n", gray_start, safe_name (gray_start)));
1921                         gray_start = scan_object (gray_start, start_addr, end_addr);
1922                 }
1923         } while (fin_ready != num_ready_finalizers || bigo_scanned_num);
1924
1925         DEBUG (2, fprintf (gc_debug_file, "Copied to old space: %d bytes\n", (int)(gray_objects - to_space)));
1926         to_space = gray_start;
1927         to_space_section->next_data = to_space;
1928
1929         /*
1930          * handle disappearing links
1931          * Note we do this after checking the finalization queue because if an object
1932          * survives (at least long enough to be finalized) we don't clear the link.
1933          * This also deals with a possible issue with the monitor reclamation: with the Boehm
1934          * GC a finalized object my lose the monitor because it is cleared before the finalizer is
1935          * called.
1936          */
1937         null_link_in_range (start_addr, end_addr);
1938         TV_GETTIME (btv);
1939         DEBUG (2, fprintf (gc_debug_file, "Finalize queue handling scan: %d usecs\n", TV_ELAPSED (atv, btv)));
1940 }
1941
1942 static int last_num_pinned = 0;
1943
1944 static void
1945 build_nursery_fragments (int start_pin, int end_pin)
1946 {
1947         char *frag_start, *frag_end;
1948         size_t frag_size;
1949         int i;
1950
1951         /* FIXME: handle non-NULL fragment_freelist */
1952         fragment_freelist = nursery_fragments;
1953         nursery_fragments = NULL;
1954         frag_start = nursery_start;
1955         fragment_total = 0;
1956         /* clear scan starts */
1957         memset (nursery_section->scan_starts, 0, nursery_section->num_scan_start * sizeof (gpointer));
1958         for (i = start_pin; i < end_pin; ++i) {
1959                 frag_end = pin_queue [i];
1960                 /* remove the pin bit from pinned objects */
1961                 unpin_object (frag_end);
1962                 nursery_section->scan_starts [((char*)frag_end - (char*)nursery_section->data)/SCAN_START_SIZE] = frag_end;
1963                 frag_size = frag_end - frag_start;
1964                 if (frag_size)
1965                         add_nursery_frag (frag_size, frag_start, frag_end);
1966                 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
1967                 frag_size += ALLOC_ALIGN - 1;
1968                 frag_size &= ~(ALLOC_ALIGN - 1);
1969                 frag_start = (char*)pin_queue [i] + frag_size;
1970         }
1971         nursery_last_pinned_end = frag_start;
1972         frag_end = nursery_real_end;
1973         frag_size = frag_end - frag_start;
1974         if (frag_size)
1975                 add_nursery_frag (frag_size, frag_start, frag_end);
1976         if (!nursery_fragments) {
1977                 DEBUG (1, fprintf (gc_debug_file, "Nursery fully pinned (%d)\n", end_pin - start_pin));
1978                 for (i = start_pin; i < end_pin; ++i) {
1979                         DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", pin_queue [i], safe_name (pin_queue [i]), safe_object_get_size (pin_queue [i])));
1980                 }
1981                 degraded_mode = 1;
1982         }
1983 }
1984
1985 /* FIXME: later reduce code duplication here with the above
1986  * We don't keep track of section fragments for non-nursery sections yet, so
1987  * just memset to 0.
1988  */
1989 static void
1990 build_section_fragments (GCMemSection *section)
1991 {
1992         int i;
1993         char *frag_start, *frag_end;
1994         size_t frag_size;
1995
1996         /* clear scan starts */
1997         memset (section->scan_starts, 0, section->num_scan_start * sizeof (gpointer));
1998         frag_start = section->data;
1999         section->next_data = section->data;
2000         for (i = section->pin_queue_start; i < section->pin_queue_end; ++i) {
2001                 frag_end = pin_queue [i];
2002                 /* remove the pin bit from pinned objects */
2003                 unpin_object (frag_end);
2004                 if (frag_end >= section->data + section->size) {
2005                         frag_end = section->data + section->size;
2006                 } else {
2007                         section->scan_starts [((char*)frag_end - (char*)section->data)/SCAN_START_SIZE] = frag_end;
2008                 }
2009                 frag_size = frag_end - frag_start;
2010                 if (frag_size)
2011                         memset (frag_start, 0, frag_size);
2012                 frag_size = safe_object_get_size ((MonoObject*)pin_queue [i]);
2013                 frag_size += ALLOC_ALIGN - 1;
2014                 frag_size &= ~(ALLOC_ALIGN - 1);
2015                 frag_start = (char*)pin_queue [i] + frag_size;
2016                 section->next_data = MAX (section->next_data, frag_start);
2017         }
2018         frag_end = section->end_data;
2019         frag_size = frag_end - frag_start;
2020         if (frag_size)
2021                 memset (frag_start, 0, frag_size);
2022 }
2023
2024 static void
2025 scan_from_registered_roots (char *addr_start, char *addr_end)
2026 {
2027         int i;
2028         RootRecord *root;
2029         for (i = 0; i < roots_hash_size; ++i) {
2030                 for (root = roots_hash [i]; root; root = root->next) {
2031                         /* if desc is non-null it has precise info */
2032                         if (!root->root_desc)
2033                                 continue;
2034                         DEBUG (6, fprintf (gc_debug_file, "Precise root scan %p-%p (desc: %p)\n", root->start_root, root->end_root, (void*)root->root_desc));
2035                         precisely_scan_objects_from ((void**)root->start_root, (void**)root->end_root, addr_start, addr_end, root->root_desc);
2036                 }
2037         }
2038 }
2039
2040 /*
2041  * Collect objects in the nursery.
2042  */
2043 static void
2044 collect_nursery (size_t requested_size)
2045 {
2046         GCMemSection *section;
2047         size_t max_garbage_amount;
2048         int i;
2049         TV_DECLARE (atv);
2050         TV_DECLARE (btv);
2051
2052         degraded_mode = 0;
2053         nursery_next = MAX (nursery_next, nursery_last_pinned_end);
2054         /* FIXME: optimize later to use the higher address where an object can be present */
2055         nursery_next = MAX (nursery_next, nursery_real_end);
2056
2057         DEBUG (1, fprintf (gc_debug_file, "Start nursery collection %d %p-%p, size: %d\n", num_minor_gcs, nursery_start, nursery_next, (int)(nursery_next - nursery_start)));
2058         max_garbage_amount = nursery_next - nursery_start;
2059         /* 
2060          * not enough room in the old generation to store all the possible data from 
2061          * the nursery in a single continuous space.
2062          * We reset to_space if we allocated objects in degraded mode.
2063          */
2064         if (to_space_section)
2065                 to_space = gray_objects = to_space_section->next_data;
2066         if ((to_space_end - to_space) < max_garbage_amount) {
2067                 section = alloc_section (nursery_section->size * 4);
2068                 g_assert (nursery_section->size >= max_garbage_amount);
2069                 to_space = gray_objects = section->next_data;
2070                 to_space_end = section->end_data;
2071                 to_space_section = section;
2072         }
2073         DEBUG (2, fprintf (gc_debug_file, "To space setup: %p-%p in section %p\n", to_space, to_space_end, to_space_section));
2074         nursery_section->next_data = nursery_next;
2075
2076         num_minor_gcs++;
2077         /* world must be stopped already */
2078         TV_GETTIME (atv);
2079         /* pin from pinned handles */
2080         pin_from_roots (nursery_start, nursery_next);
2081         /* identify pinned objects */
2082         optimize_pin_queue (0);
2083         next_pin_slot = pin_objects_from_addresses (nursery_section, pin_queue, pin_queue + next_pin_slot, nursery_start, nursery_next);
2084         TV_GETTIME (btv);
2085         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
2086         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2087
2088         /* 
2089          * walk all the roots and copy the young objects to the old generation,
2090          * starting from to_space
2091          */
2092
2093         scan_from_remsets (nursery_start, nursery_next);
2094         /* we don't have complete write barrier yet, so we scan all the old generation sections */
2095         TV_GETTIME (atv);
2096         DEBUG (2, fprintf (gc_debug_file, "Old generation scan: %d usecs\n", TV_ELAPSED (btv, atv)));
2097         /* FIXME: later scan also alloc_pinned objects */
2098
2099         /* the pinned objects are roots */
2100         for (i = 0; i < next_pin_slot; ++i) {
2101                 DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of pinned %p (%s)\n", i, pin_queue [i], safe_name (pin_queue [i])));
2102                 scan_object (pin_queue [i], nursery_start, nursery_next);
2103         }
2104         /* registered roots, this includes static fields */
2105         scan_from_registered_roots (nursery_start, nursery_next);
2106         TV_GETTIME (btv);
2107         DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (atv, btv)));
2108
2109         drain_gray_stack (nursery_start, nursery_next);
2110
2111         /* walk the pin_queue, build up the fragment list of free memory, unmark
2112          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2113          * next allocations.
2114          */
2115         build_nursery_fragments (0, next_pin_slot);
2116         TV_GETTIME (atv);
2117         DEBUG (2, fprintf (gc_debug_file, "Fragment creation: %d usecs, %d bytes available\n", TV_ELAPSED (btv, atv), fragment_total));
2118
2119         /* prepare the pin queue for the next collection */
2120         last_num_pinned = next_pin_slot;
2121         next_pin_slot = 0;
2122         if (fin_ready_list) {
2123                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2124                 mono_gc_finalize_notify ();
2125         }
2126 }
2127
2128 static void
2129 major_collection (void)
2130 {
2131         GCMemSection *section, *prev_section;
2132         LOSObject *bigobj, *prevbo;
2133         int i;
2134         PinnedChunk *chunk;
2135         FinalizeEntry *fin;
2136         int count;
2137         TV_DECLARE (atv);
2138         TV_DECLARE (btv);
2139         /* FIXME: only use these values for the precise scan
2140          * note that to_space pointers should be excluded anyway...
2141          */
2142         char *heap_start = NULL;
2143         char *heap_end = (char*)-1;
2144         size_t copy_space_required = 0;
2145
2146         degraded_mode = 0;
2147         DEBUG (1, fprintf (gc_debug_file, "Start major collection %d\n", num_major_gcs));
2148         num_major_gcs++;
2149         /* 
2150          * FIXME: implement Mark/Compact
2151          * Until that is done, we can just apply mostly the same alg as for the nursery:
2152          * this means we need a big section to potentially copy all the other sections, so
2153          * it is not ideal specially with large heaps.
2154          */
2155         if (g_getenv ("MONO_GC_NO_MAJOR")) {
2156                 collect_nursery (0);
2157                 return;
2158         }
2159         /* FIXME: make sure the nursery next_data ptr is updated */
2160         nursery_section->next_data = nursery_real_end;
2161         /* we should also coalesce scanning from sections close to each other
2162          * and deal with pointers outside of the sections later.
2163          */
2164         /* The remsets are not useful for a major collection */
2165         clear_remsets ();
2166         /* world must be stopped already */
2167         TV_GETTIME (atv);
2168         DEBUG (6, fprintf (gc_debug_file, "Pinning from sections\n"));
2169         for (section = section_list; section; section = section->next) {
2170                 section->pin_queue_start = count = section->pin_queue_end = next_pin_slot;
2171                 pin_from_roots (section->data, section->next_data);
2172                 if (count != next_pin_slot) {
2173                         int reduced_to;
2174                         optimize_pin_queue (count);
2175                         DEBUG (6, fprintf (gc_debug_file, "Found %d pinning addresses in section %p (%d-%d)\n", next_pin_slot - count, section, count, next_pin_slot));
2176                         reduced_to = pin_objects_from_addresses (section, pin_queue + count, pin_queue + next_pin_slot, section->data, section->next_data);
2177                         section->pin_queue_end = next_pin_slot = count + reduced_to;
2178                 }
2179                 copy_space_required += (char*)section->next_data - (char*)section->data;
2180         }
2181         /* identify possible pointers to the insize of large objects */
2182         DEBUG (6, fprintf (gc_debug_file, "Pinning from large objects\n"));
2183         for (bigobj = los_object_list; bigobj; bigobj = bigobj->next) {
2184                 count = next_pin_slot;
2185                 pin_from_roots (bigobj->data, (char*)bigobj->data + bigobj->size);
2186                 /* FIXME: this is only valid until we don't optimize the pin queue midway */
2187                 if (next_pin_slot != count) {
2188                         next_pin_slot = count;
2189                         pin_object (bigobj->data);
2190                         DEBUG (6, fprintf (gc_debug_file, "Marked large object %p (%s) size: %d from roots\n", bigobj->data, safe_name (bigobj->data), bigobj->size));
2191                 }
2192         }
2193         /* look for pinned addresses for pinned-alloc objects */
2194         DEBUG (6, fprintf (gc_debug_file, "Pinning from pinned-alloc objects\n"));
2195         for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
2196                 count = next_pin_slot;
2197                 pin_from_roots (chunk->start_data, (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE);
2198                 /* FIXME: this is only valid until we don't optimize the pin queue midway */
2199                 if (next_pin_slot != count) {
2200                         mark_pinned_from_addresses (chunk, pin_queue + count, pin_queue + next_pin_slot);
2201                         next_pin_slot = count;
2202                 }
2203         }
2204
2205         TV_GETTIME (btv);
2206         DEBUG (2, fprintf (gc_debug_file, "Finding pinned pointers: %d in %d usecs\n", next_pin_slot, TV_ELAPSED (atv, btv)));
2207         DEBUG (4, fprintf (gc_debug_file, "Start scan with %d pinned objects\n", next_pin_slot));
2208
2209         /* allocate the big to space */
2210         DEBUG (4, fprintf (gc_debug_file, "Allocate tospace for size: %d\n", copy_space_required));
2211         section = alloc_section (copy_space_required);
2212         to_space = gray_objects = section->next_data;
2213         to_space_end = section->end_data;
2214         to_space_section = section;
2215
2216         /* the old generation doesn't need to be scanned (no remembered sets or card
2217          * table needed either): the only objects that must survive are those pinned and
2218          * those referenced by the precise roots.
2219          * mark any section without pinned objects, so we can free it since we will be able to
2220          * move all the objects.
2221          */
2222         /* the pinned objects are roots (big objects are included in this list, too) */
2223         for (i = 0; i < next_pin_slot; ++i) {
2224                 DEBUG (6, fprintf (gc_debug_file, "Precise object scan %d of pinned %p (%s)\n", i, pin_queue [i], safe_name (pin_queue [i])));
2225                 scan_object (pin_queue [i], heap_start, heap_end);
2226         }
2227         /* registered roots, this includes static fields */
2228         scan_from_registered_roots (heap_start, heap_end);
2229
2230         /* scan the list of objects ready for finalization */
2231         for (fin = fin_ready_list; fin; fin = fin->next) {
2232                 DEBUG (5, fprintf (gc_debug_file, "Scan of fin ready object: %p (%s)\n", fin->object, safe_name (fin->object)));
2233                 fin->object = copy_object (fin->object, heap_start, heap_end);
2234         }
2235         TV_GETTIME (atv);
2236         DEBUG (2, fprintf (gc_debug_file, "Root scan: %d usecs\n", TV_ELAPSED (btv, atv)));
2237
2238         /* we need to go over the big object list to see if any was marked and scan it
2239          * And we need to make this in a loop, considering that objects referenced by finalizable
2240          * objects could reference big objects (this happens in drain_gray_stack ())
2241          */
2242         scan_needed_big_objects (heap_start, heap_end);
2243         /* all the objects in the heap */
2244         drain_gray_stack (heap_start, heap_end);
2245
2246         /* sweep the big objects list */
2247         prevbo = NULL;
2248         for (bigobj = los_object_list; bigobj;) {
2249                 if (object_is_pinned (bigobj->data)) {
2250                         unpin_object (bigobj->data);
2251                         bigobj->scanned = FALSE;
2252                 } else {
2253                         LOSObject *to_free;
2254                         /* not referenced anywhere, so we can free it */
2255                         if (prevbo)
2256                                 prevbo->next = bigobj->next;
2257                         else
2258                                 los_object_list = bigobj->next;
2259                         to_free = bigobj;
2260                         bigobj = bigobj->next;
2261                         free_large_object (to_free);
2262                         continue;
2263                 }
2264                 prevbo = bigobj;
2265                 bigobj = bigobj->next;
2266         }
2267         /* unpin objects from the pinned chunks and free the unmarked ones */
2268         sweep_pinned_objects ();
2269
2270         /* free the unused sections */
2271         prev_section = NULL;
2272         for (section = section_list; section;) {
2273                 /* to_space doesn't need handling here and the nursery is special */
2274                 if (section == to_space_section || section == nursery_section) {
2275                         prev_section = section;
2276                         section = section->next;
2277                         continue;
2278                 }
2279                 /* no pinning object, so the section is free */
2280                 if (section->pin_queue_start == section->pin_queue_end) {
2281                         GCMemSection *to_free;
2282                         if (prev_section)
2283                                 prev_section->next = section->next;
2284                         else
2285                                 section_list = section->next;
2286                         to_free = section;
2287                         section = section->next;
2288                         free_mem_section (to_free);
2289                         continue;
2290                 } else {
2291                         DEBUG (6, fprintf (gc_debug_file, "Section %p has still pinned objects (%d)\n", section, section->pin_queue_end - section->pin_queue_start));
2292                         build_section_fragments (section);
2293                 }
2294                 prev_section = section;
2295                 section = section->next;
2296         }
2297
2298         /* walk the pin_queue, build up the fragment list of free memory, unmark
2299          * pinned objects as we go, memzero() the empty fragments so they are ready for the
2300          * next allocations.
2301          */
2302         build_nursery_fragments (nursery_section->pin_queue_start, nursery_section->pin_queue_end);
2303
2304         /* prepare the pin queue for the next collection */
2305         next_pin_slot = 0;
2306         if (fin_ready_list) {
2307                 DEBUG (4, fprintf (gc_debug_file, "Finalizer-thread wakeup: ready %d\n", num_ready_finalizers));
2308                 mono_gc_finalize_notify ();
2309         }
2310 }
2311
2312 /*
2313  * Allocate a new section of memory to be used as old generation.
2314  */
2315 static GCMemSection*
2316 alloc_section (size_t size)
2317 {
2318         GCMemSection *section;
2319         char *data;
2320         int scan_starts;
2321         size_t new_size = next_section_size;
2322
2323         if (size > next_section_size) {
2324                 new_size = size;
2325                 new_size += pagesize - 1;
2326                 new_size &= ~(pagesize - 1);
2327         }
2328         section_size_used++;
2329         if (section_size_used > 3) {
2330                 section_size_used = 0;
2331                 next_section_size *= 2;
2332                 if (next_section_size > max_section_size)
2333                         next_section_size = max_section_size;
2334         }
2335         section = get_internal_mem (sizeof (GCMemSection));
2336         data = get_os_memory (new_size, TRUE);
2337         section->data = section->next_data = data;
2338         section->size = new_size;
2339         section->end_data = data + new_size;
2340         UPDATE_HEAP_BOUNDARIES (data, section->end_data);
2341         total_alloc += new_size;
2342         DEBUG (2, fprintf (gc_debug_file, "Expanding heap size: %d, total: %d\n", new_size, total_alloc));
2343         section->data = data;
2344         section->size = new_size;
2345         scan_starts = new_size / SCAN_START_SIZE;
2346         section->scan_starts = get_internal_mem (sizeof (char*) * scan_starts);
2347         section->num_scan_start = scan_starts;
2348
2349         /* add to the section list */
2350         section->next = section_list;
2351         section_list = section;
2352
2353         return section;
2354 }
2355
2356 static void
2357 free_mem_section (GCMemSection *section)
2358 {
2359         char *data = section->data;
2360         size_t size = section->size;
2361         DEBUG (2, fprintf (gc_debug_file, "Freed section %p, size %d\n", data, size));
2362         free_os_memory (data, size);
2363         free_internal_mem (section);
2364         total_alloc -= size;
2365 }
2366
2367 /*
2368  * When deciding if it's better to collect or to expand, keep track
2369  * of how much garbage was reclaimed with the last collection: if it's too
2370  * little, expand.
2371  * This is called when we could not allocate a small object.
2372  */
2373 static void __attribute__((noinline))
2374 minor_collect_or_expand_inner (size_t size)
2375 {
2376         int do_minor_collection = 1;
2377
2378         if (!nursery_section) {
2379                 alloc_nursery ();
2380                 return;
2381         }
2382         if (do_minor_collection) {
2383                 stop_world ();
2384                 collect_nursery (size);
2385                 DEBUG (2, fprintf (gc_debug_file, "Heap size: %d, LOS size: %d\n", total_alloc, los_memory_usage));
2386                 restart_world ();
2387                 /* this also sets the proper pointers for the next allocation */
2388                 if (!search_fragment_for_size (size)) {
2389                         int i;
2390                         /* TypeBuilder and MonoMethod are killing mcs with fragmentation */
2391                         DEBUG (1, fprintf (gc_debug_file, "nursery collection didn't find enough room for %d alloc (%d pinned)", size, last_num_pinned));
2392                         for (i = 0; i < last_num_pinned; ++i) {
2393                                 DEBUG (3, fprintf (gc_debug_file, "Bastard pinning obj %p (%s), size: %d\n", pin_queue [i], safe_name (pin_queue [i]), safe_object_get_size (pin_queue [i])));
2394                         }
2395                         degraded_mode = 1;
2396                 }
2397         }
2398         //report_internal_mem_usage ();
2399 }
2400
2401 /*
2402  * ######################################################################
2403  * ########  Memory allocation from the OS
2404  * ######################################################################
2405  * This section of code deals with getting memory from the OS and
2406  * allocating memory for GC-internal data structures.
2407  * Internal memory can be handled with a freelist for small objects.
2408  */
2409
2410 /*
2411  * Allocate a big chunk of memory from the OS (usually 64KB to several megabytes).
2412  * This must not require any lock.
2413  */
2414 static void*
2415 get_os_memory (size_t size, int activate)
2416 {
2417         void *ptr;
2418         unsigned long prot_flags = activate? MONO_MMAP_READ|MONO_MMAP_WRITE: MONO_MMAP_NONE;
2419
2420         prot_flags |= MONO_MMAP_PRIVATE | MONO_MMAP_ANON;
2421         size += pagesize - 1;
2422         size &= ~(pagesize - 1);
2423         ptr = mono_valloc (0, size, prot_flags);
2424         return ptr;
2425 }
2426
2427 /*
2428  * Free the memory returned by get_os_memory (), returning it to the OS.
2429  */
2430 static void
2431 free_os_memory (void *addr, size_t size)
2432 {
2433         munmap (addr, size);
2434 }
2435
2436 /*
2437  * Debug reporting.
2438  */
2439 static void
2440 report_pinned_chunk (PinnedChunk *chunk, int seq) {
2441         void **p;
2442         int i, free_pages, num_free, free_mem;
2443         free_pages = 0;
2444         for (i = 0; i < chunk->num_pages; ++i) {
2445                 if (!chunk->page_sizes [i])
2446                         free_pages++;
2447         }
2448         printf ("Pinned chunk %d at %p, size: %d, pages: %d, free: %d\n", seq, chunk, chunk->num_pages * FREELIST_PAGESIZE, chunk->num_pages, free_pages);
2449         free_mem = FREELIST_PAGESIZE * free_pages;
2450         for (i = 0; i < FREELIST_NUM_SLOTS; ++i) {
2451                 if (!chunk->free_list [i])
2452                         continue;
2453                 num_free = 0;
2454                 p = chunk->free_list [i];
2455                 while (p) {
2456                         num_free++;
2457                         p = *p;
2458                 }
2459                 printf ("\tfree list of size %d, %d items\n", freelist_sizes [i], num_free);
2460                 free_mem += freelist_sizes [i] * num_free;
2461         }
2462         printf ("\tfree memory in chunk: %d\n", free_mem);
2463 }
2464
2465 /*
2466  * Debug reporting.
2467  */
2468 static void
2469 report_internal_mem_usage (void) {
2470         PinnedChunk *chunk;
2471         int i;
2472         printf ("Internal memory usage:\n");
2473         i = 0;
2474         for (chunk = internal_chunk_list; chunk; chunk = chunk->next) {
2475                 report_pinned_chunk (chunk, i++);
2476         }
2477         printf ("Pinned memory usage:\n");
2478         i = 0;
2479         for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
2480                 report_pinned_chunk (chunk, i++);
2481         }
2482 }
2483
2484 /*
2485  * the array of pointers from @start to @end contains conservative
2486  * pointers to objects inside @chunk: mark each referenced object
2487  * with the PIN bit.
2488  */
2489 static void
2490 mark_pinned_from_addresses (PinnedChunk *chunk, void **start, void **end)
2491 {
2492         for (; start < end; start++) {
2493                 char *addr = *start;
2494                 int offset = (char*)addr - (char*)chunk;
2495                 int page = offset / FREELIST_PAGESIZE;
2496                 int obj_offset = page == 0? offset - ((char*)chunk->start_data - (char*)chunk): offset % FREELIST_PAGESIZE;
2497                 int slot_size = chunk->page_sizes [page];
2498                 void **ptr;
2499                 /* the page is not allocated */
2500                 if (!slot_size)
2501                         continue;
2502                 /* would be faster if we restrict the sizes to power of two,
2503                  * but that's a waste of memory: need to measure. it could reduce
2504                  * fragmentation since there are less pages needed, if for example
2505                  * someone interns strings of each size we end up with one page per
2506                  * interned string (still this is just ~40 KB): with more fine-grained sizes
2507                  * this increases the number of used pages.
2508                  */
2509                 if (page == 0) {
2510                         obj_offset /= slot_size;
2511                         obj_offset *= slot_size;
2512                         addr = (char*)chunk->start_data + obj_offset;
2513                 } else {
2514                         obj_offset /= slot_size;
2515                         obj_offset *= slot_size;
2516                         addr = (char*)chunk + page * FREELIST_PAGESIZE + obj_offset;
2517                 }
2518                 ptr = (void**)addr;
2519                 /* if the vtable is inside the chunk it's on the freelist, so skip */
2520                 if (*ptr && (*ptr < (void*)chunk->start_data || *ptr > (void*)((char*)chunk + chunk->num_pages * FREELIST_PAGESIZE))) {
2521                         pin_object (addr);
2522                         DEBUG (6, fprintf (gc_debug_file, "Marked pinned object %p (%s) from roots\n", addr, safe_name (addr)));
2523                 }
2524         }
2525 }
2526
2527 static void
2528 sweep_pinned_objects (void)
2529 {
2530         PinnedChunk *chunk;
2531         int i, obj_size;
2532         char *p, *endp;
2533         void **ptr;
2534         void *end_chunk;
2535         for (chunk = pinned_chunk_list; chunk; chunk = chunk->next) {
2536                 end_chunk = (char*)chunk + chunk->num_pages * FREELIST_PAGESIZE;
2537                 DEBUG (6, fprintf (gc_debug_file, "Sweeping pinned chunk %p (ranhe: %p-%p)\n", chunk, chunk->start_data, end_chunk));
2538                 for (i = 0; i < chunk->num_pages; ++i) {
2539                         obj_size = chunk->page_sizes [i];
2540                         if (!obj_size)
2541                                 continue;
2542                         p = i? (char*)chunk + i * FREELIST_PAGESIZE: chunk->start_data;
2543                         endp = i? p + FREELIST_PAGESIZE: (char*)chunk + FREELIST_PAGESIZE;
2544                         DEBUG (6, fprintf (gc_debug_file, "Page %d (size: %d, range: %p-%p)\n", i, obj_size, p, endp));
2545                         while (p + obj_size <= endp) {
2546                                 ptr = (void**)p;
2547                                 DEBUG (9, fprintf (gc_debug_file, "Considering %p (vtable: %p)\n", ptr, *ptr));
2548                                 /* if the first word (the vtable) is outside the chunk we have an object */
2549                                 if (*ptr && (*ptr < (void*)chunk || *ptr >= end_chunk)) {
2550                                         if (object_is_pinned (ptr)) {
2551                                                 unpin_object (ptr);
2552                                                 DEBUG (6, fprintf (gc_debug_file, "Unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
2553                                         } else {
2554                                                 /* FIXME: add to freelist */
2555                                                 DEBUG (6, fprintf (gc_debug_file, "Going to free unmarked pinned object %p (%s)\n", ptr, safe_name (ptr)));
2556                                         }
2557                                 }
2558                                 p += obj_size;
2559                         }
2560                 }
2561         }
2562 }
2563
2564 /*
2565  * Find the slot number in the freelist for memory chunks that
2566  * can contain @size objects.
2567  */
2568 static int
2569 slot_for_size (size_t size)
2570 {
2571         int slot;
2572         /* do a binary search or lookup table later. */
2573         for (slot = 0; slot < FREELIST_NUM_SLOTS; ++slot) {
2574                 if (freelist_sizes [slot] >= size)
2575                         return slot;
2576         }
2577         g_assert_not_reached ();
2578         return -1;
2579 }
2580
2581 /*
2582  * Build a free list for @size memory chunks from the memory area between
2583  * start_page and end_page.
2584  */
2585 static void
2586 build_freelist (PinnedChunk *chunk, int slot, int size, char *start_page, char *end_page)
2587 {
2588         void **p, **end;
2589         int count = 0;
2590         /*g_print ("building freelist for slot %d, size %d in %p\n", slot, size, chunk);*/
2591         p = (void**)start_page;
2592         end = (void**)(end_page - size);
2593         g_assert (!chunk->free_list [slot]);
2594         chunk->free_list [slot] = p;
2595         while ((char*)p + size <= (char*)end) {
2596                 count++;
2597                 *p = (void*)((char*)p + size);
2598                 p = *p;
2599         }
2600         *p = NULL;
2601         /*g_print ("%d items created, max: %d\n", count, (end_page - start_page) / size);*/
2602 }
2603
2604 static PinnedChunk*
2605 alloc_pinned_chunk (size_t size)
2606 {
2607         PinnedChunk *chunk;
2608         int offset;
2609
2610         size += pagesize; /* at least one page */
2611         size += pagesize - 1;
2612         size &= ~(pagesize - 1);
2613         if (size < PINNED_CHUNK_MIN_SIZE * 2)
2614                 size = PINNED_CHUNK_MIN_SIZE * 2;
2615         chunk = get_os_memory (size, TRUE);
2616         UPDATE_HEAP_BOUNDARIES (chunk, ((char*)chunk + size));
2617         total_alloc += size;
2618
2619         /* setup the bookeeping fields */
2620         chunk->num_pages = size / FREELIST_PAGESIZE;
2621         offset = G_STRUCT_OFFSET (PinnedChunk, data);
2622         chunk->page_sizes = (void*)((char*)chunk + offset);
2623         offset += sizeof (int) * chunk->num_pages;
2624         offset += ALLOC_ALIGN - 1;
2625         offset &= ~(ALLOC_ALIGN - 1);
2626         chunk->free_list = (void*)((char*)chunk + offset);
2627         offset += sizeof (void*) * FREELIST_NUM_SLOTS;
2628         offset += ALLOC_ALIGN - 1;
2629         offset &= ~(ALLOC_ALIGN - 1);
2630         chunk->start_data = (void*)((char*)chunk + offset);
2631
2632         /* allocate the first page to the freelist */
2633         chunk->page_sizes [0] = PINNED_FIRST_SLOT_SIZE;
2634         build_freelist (chunk, slot_for_size (PINNED_FIRST_SLOT_SIZE), PINNED_FIRST_SLOT_SIZE, chunk->start_data, ((char*)chunk + FREELIST_PAGESIZE));
2635         DEBUG (4, fprintf (gc_debug_file, "Allocated pinned chunk %p, size: %d\n", chunk, size));
2636         min_pinned_chunk_addr = MIN (min_pinned_chunk_addr, (char*)chunk->start_data);
2637         max_pinned_chunk_addr = MAX (max_pinned_chunk_addr, ((char*)chunk + size));
2638         return chunk;
2639 }
2640
2641 /* assumes freelist for slot is empty, so try to alloc a new page */
2642 static void*
2643 get_chunk_freelist (PinnedChunk *chunk, int slot)
2644 {
2645         int i;
2646         void **p;
2647         p = chunk->free_list [slot];
2648         if (p) {
2649                 chunk->free_list [slot] = *p;
2650                 return p;
2651         }
2652         for (i = 0; i < chunk->num_pages; ++i) {
2653                 int size;
2654                 if (chunk->page_sizes [i])
2655                         continue;
2656                 size = freelist_sizes [slot];
2657                 chunk->page_sizes [i] = size;
2658                 build_freelist (chunk, slot, size, (char*)chunk + FREELIST_PAGESIZE * i, (char*)chunk + FREELIST_PAGESIZE * (i + 1));
2659                 break;
2660         }
2661         /* try again */
2662         p = chunk->free_list [slot];
2663         if (p) {
2664                 chunk->free_list [slot] = *p;
2665                 return p;
2666         }
2667         return NULL;
2668 }
2669
2670 static void*
2671 alloc_from_freelist (size_t size)
2672 {
2673         int slot;
2674         void *res = NULL;
2675         PinnedChunk *pchunk;
2676         slot = slot_for_size (size);
2677         /*g_print ("using slot %d for size %d (slot size: %d)\n", slot, size, freelist_sizes [slot]);*/
2678         g_assert (size <= freelist_sizes [slot]);
2679         for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->next) {
2680                 void **p = pchunk->free_list [slot];
2681                 if (p) {
2682                         /*g_print ("found freelist for slot %d in chunk %p, returning %p, next %p\n", slot, pchunk, p, *p);*/
2683                         pchunk->free_list [slot] = *p;
2684                         return p;
2685                 }
2686         }
2687         for (pchunk = pinned_chunk_list; pchunk; pchunk = pchunk->next) {
2688                 res = get_chunk_freelist (pchunk, slot);
2689                 if (res)
2690                         return res;
2691         }
2692         pchunk = alloc_pinned_chunk (size);
2693         /* FIXME: handle OOM */
2694         pchunk->next = pinned_chunk_list;
2695         pinned_chunk_list = pchunk;
2696         res = get_chunk_freelist (pchunk, slot);
2697         return res;
2698 }
2699
2700 /* used for the GC-internal data structures */
2701 /* FIXME: add support for bigger sizes by allocating more than one page
2702  * in the chunk.
2703  */
2704 static void*
2705 get_internal_mem (size_t size)
2706 {
2707         return calloc (1, size);
2708         int slot;
2709         void *res = NULL;
2710         PinnedChunk *pchunk;
2711         slot = slot_for_size (size);
2712         g_assert (size <= freelist_sizes [slot]);
2713         for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->next) {
2714                 void **p = pchunk->free_list [slot];
2715                 if (p) {
2716                         pchunk->free_list [slot] = *p;
2717                         return p;
2718                 }
2719         }
2720         for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->next) {
2721                 res = get_chunk_freelist (pchunk, slot);
2722                 if (res)
2723                         return res;
2724         }
2725         pchunk = alloc_pinned_chunk (size);
2726         /* FIXME: handle OOM */
2727         pchunk->next = internal_chunk_list;
2728         internal_chunk_list = pchunk;
2729         res = get_chunk_freelist (pchunk, slot);
2730         return res;
2731 }
2732
2733 static void
2734 free_internal_mem (void *addr)
2735 {
2736         free (addr);
2737         return;
2738         PinnedChunk *pchunk;
2739         for (pchunk = internal_chunk_list; pchunk; pchunk = pchunk->next) {
2740                 /*printf ("trying to free %p in %p (pages: %d)\n", addr, pchunk, pchunk->num_pages);*/
2741                 if (addr >= (void*)pchunk && (char*)addr < (char*)pchunk + pchunk->num_pages * FREELIST_PAGESIZE) {
2742                         int offset = (char*)addr - (char*)pchunk;
2743                         int page = offset / FREELIST_PAGESIZE;
2744                         int slot = slot_for_size (pchunk->page_sizes [page]);
2745                         void **p = addr;
2746                         *p = pchunk->free_list [slot];
2747                         pchunk->free_list [slot] = p;
2748                         return;
2749                 }
2750         }
2751         printf ("free of %p failed\n", addr);
2752         g_assert_not_reached ();
2753 }
2754
2755 /*
2756  * ######################################################################
2757  * ########  Object allocation
2758  * ######################################################################
2759  * This section of code deals with allocating memory for objects.
2760  * There are several ways:
2761  * *) allocate large objects
2762  * *) allocate normal objects
2763  * *) fast lock-free allocation
2764  * *) allocation of pinned objects
2765  */
2766
2767 static void
2768 free_large_object (LOSObject *obj)
2769 {
2770         size_t size = obj->size;
2771         DEBUG (4, fprintf (gc_debug_file, "Freed large object %p, size %d\n", obj->data, obj->size));
2772
2773         los_memory_usage -= size;
2774         size += sizeof (LOSObject);
2775         size += pagesize - 1;
2776         size &= ~(pagesize - 1);
2777         total_alloc -= size;
2778         los_num_objects--;
2779         free_os_memory (obj, size);
2780 }
2781
2782 /*
2783  * Objects with size >= 64KB are allocated in the large object space.
2784  * They are currently kept track of with a linked list.
2785  * They don't move, so there is no need to pin them during collection
2786  * and we avoid the memcpy overhead.
2787  */
2788 static void* __attribute__((noinline))
2789 alloc_large_inner (MonoVTable *vtable, size_t size)
2790 {
2791         LOSObject *obj;
2792         void **vtslot;
2793         size_t alloc_size;
2794         int just_did_major_gc = FALSE;
2795
2796         if (los_memory_usage > next_los_collection) {
2797                 DEBUG (4, fprintf (gc_debug_file, "Should trigger major collection: req size %d (los already: %u, limit: %u)\n", size, los_memory_usage, next_los_collection));
2798                 just_did_major_gc = TRUE;
2799                 stop_world ();
2800                 major_collection ();
2801                 restart_world ();
2802                 /* later increase based on a percent of the heap size */
2803                 next_los_collection = los_memory_usage + 5*1024*1024;
2804         }
2805         alloc_size = size;
2806         alloc_size += sizeof (LOSObject);
2807         alloc_size += pagesize - 1;
2808         alloc_size &= ~(pagesize - 1);
2809         /* FIXME: handle OOM */
2810         obj = get_os_memory (alloc_size, TRUE);
2811         obj->size = size;
2812         vtslot = (void**)obj->data;
2813         *vtslot = vtable;
2814         total_alloc += alloc_size;
2815         UPDATE_HEAP_BOUNDARIES (obj->data, (char*)obj->data + size);
2816         obj->next = los_object_list;
2817         los_object_list = obj;
2818         los_memory_usage += size;
2819         los_num_objects++;
2820         DEBUG (4, fprintf (gc_debug_file, "Allocated large object %p, vtable: %p (%s), size: %d\n", obj->data, vtable, vtable->klass->name, size));
2821         return obj->data;
2822 }
2823
2824 /* check if we have a suitable fragment in nursery_fragments to be able to allocate
2825  * an object of size @size
2826  * Return FALSE if not found (which means we need a collection)
2827  */
2828 static gboolean
2829 search_fragment_for_size (size_t size)
2830 {
2831         Fragment *frag, *prev;
2832         DEBUG (4, fprintf (gc_debug_file, "Searching nursery fragment %p, size: %d\n", nursery_frag_real_end, size));
2833         prev = NULL;
2834         for (frag = nursery_fragments; frag; frag = frag->next) {
2835                 if (size <= (frag->fragment_end - frag->fragment_start)) {
2836                         /* remove from the list */
2837                         if (prev)
2838                                 prev->next = frag->next;
2839                         else
2840                                 nursery_fragments = frag->next;
2841                         nursery_next = frag->fragment_start;
2842                         nursery_frag_real_end = frag->fragment_end;
2843                         nursery_temp_end = MIN (nursery_frag_real_end, nursery_next + size + SCAN_START_SIZE);
2844
2845                         DEBUG (4, fprintf (gc_debug_file, "Using nursery fragment %p-%p, size: %d (req: %d)\n", nursery_next, nursery_frag_real_end, nursery_frag_real_end - nursery_next, size));
2846                         frag->next = fragment_freelist;
2847                         fragment_freelist = frag;
2848                         return TRUE;
2849                 }
2850                 prev = frag;
2851         }
2852         return FALSE;
2853 }
2854
2855 /*
2856  * size is already rounded up and we hold the GC lock.
2857  */
2858 static void*
2859 alloc_degraded (MonoVTable *vtable, size_t size)
2860 {
2861         GCMemSection *section;
2862         void **p = NULL;
2863         for (section = section_list; section; section = section->next) {
2864                 if (section != nursery_section && (section->end_data - section->next_data) >= size) {
2865                         p = (void**)section->next_data;
2866                         break;
2867                 }
2868         }
2869         if (!p) {
2870                 section = alloc_section (nursery_section->size * 4);
2871                 /* FIXME: handle OOM */
2872                 p = (void**)section->next_data;
2873         }
2874         section->next_data += size;
2875         degraded_mode += size;
2876         DEBUG (3, fprintf (gc_debug_file, "Allocated (degraded) object %p, vtable: %p (%s), size: %d in section %p\n", p, vtable, vtable->klass->name, size, section));
2877         *p = vtable;
2878         return p;
2879 }
2880
2881 /*
2882  * Provide a variant that takes just the vtable for small fixed-size objects.
2883  * The aligned size is already computed and stored in vt->gc_descr.
2884  * Note: every SCAN_START_SIZE or so we are given the chance to do some special
2885  * processing. We can keep track of where objects start, for example,
2886  * so when we scan the thread stacks for pinned objects, we can start
2887  * a search for the pinned object in SCAN_START_SIZE chunks.
2888  */
2889 void*
2890 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
2891 {
2892         /* FIXME: handle OOM */
2893         void **p;
2894         int dummy;
2895         size += ALLOC_ALIGN - 1;
2896         size &= ~(ALLOC_ALIGN - 1);
2897
2898         g_assert (vtable->gc_descr);
2899         LOCK_GC;
2900
2901         p = (void**)nursery_next;
2902         /* FIXME: handle overflow */
2903         nursery_next += size;
2904         if (nursery_next >= nursery_temp_end) {
2905                 /* there are two cases: the object is too big or we need to collect */
2906                 /* there can be another case (from ORP), if we cooperate with the runtime a bit:
2907                  * objects that need finalizers can have the high bit set in their size
2908                  * so the above check fails and we can readily add the object to the queue.
2909                  * This avoids taking again the GC lock when registering, but this is moot when
2910                  * doing thread-local allocation, so it may not be a good idea.
2911                  */
2912                 if (size > MAX_SMALL_OBJ_SIZE) {
2913                         /* get ready for possible collection */
2914                         update_current_thread_stack (&dummy);
2915                         nursery_next -= size;
2916                         p = alloc_large_inner (vtable, size);
2917                 } else {
2918                         if (nursery_next >= nursery_frag_real_end) {
2919                                 nursery_next -= size;
2920                                 /* when running in degraded mode, we continue allocing that way
2921                                  * for a while, to decrease the number of useless nursery collections.
2922                                  */
2923                                 if (degraded_mode && degraded_mode < DEFAULT_NURSERY_SIZE) {
2924                                         p = alloc_degraded (vtable, size);
2925                                         UNLOCK_GC;
2926                                         return p;
2927                                 }
2928                                 if (!search_fragment_for_size (size)) {
2929                                         /* get ready for possible collection */
2930                                         update_current_thread_stack (&dummy);
2931                                         minor_collect_or_expand_inner (size);
2932                                         if (degraded_mode) {
2933                                                 p = alloc_degraded (vtable, size);
2934                                                 UNLOCK_GC;
2935                                                 return p;
2936                                         }
2937                                 }
2938                                 /* nursery_next changed by minor_collect_or_expand_inner () */
2939                                 p = (void*)nursery_next;
2940                                 nursery_next += size;
2941                                 if (nursery_next > nursery_temp_end) {
2942                                         // no space left
2943                                         g_assert (0);
2944                                 }
2945                         } else {
2946                                 /* record the scan start so we can find pinned objects more easily */
2947                                 nursery_section->scan_starts [((char*)p - (char*)nursery_section->data)/SCAN_START_SIZE] = (char*)p;
2948                                 /* we just bump nursery_temp_end as well */
2949                                 nursery_temp_end = MIN (nursery_frag_real_end, nursery_next + SCAN_START_SIZE);
2950                                 DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", nursery_next, nursery_temp_end));
2951                         }
2952                 }
2953         }
2954         DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %d\n", p, vtable, vtable->klass->name, size));
2955         *p = vtable;
2956
2957         UNLOCK_GC;
2958
2959         return p;
2960 }
2961
2962 /*
2963  * To be used for interned strings and possibly MonoThread, reflection handles.
2964  * We may want to explicitly free these objects.
2965  */
2966 void*
2967 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
2968 {
2969         /* FIXME: handle OOM */
2970         void **p;
2971         size += ALLOC_ALIGN - 1;
2972         size &= ~(ALLOC_ALIGN - 1);
2973         LOCK_GC;
2974         if (size > MAX_FREELIST_SIZE) {
2975                 update_current_thread_stack (&p);
2976                 /* large objects are always pinned anyway */
2977                 p = alloc_large_inner (vtable, size);
2978         } else {
2979                 p = alloc_from_freelist (size);
2980                 memset (p, 0, size);
2981         }
2982         DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %d\n", p, vtable, vtable->klass->name, size));
2983         *p = vtable;
2984         UNLOCK_GC;
2985         return p;
2986 }
2987
2988 /*
2989  * ######################################################################
2990  * ########  Finalization support
2991  * ######################################################################
2992  */
2993
2994 /*
2995  * this is valid for the nursery: if the object has been forwarded it means it's
2996  * still refrenced from a root. If it is pinned it's still alive as well.
2997  * Return TRUE if @obj is ready to be finalized.
2998  */
2999 #define object_is_fin_ready(obj) (!object_is_pinned (obj) && !object_is_forwarded (obj))
3000
3001 static void
3002 finalize_in_range (char *start, char *end)
3003 {
3004         FinalizeEntry *entry, *prev;
3005         int i;
3006         if (no_finalize)
3007                 return;
3008         for (i = 0; i < finalizable_hash_size; ++i) {
3009                 prev = NULL;
3010                 for (entry = finalizable_hash [i]; entry;) {
3011                         if (entry->object >= start && entry->object < end && (entry->object < to_space || entry->object >= to_space_end)) {
3012                                 if (object_is_fin_ready (entry->object)) {
3013                                         char *from;
3014                                         FinalizeEntry *next;
3015                                         /* remove and put in fin_ready_list */
3016                                         if (prev)
3017                                                 prev->next = entry->next;
3018                                         else
3019                                                 finalizable_hash [i] = entry->next;
3020                                         next = entry->next;
3021                                         num_ready_finalizers++;
3022                                         num_registered_finalizers--;
3023                                         entry->next = fin_ready_list;
3024                                         fin_ready_list = entry;
3025                                         /* Make it survive */
3026                                         from = entry->object;
3027                                         entry->object = copy_object (entry->object, start, end);
3028                                         DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", entry->object, safe_name (entry->object), from, num_ready_finalizers, num_registered_finalizers));
3029                                         entry = next;
3030                                         continue;
3031                                 } else {
3032                                         /* update pointer */
3033                                         DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s)\n", entry->object, safe_name (entry->object)));
3034                                         entry->object = copy_object (entry->object, start, end);
3035                                 }
3036                         }
3037                         prev = entry;
3038                         entry = entry->next;
3039                 }
3040         }
3041 }
3042
3043 static void
3044 null_link_in_range (char *start, char *end)
3045 {
3046         FinalizeEntry *entry, *prev;
3047         int i;
3048         for (i = 0; i < disappearing_link_hash_size; ++i) {
3049                 prev = NULL;
3050                 for (entry = disappearing_link_hash [i]; entry;) {
3051                         if (entry->object >= start && entry->object < end && (entry->object < to_space || entry->object >= to_space_end)) {
3052                                 if (object_is_fin_ready (entry->object)) {
3053                                         void **p = entry->data;
3054                                         FinalizeEntry *old;
3055                                         *p = NULL;
3056                                         /* remove from list */
3057                                         if (prev)
3058                                                 prev->next = entry->next;
3059                                         else
3060                                                 disappearing_link_hash [i] = entry->next;
3061                                         DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", p, entry->object));
3062                                         old = entry->next;
3063                                         free_internal_mem (entry);
3064                                         entry = old;
3065                                         num_disappearing_links--;
3066                                         continue;
3067                                 } else {
3068                                         void **link;
3069                                         /* update pointer if it's moved
3070                                          * FIXME: what if an object is moved earlier?
3071                                          */
3072                                         entry->object = copy_object (entry->object, start, end);
3073                                         DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", entry->data, entry->object));
3074                                         link = entry->data;
3075                                         *link = entry->object;
3076                                 }
3077                         }
3078                         prev = entry;
3079                         entry = entry->next;
3080                 }
3081         }
3082 }
3083
3084 /**
3085  * mono_gc_finalizers_for_domain:
3086  * @domain: the unloading appdomain
3087  * @out_array: output array
3088  * @out_size: size of output array
3089  *
3090  * Store inside @out_array up to @out_size objects that belong to the unloading
3091  * appdomain @domain. Returns the number of stored items. Can be called repeteadly
3092  * until it returns 0.
3093  * The items are removed from the finalizer data structure, so the caller is supposed
3094  * to finalize them.
3095  * @out_array should be on the stack to allow the GC to know the objects are still alive.
3096  */
3097 int
3098 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
3099 {
3100         FinalizeEntry *entry, *prev;
3101         int i, count;
3102         if (no_finalize || !out_size || !out_array)
3103                 return 0;
3104         count = 0;
3105         LOCK_GC;
3106         for (i = 0; i < finalizable_hash_size; ++i) {
3107                 prev = NULL;
3108                 for (entry = finalizable_hash [i]; entry;) {
3109                         if (mono_object_domain (entry->object) == domain) {
3110                                 FinalizeEntry *next;
3111                                 /* remove and put in out_array */
3112                                 if (prev)
3113                                         prev->next = entry->next;
3114                                 else
3115                                         finalizable_hash [i] = entry->next;
3116                                 next = entry->next;
3117                                 num_registered_finalizers--;
3118                                 out_array [count ++] = entry->object;
3119                                 DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", entry->object, safe_name (entry->object), num_ready_finalizers, num_registered_finalizers));
3120                                 entry = next;
3121                                 if (count == out_size) {
3122                                         UNLOCK_GC;
3123                                         return count;
3124                                 }
3125                                 continue;
3126                         }
3127                         prev = entry;
3128                         entry = entry->next;
3129                 }
3130         }
3131         UNLOCK_GC;
3132         return count;
3133 }
3134
3135 static void
3136 rehash_fin_table (void)
3137 {
3138         int i;
3139         unsigned int hash;
3140         FinalizeEntry **new_hash;
3141         FinalizeEntry *entry, *next;
3142         int new_size = g_spaced_primes_closest (num_registered_finalizers);
3143
3144         new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*));
3145         for (i = 0; i < finalizable_hash_size; ++i) {
3146                 for (entry = finalizable_hash [i]; entry; entry = next) {
3147                         hash = mono_object_hash (entry->object) % new_size;
3148                         next = entry->next;
3149                         entry->next = new_hash [hash];
3150                         new_hash [hash] = entry;
3151                 }
3152         }
3153         free_internal_mem (finalizable_hash);
3154         finalizable_hash = new_hash;
3155         finalizable_hash_size = new_size;
3156 }
3157
3158 void
3159 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
3160 {
3161         FinalizeEntry *entry, *prev;
3162         unsigned int hash;
3163         if (no_finalize)
3164                 return;
3165         hash = mono_object_hash (obj);
3166         LOCK_GC;
3167         if (num_registered_finalizers >= finalizable_hash_size * 2)
3168                 rehash_fin_table ();
3169         hash %= finalizable_hash_size;
3170         prev = NULL;
3171         for (entry = finalizable_hash [hash]; entry; entry = entry->next) {
3172                 if (entry->object == obj) {
3173                         if (user_data) {
3174                                 entry->data = user_data;
3175                         } else {
3176                                 /* remove from the list */
3177                                 if (prev)
3178                                         prev->next = entry->next;
3179                                 else
3180                                         finalizable_hash [hash] = entry->next;
3181                                 num_registered_finalizers--;
3182                                 DEBUG (5, fprintf (gc_debug_file, "Removed finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, num_registered_finalizers));
3183                                 free_internal_mem (entry);
3184                         }
3185                         UNLOCK_GC;
3186                         return;
3187                 }
3188                 prev = entry;
3189         }
3190         if (!user_data) {
3191                 /* request to deregister, but already out of the list */
3192                 UNLOCK_GC;
3193                 return;
3194         }
3195         entry = get_internal_mem (sizeof (FinalizeEntry));
3196         entry->object = obj;
3197         entry->data = user_data;
3198         entry->next = finalizable_hash [hash];
3199         finalizable_hash [hash] = entry;
3200         num_registered_finalizers++;
3201         DEBUG (5, fprintf (gc_debug_file, "Added finalizer %p for object: %p (%s) (%d)\n", entry, obj, obj->vtable->klass->name, num_registered_finalizers));
3202         UNLOCK_GC;
3203 }
3204
3205 static void
3206 rehash_dislink (void)
3207 {
3208         int i;
3209         unsigned int hash;
3210         FinalizeEntry **new_hash;
3211         FinalizeEntry *entry, *next;
3212         int new_size = g_spaced_primes_closest (num_disappearing_links);
3213
3214         new_hash = get_internal_mem (new_size * sizeof (FinalizeEntry*));
3215         for (i = 0; i < disappearing_link_hash_size; ++i) {
3216                 for (entry = disappearing_link_hash [i]; entry; entry = next) {
3217                         hash = mono_aligned_addr_hash (entry->data) % new_size;
3218                         next = entry->next;
3219                         entry->next = new_hash [hash];
3220                         new_hash [hash] = entry;
3221                 }
3222         }
3223         free_internal_mem (disappearing_link_hash);
3224         disappearing_link_hash = new_hash;
3225         disappearing_link_hash_size = new_size;
3226 }
3227
3228 static void
3229 mono_gc_register_disappearing_link (MonoObject *obj, void *link)
3230 {
3231         FinalizeEntry *entry, *prev;
3232         unsigned int hash;
3233         LOCK_GC;
3234
3235         if (num_disappearing_links >= disappearing_link_hash_size * 2)
3236                 rehash_dislink ();
3237         /* FIXME: add check that link is not in the heap */
3238         hash = mono_aligned_addr_hash (link) % disappearing_link_hash_size;
3239         entry = disappearing_link_hash [hash];
3240         prev = NULL;
3241         for (; entry; entry = entry->next) {
3242                 /* link already added */
3243                 if (link == entry->data) {
3244                         /* NULL obj means remove */
3245                         if (obj == NULL) {
3246                                 if (prev)
3247                                         prev->next = entry->next;
3248                                 else
3249                                         disappearing_link_hash [hash] = entry->next;
3250                                 num_disappearing_links--;
3251                                 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d)\n", entry, num_disappearing_links));
3252                                 free_internal_mem (entry);
3253                         } else {
3254                                 entry->object = obj; /* we allow the change of object */
3255                         }
3256                         UNLOCK_GC;
3257                         return;
3258                 }
3259                 prev = entry;
3260         }
3261         entry = get_internal_mem (sizeof (FinalizeEntry));
3262         entry->object = obj;
3263         entry->data = link;
3264         entry->next = disappearing_link_hash [hash];
3265         disappearing_link_hash [hash] = entry;
3266         num_disappearing_links++;
3267         DEBUG (5, fprintf (gc_debug_file, "Added dislink %p for object: %p (%s) at %p\n", entry, obj, obj->vtable->klass->name, link));
3268         UNLOCK_GC;
3269 }
3270
3271 int
3272 mono_gc_invoke_finalizers (void)
3273 {
3274         FinalizeEntry *entry;
3275         int count = 0;
3276         void *obj;
3277         /* FIXME: batch to reduce lock contention */
3278         while (fin_ready_list) {
3279                 LOCK_GC;
3280                 entry = fin_ready_list;
3281                 if (entry) {
3282                         fin_ready_list = entry->next;
3283                         num_ready_finalizers--;
3284                         obj = entry->object;
3285                         DEBUG (7, fprintf (gc_debug_file, "Finalizing object %p (%s)\n", obj, safe_name (obj)));
3286                 }
3287                 UNLOCK_GC;
3288                 if (entry) {
3289                         void (*callback)(void *, void*) = entry->data;
3290                         entry->next = NULL;
3291                         obj = entry->object;
3292                         count++;
3293                         /* the object is on the stack so it is pinned */
3294                         /*g_print ("Calling finalizer for object: %p (%s)\n", entry->object, safe_name (entry->object));*/
3295                         callback (obj, NULL);
3296                         free_internal_mem (entry);
3297                 }
3298         }
3299         return count;
3300 }
3301
3302 gboolean
3303 mono_gc_pending_finalizers (void)
3304 {
3305         return fin_ready_list != NULL;
3306 }
3307
3308 /* Negative value to remove */
3309 void
3310 mono_gc_add_memory_pressure (gint64 value)
3311 {
3312         /* FIXME: Use interlocked functions */
3313         LOCK_GC;
3314         memory_pressure += value;
3315         UNLOCK_GC;
3316 }
3317
3318 /*
3319  * ######################################################################
3320  * ########  registered roots support
3321  * ######################################################################
3322  */
3323
3324 static void
3325 rehash_roots (void)
3326 {
3327         int i;
3328         unsigned int hash;
3329         RootRecord **new_hash;
3330         RootRecord *entry, *next;
3331         int new_size = g_spaced_primes_closest (num_roots_entries);
3332
3333         new_hash = get_internal_mem (new_size * sizeof (RootRecord*));
3334         for (i = 0; i < roots_hash_size; ++i) {
3335                 for (entry = roots_hash [i]; entry; entry = next) {
3336                         hash = mono_aligned_addr_hash (entry->start_root) % new_size;
3337                         next = entry->next;
3338                         entry->next = new_hash [hash];
3339                         new_hash [hash] = entry;
3340                 }
3341         }
3342         free_internal_mem (roots_hash);
3343         roots_hash = new_hash;
3344         roots_hash_size = new_size;
3345 }
3346
3347 /*
3348  * We do not coalesce roots.
3349  */
3350 int
3351 mono_gc_register_root (char *start, size_t size, void *descr)
3352 {
3353         RootRecord *new_root;
3354         unsigned int hash = mono_aligned_addr_hash (start);
3355         LOCK_GC;
3356         if (num_roots_entries >= roots_hash_size * 2)
3357                 rehash_roots ();
3358         hash %= roots_hash_size;
3359         for (new_root = roots_hash [hash]; new_root; new_root = new_root->next) {
3360                 /* we allow changing the size and the descriptor (for thread statics etc) */
3361                 if (new_root->start_root == start) {
3362                         size_t old_size = new_root->end_root - new_root->start_root;
3363                         new_root->end_root = new_root->start_root + size;
3364                         new_root->root_desc = (mword)descr;
3365                         roots_size += size;
3366                         roots_size -= old_size;
3367                         UNLOCK_GC;
3368                         return TRUE;
3369                 }
3370         }
3371         new_root = get_internal_mem (sizeof (RootRecord));
3372         if (new_root) {
3373                 new_root->start_root = start;
3374                 new_root->end_root = new_root->start_root + size;
3375                 new_root->root_desc = (mword)descr;
3376                 roots_size += size;
3377                 num_roots_entries++;
3378                 new_root->next = roots_hash [hash];
3379                 roots_hash [hash] = new_root;
3380                 DEBUG (3, fprintf (gc_debug_file, "Added root %p for range: %p-%p, descr: %p  (%d/%d bytes)\n", new_root, new_root->start_root, new_root->end_root, descr, (int)size, (int)roots_size));
3381         } else {
3382                 UNLOCK_GC;
3383                 return FALSE;
3384         }
3385         UNLOCK_GC;
3386         return TRUE;
3387 }
3388
3389 void
3390 mono_gc_deregister_root (char* addr)
3391 {
3392         RootRecord *tmp, *prev = NULL;
3393         unsigned int hash = mono_aligned_addr_hash (addr);
3394         LOCK_GC;
3395         hash %= roots_hash_size;
3396         tmp = roots_hash [hash];
3397         while (tmp) {
3398                 if (tmp->start_root == (char*)addr) {
3399                         if (prev)
3400                                 prev->next = tmp->next;
3401                         else
3402                                 roots_hash [hash] = tmp->next;
3403                         roots_size -= (tmp->end_root - tmp->start_root);
3404                         num_roots_entries--;
3405                         DEBUG (3, fprintf (gc_debug_file, "Removed root %p for range: %p-%p\n", tmp, tmp->start_root, tmp->end_root));
3406                         free_internal_mem (tmp);
3407                         break;
3408                 }
3409                 prev = tmp;
3410                 tmp = tmp->next;
3411         }
3412         UNLOCK_GC;
3413 }
3414
3415 /*
3416  * ######################################################################
3417  * ########  Thread handling (stop/start code)
3418  * ######################################################################
3419  */
3420
3421 /* eventually share with MonoThread? */
3422 typedef struct _SgenThreadInfo SgenThreadInfo;
3423
3424 struct _SgenThreadInfo {
3425         SgenThreadInfo *next;
3426         ARCH_THREAD_TYPE id;
3427         unsigned int stop_count; /* to catch duplicate signals */
3428         int signal;
3429         int skip;
3430         void *stack_end;
3431         void *stack_start;
3432         RememberedSet *remset;
3433 };
3434
3435 /* FIXME: handle large/small config */
3436 #define THREAD_HASH_SIZE 11
3437 #define HASH_PTHREAD_T(id) (((unsigned int)(id) >> 4) * 2654435761u)
3438
3439 static SgenThreadInfo* thread_table [THREAD_HASH_SIZE];
3440
3441 #if USE_SIGNAL_BASED_START_STOP_WORLD
3442
3443 static sem_t suspend_ack_semaphore;
3444 static unsigned int global_stop_count = 0;
3445 static int suspend_signal_num = SIGPWR;
3446 static int restart_signal_num = SIGXCPU;
3447 static sigset_t suspend_signal_mask;
3448 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
3449
3450 /* LOCKING: assumes the GC lock is held */
3451 static SgenThreadInfo*
3452 thread_info_lookup (ARCH_THREAD_TYPE id)
3453 {
3454         unsigned int hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
3455         SgenThreadInfo *info;
3456
3457         info = thread_table [hash];
3458         while (info && !ARCH_THREAD_EQUALS (info->id, id)) {
3459                 info = info->next;
3460         }
3461         return info;
3462 }
3463
3464 static void
3465 update_current_thread_stack (void *start)
3466 {
3467         void *ptr = cur_thread_regs;
3468         SgenThreadInfo *info = thread_info_lookup (ARCH_GET_THREAD ());
3469         info->stack_start = align_pointer (&ptr);
3470         ARCH_STORE_REGS (ptr);
3471 }
3472
3473 static const char*
3474 signal_desc (int signum)
3475 {
3476         if (signum == suspend_signal_num)
3477                 return "suspend";
3478         if (signum == restart_signal_num)
3479                 return "restart";
3480         return "unknown";
3481 }
3482
3483 /* LOCKING: assumes the GC lock is held */
3484 static int
3485 thread_handshake (int signum)
3486 {
3487         int count, i, result;
3488         SgenThreadInfo *info;
3489         pthread_t me = pthread_self ();
3490
3491         count = 0;
3492         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
3493                 for (info = thread_table [i]; info; info = info->next) {
3494                         DEBUG (4, fprintf (gc_debug_file, "considering thread %p for signal %d (%s)\n", info, signum, signal_desc (signum)));
3495                         if (ARCH_THREAD_EQUALS (info->id, me)) {
3496                                 DEBUG (4, fprintf (gc_debug_file, "Skip (equal): %p, %p\n", (void*)me, (void*)info->id));
3497                                 continue;
3498                         }
3499                         /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
3500                                 continue;*/
3501                         result = pthread_kill (info->id, signum);
3502                         if (result == 0) {
3503                                 DEBUG (4, fprintf (gc_debug_file, "thread %p signal sent\n", info));
3504                                 count++;
3505                         } else {
3506                                 DEBUG (4, fprintf (gc_debug_file, "thread %p signal failed: %d (%s)\n", (void*)info->id, result, strerror (result)));
3507                                 info->skip = 1;
3508                         }
3509                 }
3510         }
3511
3512         for (i = 0; i < count; ++i) {
3513                 while ((result = sem_wait (&suspend_ack_semaphore)) != 0) {
3514                         if (errno != EINTR) {
3515                                 g_error ("sem_wait ()");
3516                         }
3517                 }
3518         }
3519         return count;
3520 }
3521
3522 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
3523 static void
3524 suspend_handler (int sig)
3525 {
3526         SgenThreadInfo *info;
3527         pthread_t id;
3528         int stop_count;
3529         int old_errno = errno;
3530
3531         id = pthread_self ();
3532         info = thread_info_lookup (id);
3533         stop_count = global_stop_count;
3534         /* duplicate signal */
3535         if (0 && info->stop_count == stop_count) {
3536                 errno = old_errno;
3537                 return;
3538         }
3539         /* update the remset info in the thread data structure */
3540         info->remset = remembered_set;
3541         /* 
3542          * this includes the register values that the kernel put on the stack.
3543          * Write arch-specific code to only push integer regs and a more accurate
3544          * stack pointer.
3545          */
3546         info->stack_start = align_pointer (&id);
3547
3548         /* notify the waiting thread */
3549         sem_post (&suspend_ack_semaphore);
3550         info->stop_count = stop_count;
3551
3552         /* wait until we receive the restart signal */
3553         do {
3554                 info->signal = 0;
3555                 sigsuspend (&suspend_signal_mask);
3556         } while (info->signal != restart_signal_num);
3557
3558         /* notify the waiting thread */
3559         sem_post (&suspend_ack_semaphore);
3560         
3561         errno = old_errno;
3562 }
3563
3564 static void
3565 restart_handler (int sig)
3566 {
3567         SgenThreadInfo *info;
3568         int old_errno = errno;
3569
3570         info = thread_info_lookup (pthread_self ());
3571         info->signal = restart_signal_num;
3572
3573         errno = old_errno;
3574 }
3575
3576 static TV_DECLARE (stop_world_time);
3577 static unsigned long max_pause_usec = 0;
3578
3579 /* LOCKING: assumes the GC lock is held */
3580 static int
3581 stop_world (void)
3582 {
3583         int count;
3584
3585         global_stop_count++;
3586         DEBUG (3, fprintf (gc_debug_file, "stopping world n %d from %p %p\n", global_stop_count, thread_info_lookup (ARCH_GET_THREAD ()), (gpointer)ARCH_GET_THREAD ()));
3587         TV_GETTIME (stop_world_time);
3588         count = thread_handshake (suspend_signal_num);
3589         DEBUG (3, fprintf (gc_debug_file, "world stopped %d thread(s)\n", count));
3590         return count;
3591 }
3592
3593 /* LOCKING: assumes the GC lock is held */
3594 static int
3595 restart_world (void)
3596 {
3597         int count;
3598         TV_DECLARE (end_sw);
3599         unsigned long usec;
3600
3601         count = thread_handshake (restart_signal_num);
3602         TV_GETTIME (end_sw);
3603         usec = TV_ELAPSED (stop_world_time, end_sw);
3604         max_pause_usec = MAX (usec, max_pause_usec);
3605         DEBUG (2, fprintf (gc_debug_file, "restarted %d thread(s) (pause time: %d usec, max: %d)\n", count, (int)usec, (int)max_pause_usec));
3606         return count;
3607 }
3608
3609 #endif /* USE_SIGNAL_BASED_START_STOP_WORLD */
3610
3611 /*
3612  * Identify objects pinned in a thread stack and its registers.
3613  */
3614 static void
3615 pin_thread_data (void *start_nursery, void *end_nursery)
3616 {
3617         int i;
3618         SgenThreadInfo *info;
3619
3620         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
3621                 for (info = thread_table [i]; info; info = info->next) {
3622                         if (info->skip) {
3623                                 DEBUG (2, fprintf (gc_debug_file, "Skipping dead thread %p, range: %p-%p, size: %d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
3624                                 continue;
3625                         }
3626                         DEBUG (2, fprintf (gc_debug_file, "Scanning thread %p, range: %p-%p, size: %d\n", info, info->stack_start, info->stack_end, (char*)info->stack_end - (char*)info->stack_start));
3627                         conservatively_pin_objects_from (info->stack_start, info->stack_end, start_nursery, end_nursery);
3628                 }
3629         }
3630         DEBUG (2, fprintf (gc_debug_file, "Scanning current thread registers\n"));
3631         conservatively_pin_objects_from ((void*)cur_thread_regs, (void*)(cur_thread_regs + ARCH_NUM_REGS), start_nursery, end_nursery);
3632 }
3633
3634 static void
3635 find_pinning_ref_from_thread (char *obj, size_t size)
3636 {
3637         int i;
3638         SgenThreadInfo *info;
3639         char *endobj = obj + size;
3640
3641         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
3642                 for (info = thread_table [i]; info; info = info->next) {
3643                         char **start = (char**)info->stack_start;
3644                         if (info->skip)
3645                                 continue;
3646                         while (start < (char**)info->stack_end) {
3647                                 if (*start >= obj && *start < endobj) {
3648                                         DEBUG (0, fprintf (gc_debug_file, "Object %p referenced in thread %p (id %p) at %p, stack: %p-%p\n", obj, info, info->id, start, info->stack_start, info->stack_end));
3649                                 }
3650                                 start++;
3651                         }
3652                 }
3653         }
3654         /* FIXME: check register */
3655 }
3656
3657 /* return TRUE if ptr points inside the managed heap */
3658 static gboolean
3659 ptr_in_heap (void* ptr)
3660 {
3661         mword p = (mword)ptr;
3662         if (p < lowest_heap_address || p >= highest_heap_address)
3663                 return FALSE;
3664         /* FIXME: more checks */
3665         return TRUE;
3666 }
3667
3668 static mword*
3669 handle_remset (mword *p, void *start_nursery, void *end_nursery, gboolean global)
3670 {
3671         void **ptr;
3672         mword count;
3673
3674         /* FIXME: exclude stack locations */
3675         switch ((*p) & REMSET_TYPE_MASK) {
3676         case REMSET_LOCATION:
3677                 ptr = (void**)(*p);
3678                 if ((ptr < start_nursery || ptr >= end_nursery) && ptr_in_heap (ptr)) {
3679                         *ptr = copy_object (*ptr, start_nursery, end_nursery);
3680                         DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p\n", ptr, *ptr));
3681                         if (!global && *ptr >= start_nursery && *ptr < end_nursery)
3682                                 add_to_global_remset (ptr);
3683                 } else {
3684                         DEBUG (9, fprintf (gc_debug_file, "Skipping remset at %p holding %p\n", ptr, *ptr));
3685                 }
3686                 return p + 1;
3687         case REMSET_RANGE:
3688                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
3689                 if ((ptr >= start_nursery && ptr < end_nursery) || !ptr_in_heap (ptr))
3690                         return p + 2;
3691                 count = p [1];
3692                 while (count-- > 0) {
3693                         *ptr = copy_object (*ptr, start_nursery, end_nursery);
3694                         DEBUG (9, fprintf (gc_debug_file, "Overwrote remset at %p with %p (count: %d)\n", ptr, *ptr, (int)count));
3695                         if (!global && *ptr >= start_nursery && *ptr < end_nursery)
3696                                 add_to_global_remset (ptr);
3697                         ++ptr;
3698                 }
3699                 return p + 2;
3700         case REMSET_OBJECT:
3701                 ptr = (void**)(*p & ~REMSET_TYPE_MASK);
3702                 if ((ptr >= start_nursery && ptr < end_nursery) || !ptr_in_heap (ptr))
3703                         return p + 1;
3704                 scan_object (*ptr, start_nursery, end_nursery);
3705                 return p + 1;
3706         default:
3707                 g_assert_not_reached ();
3708         }
3709         return NULL;
3710 }
3711
3712 static void
3713 scan_from_remsets (void *start_nursery, void *end_nursery)
3714 {
3715         int i;
3716         SgenThreadInfo *info;
3717         RememberedSet *remset, *next;
3718         mword *p;
3719
3720         /* the global one */
3721         for (remset = global_remset; remset; remset = remset->next) {
3722                 DEBUG (4, fprintf (gc_debug_file, "Scanning global remset range: %p-%p, size: %d\n", remset->data, remset->store_next, remset->store_next - remset->data));
3723                 for (p = remset->data; p < remset->store_next;) {
3724                         p = handle_remset (p, start_nursery, end_nursery, TRUE);
3725                 }
3726         }
3727         /* the per-thread ones */
3728         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
3729                 for (info = thread_table [i]; info; info = info->next) {
3730                         for (remset = info->remset; remset; remset = next) {
3731                                 DEBUG (4, fprintf (gc_debug_file, "Scanning remset for thread %p, range: %p-%p, size: %d\n", info, remset->data, remset->store_next, remset->store_next - remset->data));
3732                                 for (p = remset->data; p < remset->store_next;) {
3733                                         p = handle_remset (p, start_nursery, end_nursery, FALSE);
3734                                 }
3735                                 remset->store_next = remset->data;
3736                                 next = remset->next;
3737                                 remset->next = NULL;
3738                                 if (remset != info->remset) {
3739                                         DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
3740                                         free_internal_mem (remset);
3741                                 }
3742                         }
3743                 }
3744         }
3745 }
3746
3747 /*
3748  * Clear the info in the remembered sets: we're doing a major collection, so
3749  * the per-thread ones are not needed and the global ones will be reconstructed
3750  * during the copy.
3751  */
3752 static void
3753 clear_remsets (void)
3754 {
3755         int i;
3756         SgenThreadInfo *info;
3757         RememberedSet *remset, *next;
3758
3759         /* the global list */
3760         for (remset = global_remset; remset; remset = next) {
3761                 remset->store_next = remset->data;
3762                 next = remset->next;
3763                 remset->next = NULL;
3764                 if (remset != global_remset) {
3765                         DEBUG (4, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
3766                         free_internal_mem (remset);
3767                 }
3768         }
3769         /* the per-thread ones */
3770         for (i = 0; i < THREAD_HASH_SIZE; ++i) {
3771                 for (info = thread_table [i]; info; info = info->next) {
3772                         for (remset = info->remset; remset; remset = next) {
3773                                 remset->store_next = remset->data;
3774                                 next = remset->next;
3775                                 remset->next = NULL;
3776                                 if (remset != info->remset) {
3777                                         DEBUG (1, fprintf (gc_debug_file, "Freed remset at %p\n", remset->data));
3778                                         free_internal_mem (remset);
3779                                 }
3780                         }
3781                 }
3782         }
3783 }
3784
3785 /* LOCKING: assumes the GC lock is held */
3786 static SgenThreadInfo*
3787 gc_register_current_thread (void *addr)
3788 {
3789         int hash;
3790         SgenThreadInfo* info = malloc (sizeof (SgenThreadInfo));
3791         if (!info)
3792                 return NULL;
3793         info->id = ARCH_GET_THREAD ();
3794         info->stop_count = -1;
3795         info->skip = 0;
3796         info->signal = 0;
3797         info->stack_start = NULL;
3798
3799         /* try to get it with attributes first */
3800 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
3801         {
3802                 size_t size;
3803                 void *sstart;
3804                 pthread_attr_t attr;
3805                 pthread_getattr_np (pthread_self (), &attr);
3806                 pthread_attr_getstack (&attr, &sstart, &size);
3807                 info->stack_end = (char*)sstart + size;
3808                 pthread_attr_destroy (&attr);
3809         }
3810 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
3811                  info->stack_end = (char*)pthread_get_stackaddr_np (pthread_self ());
3812 #else
3813         {
3814                 /* FIXME: we assume the stack grows down */
3815                 gsize stack_bottom = (gsize)addr;
3816                 stack_bottom += 4095;
3817                 stack_bottom &= ~4095;
3818                 info->stack_end = (char*)stack_bottom;
3819         }
3820 #endif
3821
3822         /* hash into the table */
3823         hash = HASH_PTHREAD_T (info->id) % THREAD_HASH_SIZE;
3824         info->next = thread_table [hash];
3825         thread_table [hash] = info;
3826
3827         remembered_set = info->remset = alloc_remset (DEFAULT_REMSET_SIZE, info);
3828         pthread_setspecific (remembered_set_key, remembered_set);
3829         DEBUG (3, fprintf (gc_debug_file, "registered thread %p (%p) (hash: %d)\n", info, (gpointer)info->id, hash));
3830         return info;
3831 }
3832
3833 static void
3834 unregister_current_thread (void)
3835 {
3836         int hash;
3837         SgenThreadInfo *prev = NULL;
3838         SgenThreadInfo *p;
3839         RememberedSet *rset;
3840         ARCH_THREAD_TYPE id = ARCH_GET_THREAD ();
3841
3842         hash = HASH_PTHREAD_T (id) % THREAD_HASH_SIZE;
3843         p = thread_table [hash];
3844         assert (p);
3845         DEBUG (3, fprintf (gc_debug_file, "unregister thread %p (%p)\n", p, (gpointer)p->id));
3846         while (!ARCH_THREAD_EQUALS (p->id, id)) {
3847                 prev = p;
3848                 p = p->next;
3849         }
3850         if (prev == NULL) {
3851                 thread_table [hash] = p->next;
3852         } else {
3853                 prev->next = p->next;
3854         }
3855         rset = p->remset;
3856         /* FIXME: transfer remsets if any */
3857         while (rset) {
3858                 RememberedSet *next = rset->next;
3859                 free_internal_mem (rset);
3860                 rset = next;
3861         }
3862         free (p);
3863 }
3864
3865 static void
3866 unregister_thread (void *k)
3867 {
3868         LOCK_GC;
3869         unregister_current_thread ();
3870         UNLOCK_GC;
3871 }
3872
3873 gboolean
3874 mono_gc_register_thread (void *baseptr)
3875 {
3876         SgenThreadInfo *info;
3877         LOCK_GC;
3878         info = thread_info_lookup (ARCH_GET_THREAD ());
3879         if (info == NULL)
3880                 info = gc_register_current_thread (baseptr);
3881         UNLOCK_GC;
3882         return info != NULL;
3883 }
3884
3885 #if USE_PTHREAD_INTERCEPT
3886
3887 #undef pthread_create
3888 #undef pthread_join
3889 #undef pthread_detach
3890
3891 typedef struct {
3892         void *(*start_routine) (void *);
3893         void *arg;
3894         int flags;
3895         sem_t registered;
3896 } SgenThreadStartInfo;
3897
3898 static void*
3899 gc_start_thread (void *arg)
3900 {
3901         SgenThreadStartInfo *start_info = arg;
3902         SgenThreadInfo* info;
3903         void *t_arg = start_info->arg;
3904         void *(*start_func) (void*) = start_info->start_routine;
3905         void *result;
3906
3907         LOCK_GC;
3908         info = gc_register_current_thread (&result);
3909         UNLOCK_GC;
3910         sem_post (&(start_info->registered));
3911         result = start_func (t_arg);
3912         /*
3913          * this is done by the pthread key dtor
3914         LOCK_GC;
3915         unregister_current_thread ();
3916         UNLOCK_GC;
3917         */
3918
3919         return result;
3920 }
3921
3922 int
3923 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
3924 {
3925         SgenThreadStartInfo *start_info;
3926         int result;
3927
3928         start_info = malloc (sizeof (SgenThreadStartInfo));
3929         if (!start_info)
3930                 return ENOMEM;
3931         sem_init (&(start_info->registered), 0, 0);
3932         start_info->arg = arg;
3933         start_info->start_routine = start_routine;
3934
3935         result = pthread_create (new_thread, attr, gc_start_thread, start_info);
3936         if (result == 0) {
3937                 while (sem_wait (&(start_info->registered)) != 0) {
3938                         /*if (EINTR != errno) ABORT("sem_wait failed"); */
3939                 }
3940         }
3941         sem_destroy (&(start_info->registered));
3942         free (start_info);
3943         return result;
3944 }
3945
3946 int
3947 mono_gc_pthread_join (pthread_t thread, void **retval)
3948 {
3949         return pthread_join (thread, retval);
3950 }
3951
3952 int
3953 mono_gc_pthread_detach (pthread_t thread)
3954 {
3955         return pthread_detach (thread);
3956 }
3957
3958 #endif /* USE_PTHREAD_INTERCEPT */
3959
3960 /*
3961  * ######################################################################
3962  * ########  Write barriers
3963  * ######################################################################
3964  */
3965
3966 static RememberedSet*
3967 alloc_remset (int size, gpointer id) {
3968         RememberedSet* res = get_internal_mem (sizeof (RememberedSet) + (size * sizeof (gpointer)));
3969         res->store_next = res->data;
3970         res->end_set = res->data + size;
3971         res->next = NULL;
3972         DEBUG (4, fprintf (gc_debug_file, "Allocated remset size %d at %p for %p\n", size, res->data, id));
3973         return res;
3974 }
3975
3976 /*
3977  * Note: the write barriers first do the needed GC work and then do the actual store:
3978  * this way the value is visible to the conservative GC scan after the write barrier
3979  * itself. If a GC interrupts the barrier in the middle, value will be kept alive by
3980  * the conservative scan, otherwise by the remembered set scan. FIXME: figure out what
3981  * happens when we need to record which pointers contain references to the new generation.
3982  * The write barrier will be executed, but the pointer is still not stored.
3983  */
3984 void
3985 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
3986 {
3987         RememberedSet *rs;
3988         if (field_ptr >= nursery_start && field_ptr < nursery_real_end) {
3989                 *(void**)field_ptr = value;
3990                 return;
3991         }
3992         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", field_ptr));
3993         rs = remembered_set;
3994         if (rs->store_next < rs->end_set) {
3995                 *(rs->store_next++) = (mword)field_ptr;
3996                 *(void**)field_ptr = value;
3997                 return;
3998         }
3999         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4000         rs->next = remembered_set;
4001         remembered_set = rs;
4002         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4003         *(rs->store_next++) = (mword)field_ptr;
4004         *(void**)field_ptr = value;
4005 }
4006
4007 void
4008 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
4009 {
4010         RememberedSet *rs = remembered_set;
4011         if (slot_ptr >= nursery_start && slot_ptr < nursery_real_end) {
4012                 *(void**)slot_ptr = value;
4013                 return;
4014         }
4015         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", slot_ptr));
4016         if (rs->store_next < rs->end_set) {
4017                 *(rs->store_next++) = (mword)slot_ptr;
4018                 *(void**)slot_ptr = value;
4019                 return;
4020         }
4021         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4022         rs->next = remembered_set;
4023         remembered_set = rs;
4024         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4025         *(rs->store_next++) = (mword)slot_ptr;
4026         *(void**)slot_ptr = value;
4027 }
4028
4029 void
4030 mono_gc_wbarrier_arrayref_copy (MonoArray *arr, gpointer slot_ptr, int count)
4031 {
4032         RememberedSet *rs = remembered_set;
4033         if (slot_ptr >= nursery_start && slot_ptr < nursery_real_end)
4034                 return;
4035         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p, %d\n", slot_ptr, count));
4036         if (rs->store_next + 1 < rs->end_set) {
4037                 *(rs->store_next++) = (mword)slot_ptr | REMSET_RANGE;
4038                 *(rs->store_next++) = count;
4039                 return;
4040         }
4041         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4042         rs->next = remembered_set;
4043         remembered_set = rs;
4044         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4045         *(rs->store_next++) = (mword)slot_ptr | REMSET_RANGE;
4046         *(rs->store_next++) = count;
4047 }
4048
4049 void
4050 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
4051 {
4052         RememberedSet *rs = remembered_set;
4053         if (ptr >= nursery_start && ptr < nursery_real_end) {
4054                 DEBUG (8, fprintf (gc_debug_file, "Skipping remset at %p\n", ptr));
4055                 *(void**)ptr = value;
4056                 return;
4057         }
4058         DEBUG (8, fprintf (gc_debug_file, "Adding remset at %p\n", ptr));
4059         /* FIXME: ensure it is on the heap */
4060         if (rs->store_next < rs->end_set) {
4061                 *(rs->store_next++) = (mword)ptr;
4062                 *(void**)ptr = value;
4063                 return;
4064         }
4065         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4066         rs->next = remembered_set;
4067         remembered_set = rs;
4068         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4069         *(rs->store_next++) = (mword)ptr;
4070         *(void**)ptr = value;
4071 }
4072
4073 void
4074 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
4075 {
4076         if (dest >= nursery_start && dest < nursery_real_end) {
4077                 return;
4078         }
4079         DEBUG (1, fprintf (gc_debug_file, "Adding value remset at %p, count %d for class %s\n", dest, count, klass->name));
4080 }
4081
4082 /**
4083  * mono_gc_wbarrier_object:
4084  *
4085  * Write barrier to call when obj is the result of a clone or copy of an object.
4086  */
4087 void
4088 mono_gc_wbarrier_object (MonoObject* obj)
4089 {
4090         RememberedSet *rs = remembered_set;
4091         DEBUG (1, fprintf (gc_debug_file, "Adding object remset for %p\n", obj));
4092         if (rs->store_next < rs->end_set) {
4093                 *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
4094                 return;
4095         }
4096         rs = alloc_remset (rs->end_set - rs->data, (void*)1);
4097         rs->next = remembered_set;
4098         remembered_set = rs;
4099         thread_info_lookup (ARCH_GET_THREAD ())->remset = rs;
4100         *(rs->store_next++) = (mword)obj | REMSET_OBJECT;
4101 }
4102
4103 /*
4104  * ######################################################################
4105  * ########  Other mono public interface functions.
4106  * ######################################################################
4107  */
4108
4109 void
4110 mono_gc_collect (int generation)
4111 {
4112         LOCK_GC;
4113         update_current_thread_stack (&generation);
4114         stop_world ();
4115         if (generation == 0) {
4116                 collect_nursery (0);
4117         } else {
4118                 major_collection ();
4119         }
4120         restart_world ();
4121         UNLOCK_GC;
4122 }
4123
4124 int
4125 mono_gc_max_generation (void)
4126 {
4127         return 1;
4128 }
4129
4130 int
4131 mono_gc_collection_count (int generation)
4132 {
4133         if (generation == 0)
4134                 return num_minor_gcs;
4135         return num_major_gcs;
4136 }
4137
4138 gint64
4139 mono_gc_get_used_size (void)
4140 {
4141         gint64 tot = 0;
4142         GCMemSection *section;
4143         LOCK_GC;
4144         tot = los_memory_usage;
4145         for (section = section_list; section; section = section->next) {
4146                 /* this is approximate... */
4147                 tot += section->next_data - section->data;
4148         }
4149         /* FIXME: account for pinned objects */
4150         UNLOCK_GC;
4151         return tot;
4152 }
4153
4154 gint64
4155 mono_gc_get_heap_size (void)
4156 {
4157         return total_alloc;
4158 }
4159
4160 void
4161 mono_gc_disable (void)
4162 {
4163         LOCK_GC;
4164         gc_disabled++;
4165         UNLOCK_GC;
4166 }
4167
4168 void
4169 mono_gc_enable (void)
4170 {
4171         LOCK_GC;
4172         gc_disabled--;
4173         UNLOCK_GC;
4174 }
4175
4176 gboolean
4177 mono_object_is_alive (MonoObject* o)
4178 {
4179         return TRUE;
4180 }
4181
4182 int
4183 mono_gc_get_generation (MonoObject *obj)
4184 {
4185         if ((char*)obj >= nursery_start && (char*)obj < nursery_real_end)
4186                 return 0;
4187         return 1;
4188 }
4189
4190 void
4191 mono_gc_enable_events (void)
4192 {
4193 }
4194
4195 void
4196 mono_gc_weak_link_add (void **link_addr, MonoObject *obj)
4197 {
4198         mono_gc_register_disappearing_link (obj, link_addr);
4199         *link_addr = obj;
4200 }
4201
4202 void
4203 mono_gc_weak_link_remove (void **link_addr)
4204 {
4205         mono_gc_register_disappearing_link (NULL, link_addr);
4206         *link_addr = NULL;
4207 }
4208
4209 MonoObject*
4210 mono_gc_weak_link_get (void **link_addr)
4211 {
4212         return *link_addr;
4213 }
4214
4215 void*
4216 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
4217 {
4218         if (numbits < ((sizeof (*bitmap) * 8) - ROOT_DESC_TYPE_SHIFT)) {
4219                 mword desc = ROOT_DESC_BITMAP | (bitmap [0] << ROOT_DESC_TYPE_SHIFT);
4220                 return (void*)desc;
4221         }
4222         /* conservative scanning */
4223         DEBUG (3, fprintf (gc_debug_file, "Conservative root descr for size: %d\n", numbits));
4224         return NULL;
4225 }
4226
4227 void*
4228 mono_gc_alloc_fixed (size_t size, void *descr)
4229 {
4230         /* FIXME: do a single allocation */
4231         void *res = calloc (1, size);
4232         if (!res)
4233                 return NULL;
4234         if (!mono_gc_register_root (res, size, descr)) {
4235                 free (res);
4236                 res = NULL;
4237         }
4238         return res;
4239 }
4240
4241 void
4242 mono_gc_free_fixed (void* addr)
4243 {
4244         mono_gc_deregister_root (addr);
4245         free (addr);
4246 }
4247
4248 gboolean
4249 mono_gc_is_gc_thread (void)
4250 {
4251         gboolean result;
4252         LOCK_GC;
4253         result = thread_info_lookup (ARCH_GET_THREAD ()) != NULL;
4254         UNLOCK_GC;
4255         return result;
4256 }
4257
4258 void
4259 mono_gc_base_init (void)
4260 {
4261         char *env;
4262         struct sigaction sinfo;
4263
4264         LOCK_INIT (gc_mutex);
4265         LOCK_GC;
4266         if (gc_initialized) {
4267                 UNLOCK_GC;
4268                 return;
4269         }
4270         pagesize = mono_pagesize ();
4271         gc_debug_file = stderr;
4272         /* format: MONO_GC_DEBUG=l[,filename] where l is a debug level 0-9 */
4273         if ((env = getenv ("MONO_GC_DEBUG"))) {
4274                 if (env [0] >= '0' && env [0] <= '9') {
4275                         gc_debug_level = atoi (env);
4276                         env++;
4277                 }
4278                 if (env [0] == ',')
4279                         env++;
4280                 if (env [0]) {
4281                         char *rf = g_strdup_printf ("%s.%d", env, getpid ());
4282                         gc_debug_file = fopen (rf, "wb");
4283                         if (!gc_debug_file)
4284                                 gc_debug_file = stderr;
4285                         g_free (rf);
4286                 }
4287         }
4288
4289         sem_init (&suspend_ack_semaphore, 0, 0);
4290
4291         sigfillset (&sinfo.sa_mask);
4292         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
4293         sinfo.sa_handler = suspend_handler;
4294         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
4295                 g_error ("failed sigaction");
4296         }
4297
4298         sinfo.sa_handler = restart_handler;
4299         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
4300                 g_error ("failed sigaction");
4301         }
4302
4303         sigfillset (&suspend_signal_mask);
4304         sigdelset (&suspend_signal_mask, restart_signal_num);
4305
4306         global_remset = alloc_remset (1024, NULL);
4307         global_remset->next = NULL;
4308
4309         pthread_key_create (&remembered_set_key, unregister_thread);
4310         gc_initialized = TRUE;
4311         UNLOCK_GC;
4312         mono_gc_register_thread (&sinfo);
4313 }
4314
4315 MonoMethod*
4316 mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
4317 {
4318         return NULL;
4319 }
4320
4321 int
4322 mono_gc_get_managed_allocator_type (MonoMethod *managed_alloc)
4323 {
4324         return -1;
4325 }
4326
4327 MonoMethod*
4328 mono_gc_get_managed_allocator_by_type (int atype)
4329 {
4330         return NULL;
4331 }
4332
4333 #endif /* HAVE_SGEN_GC */
4334