Merge pull request #22708 from BrianBohe/updating_comments
[platform/upstream/coreclr.git] / src / gc / gc.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5
6 //
7 // #Overview
8 //
9 // GC automatically manages memory allocated by managed code.
10 // The design doc for GC can be found at Documentation/botr/garbage-collection.md
11 //
12 // This file includes both the code for GC and the allocator. The most common
13 // case for a GC to be triggered is from the allocator code. See
14 // code:#try_allocate_more_space where it calls GarbageCollectGeneration.
15 //
16 // Entry points for the allocator are GCHeap::Alloc* which are called by the
17 // allocation helpers in gcscan.cpp
18 //
19
20 #include "gcpriv.h"
21
22 #define USE_INTROSORT
23
24 // We just needed a simple random number generator for testing.
25 class gc_rand
26 {
27 public:
28     static uint64_t x;
29
30     static uint64_t get_rand() 
31     {
32             x = (314159269*x+278281) & 0x7FFFFFFF;
33             return x;
34     }
35
36     // obtain random number in the range 0 .. r-1
37     static uint64_t get_rand(uint64_t r) {
38             // require r >= 0
39             uint64_t x = (uint64_t)((get_rand() * r) >> 31);
40             return x;
41     }
42 };
43
44 uint64_t gc_rand::x = 0;
45
46 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
47 BOOL bgc_heap_walk_for_etw_p = FALSE;
48 #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
49
50 #if defined(FEATURE_REDHAWK)
51 #define MAYBE_UNUSED_VAR(v) v = v
52 #else
53 #define MAYBE_UNUSED_VAR(v)
54 #endif // FEATURE_REDHAWK
55
56 #define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0))
57 #define commit_min_th (16*OS_PAGE_SIZE)
58
59 #ifdef SERVER_GC
60 #define partial_size_th 100
61 #define num_partial_refs 64
62 #else //SERVER_GC
63 #define partial_size_th 100
64 #define num_partial_refs 32
65 #endif //SERVER_GC
66
67 #define demotion_plug_len_th (6*1024*1024)
68
69 #ifdef BIT64
70 #define MARK_STACK_INITIAL_LENGTH 1024
71 #else
72 #define MARK_STACK_INITIAL_LENGTH 128
73 #endif // BIT64
74
75 #define LOH_PIN_QUEUE_LENGTH 100
76 #define LOH_PIN_DECAY 10
77
78 #ifdef BIT64
79 // Right now we support maximum 1024 procs - meaning that we will create at most
80 // that many GC threads and GC heaps. 
81 #define MAX_SUPPORTED_CPUS 1024
82 #else
83 #define MAX_SUPPORTED_CPUS 64
84 #endif // BIT64
85
86 uint32_t yp_spin_count_unit = 0;
87 size_t loh_size_threshold = LARGE_OBJECT_SIZE;
88
89 #ifdef GC_CONFIG_DRIVEN
90 int compact_ratio = 0;
91 #endif //GC_CONFIG_DRIVEN
92
93 // See comments in reset_memory.
94 BOOL reset_mm_p = TRUE;
95
96 bool g_fFinalizerRunOnShutDown = false;
97
98 #ifdef FEATURE_SVR_GC
99 bool g_built_with_svr_gc = true;
100 #else
101 bool g_built_with_svr_gc = false;
102 #endif // FEATURE_SVR_GC
103
104 #if defined(BUILDENV_DEBUG)
105 uint8_t g_build_variant = 0;
106 #elif defined(BUILDENV_CHECKED)
107 uint8_t g_build_variant = 1;
108 #else
109 uint8_t g_build_variant = 2;
110 #endif // defined(BUILDENV_DEBUG)
111
112 VOLATILE(int32_t) g_no_gc_lock = -1;
113
114 #if defined (TRACE_GC) && !defined (DACCESS_COMPILE)
115 const char * const allocation_state_str[] = {
116     "start",
117     "can_allocate",
118     "cant_allocate",
119     "retry_allocate",
120     "try_fit",
121     "try_fit_new_seg",
122     "try_fit_after_cg",
123     "try_fit_after_bgc",
124     "try_free_full_seg_in_bgc", 
125     "try_free_after_bgc",
126     "try_seg_end",
127     "acquire_seg",
128     "acquire_seg_after_cg",
129     "acquire_seg_after_bgc",
130     "check_and_wait_for_bgc",
131     "trigger_full_compact_gc",
132     "trigger_ephemeral_gc",
133     "trigger_2nd_ephemeral_gc",
134     "check_retry_seg"
135 };
136
137 const char * const msl_take_state_str[] = {
138     "get_large_seg",
139     "bgc_loh_sweep",
140     "wait_bgc",
141     "block_gc",
142     "clr_mem",
143     "clr_large_mem",
144     "t_eph_gc",
145     "t_full_gc",
146     "alloc_small",
147     "alloc_large",
148     "alloc_small_cant",
149     "alloc_large_cant",
150     "try_alloc",
151     "try_budget"
152 };
153 #endif //TRACE_GC && !DACCESS_COMPILE
154
155
156 // Keep this in sync with the definition of gc_reason
157 #if (defined(DT_LOG) || defined(TRACE_GC)) && !defined (DACCESS_COMPILE)
158 static const char* const str_gc_reasons[] = 
159 {
160     "alloc_soh",
161     "induced",
162     "lowmem",
163     "empty",
164     "alloc_loh",
165     "oos_soh",
166     "oos_loh",
167     "induced_noforce",
168     "gcstress",
169     "induced_lowmem",
170     "induced_compacting",
171     "lowmemory_host",
172     "pm_full_gc",
173     "lowmemory_host_blocking"
174 };
175
176 static const char* const str_gc_pause_modes[] = 
177 {
178     "batch",
179     "interactive",
180     "low_latency",
181     "sustained_low_latency",
182     "no_gc"
183 };
184 #endif // defined(DT_LOG) || defined(TRACE_GC)
185
186 inline
187 BOOL is_induced (gc_reason reason)
188 {
189     return ((reason == reason_induced) ||
190             (reason == reason_induced_noforce) ||
191             (reason == reason_lowmemory) ||
192             (reason == reason_lowmemory_blocking) || 
193             (reason == reason_induced_compacting) ||
194             (reason == reason_lowmemory_host) || 
195             (reason == reason_lowmemory_host_blocking));
196 }
197
198 inline
199 BOOL is_induced_blocking (gc_reason reason)
200 {
201     return ((reason == reason_induced) ||
202             (reason == reason_lowmemory_blocking) || 
203             (reason == reason_induced_compacting) ||
204             (reason == reason_lowmemory_host_blocking));
205 }
206
207 #ifndef DACCESS_COMPILE
208 int64_t qpf;
209
210 size_t GetHighPrecisionTimeStamp()
211 {
212     int64_t ts = GCToOSInterface::QueryPerformanceCounter();
213     
214     return (size_t)(ts / (qpf / 1000));    
215 }
216 #endif
217
218 #ifdef GC_STATS
219 // There is a current and a prior copy of the statistics.  This allows us to display deltas per reporting
220 // interval, as well as running totals.  The 'min' and 'max' values require special treatment.  They are
221 // Reset (zeroed) in the current statistics when we begin a new interval and they are updated via a
222 // comparison with the global min/max.
223 GCStatistics g_GCStatistics;
224 GCStatistics g_LastGCStatistics;
225
226 char* GCStatistics::logFileName = NULL;
227 FILE*  GCStatistics::logFile = NULL;
228
229 void GCStatistics::AddGCStats(const gc_mechanisms& settings, size_t timeInMSec)
230 {
231 #ifdef BACKGROUND_GC
232     if (settings.concurrent)
233     {
234         bgc.Accumulate((uint32_t)timeInMSec*1000);
235         cntBGC++;
236     }
237     else if (settings.background_p)
238     {
239         fgc.Accumulate((uint32_t)timeInMSec*1000);
240         cntFGC++;
241         if (settings.compaction)
242             cntCompactFGC++;
243         assert(settings.condemned_generation < max_generation);
244         cntFGCGen[settings.condemned_generation]++;
245     }
246     else
247 #endif // BACKGROUND_GC
248     {
249         ngc.Accumulate((uint32_t)timeInMSec*1000);
250         cntNGC++;
251         if (settings.compaction)
252             cntCompactNGC++;
253         cntNGCGen[settings.condemned_generation]++;
254     }
255
256     if (is_induced (settings.reason))
257         cntReasons[(int)reason_induced]++;
258     else if (settings.stress_induced)
259         cntReasons[(int)reason_gcstress]++;
260     else
261         cntReasons[(int)settings.reason]++;
262
263 #ifdef BACKGROUND_GC
264     if (settings.concurrent || !settings.background_p)
265     {
266 #endif // BACKGROUND_GC
267         RollOverIfNeeded();
268 #ifdef BACKGROUND_GC
269     }
270 #endif // BACKGROUND_GC
271 }
272
273 void GCStatistics::Initialize()
274 {
275     LIMITED_METHOD_CONTRACT;
276     // for efficiency sake we're taking a dependency on the layout of a C++ object
277     // with a vtable. protect against violations of our premise:
278     static_assert(offsetof(GCStatistics, cntDisplay) == sizeof(void*),
279             "The first field of GCStatistics follows the pointer sized vtable");
280
281     int podOffs = offsetof(GCStatistics, cntDisplay);       // offset of the first POD field
282     memset((uint8_t*)(&g_GCStatistics)+podOffs, 0, sizeof(g_GCStatistics)-podOffs);
283     memset((uint8_t*)(&g_LastGCStatistics)+podOffs, 0, sizeof(g_LastGCStatistics)-podOffs);
284 }
285
286 void GCStatistics::DisplayAndUpdate()
287 {
288     LIMITED_METHOD_CONTRACT;
289
290     if (logFileName == NULL || logFile == NULL)
291         return;
292
293     {
294         if (cntDisplay == 0)
295             fprintf(logFile, "\nGCMix **** Initialize *****\n\n");
296             
297         fprintf(logFile, "GCMix **** Summary ***** %d\n", cntDisplay);
298
299         // NGC summary (total, timing info)
300         ngc.DisplayAndUpdate(logFile, "NGC ", &g_LastGCStatistics.ngc, cntNGC, g_LastGCStatistics.cntNGC, msec);
301
302         // FGC summary (total, timing info)
303         fgc.DisplayAndUpdate(logFile, "FGC ", &g_LastGCStatistics.fgc, cntFGC, g_LastGCStatistics.cntFGC, msec);
304
305         // BGC summary
306         bgc.DisplayAndUpdate(logFile, "BGC ", &g_LastGCStatistics.bgc, cntBGC, g_LastGCStatistics.cntBGC, msec);
307
308         // NGC/FGC break out by generation & compacting vs. sweeping
309         fprintf(logFile, "NGC   ");
310         for (int i = max_generation; i >= 0; --i)
311             fprintf(logFile, "gen%d %d (%d). ", i, cntNGCGen[i]-g_LastGCStatistics.cntNGCGen[i], cntNGCGen[i]);
312         fprintf(logFile, "\n");
313
314         fprintf(logFile, "FGC   ");
315         for (int i = max_generation-1; i >= 0; --i)
316             fprintf(logFile, "gen%d %d (%d). ", i, cntFGCGen[i]-g_LastGCStatistics.cntFGCGen[i], cntFGCGen[i]);
317         fprintf(logFile, "\n");
318
319         // Compacting vs. Sweeping break out
320         int _cntSweep = cntNGC-cntCompactNGC;
321         int _cntLastSweep = g_LastGCStatistics.cntNGC-g_LastGCStatistics.cntCompactNGC;
322         fprintf(logFile, "NGC   Sweeping %d (%d) Compacting %d (%d)\n",
323                _cntSweep - _cntLastSweep, _cntSweep,
324                cntCompactNGC - g_LastGCStatistics.cntCompactNGC, cntCompactNGC);
325
326         _cntSweep = cntFGC-cntCompactFGC;
327         _cntLastSweep = g_LastGCStatistics.cntFGC-g_LastGCStatistics.cntCompactFGC;
328         fprintf(logFile, "FGC   Sweeping %d (%d) Compacting %d (%d)\n",
329                _cntSweep - _cntLastSweep, _cntSweep,
330                cntCompactFGC - g_LastGCStatistics.cntCompactFGC, cntCompactFGC);
331
332 #ifdef TRACE_GC
333         // GC reasons...
334         for (int reason=(int)reason_alloc_soh; reason <= (int)reason_gcstress; ++reason)
335         {
336             if (cntReasons[reason] != 0)
337                 fprintf(logFile, "%s %d (%d). ", str_gc_reasons[reason], 
338                     cntReasons[reason]-g_LastGCStatistics.cntReasons[reason], cntReasons[reason]);
339         }
340 #endif // TRACE_GC
341         fprintf(logFile, "\n\n");
342
343         // flush the log file...
344         fflush(logFile);
345     }
346
347     g_LastGCStatistics = *this;
348
349     ngc.Reset();
350     fgc.Reset();
351     bgc.Reset();
352 }
353
354 #endif // GC_STATS
355
356 inline
357 size_t round_up_power2 (size_t size)
358 {
359     // Get the 0-based index of the most-significant bit in size-1.
360     // If the call failed (because size-1 is zero), size must be 1,
361     // so return 1 (because 1 rounds up to itself).
362     DWORD highest_set_bit_index;
363     if (0 ==
364 #ifdef BIT64
365         BitScanReverse64(
366 #else
367         BitScanReverse(
368 #endif
369             &highest_set_bit_index, size - 1)) { return 1; }
370
371     // The size == 0 case (which would have overflowed to SIZE_MAX when decremented)
372     // is handled below by relying on the fact that highest_set_bit_index is the maximum value
373     // (31 or 63, depending on sizeof(size_t)) and left-shifting a value >= 2 by that
374     // number of bits shifts in zeros from the right, resulting in an output of zero.
375     return static_cast<size_t>(2) << highest_set_bit_index;
376 }
377
378 inline
379 size_t round_down_power2 (size_t size)
380 {
381     // Get the 0-based index of the most-significant bit in size.
382     // If the call failed, size must be zero so return zero.
383     DWORD highest_set_bit_index;
384     if (0 ==
385 #ifdef BIT64
386         BitScanReverse64(
387 #else
388         BitScanReverse(
389 #endif
390             &highest_set_bit_index, size)) { return 0; }
391
392     // Left-shift 1 by highest_set_bit_index to get back a value containing only
393     // the most-significant set bit of size, i.e. size rounded down
394     // to the next power-of-two value.
395     return static_cast<size_t>(1) << highest_set_bit_index;
396 }
397
398 // Get the 0-based index of the most-significant bit in the value.
399 // Returns -1 if the input value is zero (i.e. has no set bits).
400 inline
401 int index_of_highest_set_bit (size_t value)
402 {
403     // Get the 0-based index of the most-significant bit in the value.
404     // If the call failed (because value is zero), return -1.
405     DWORD highest_set_bit_index;
406     return (0 ==
407 #ifdef BIT64
408         BitScanReverse64(
409 #else
410         BitScanReverse(
411 #endif
412             &highest_set_bit_index, value)) ? -1 : static_cast<int>(highest_set_bit_index);
413 }
414
415 inline
416 int relative_index_power2_plug (size_t power2)
417 {
418     int index = index_of_highest_set_bit (power2);
419     assert (index <= MAX_INDEX_POWER2);
420
421     return ((index < MIN_INDEX_POWER2) ? 0 : (index - MIN_INDEX_POWER2));
422 }
423
424 inline
425 int relative_index_power2_free_space (size_t power2)
426 {
427     int index = index_of_highest_set_bit (power2);
428     assert (index <= MAX_INDEX_POWER2);
429
430     return ((index < MIN_INDEX_POWER2) ? -1 : (index - MIN_INDEX_POWER2));
431 }
432
433 #ifdef BACKGROUND_GC
434 uint32_t bgc_alloc_spin_count = 140;
435 uint32_t bgc_alloc_spin_count_loh = 16;
436 uint32_t bgc_alloc_spin = 2;
437
438
439 inline
440 void c_write (uint32_t& place, uint32_t value)
441 {
442     Interlocked::Exchange (&place, value);
443     //place = value;
444 }
445
446 #ifndef DACCESS_COMPILE
447 // If every heap's gen2 or gen3 size is less than this threshold we will do a blocking GC.
448 const size_t bgc_min_per_heap = 4*1024*1024;
449
450 int gc_heap::gchist_index = 0;
451 gc_mechanisms_store gc_heap::gchist[max_history_count];
452
453 #ifndef MULTIPLE_HEAPS
454 size_t gc_heap::total_promoted_bytes = 0;
455 VOLATILE(bgc_state) gc_heap::current_bgc_state = bgc_not_in_process;
456 int gc_heap::gchist_index_per_heap = 0;
457 gc_heap::gc_history gc_heap::gchist_per_heap[max_history_count];
458 #endif //MULTIPLE_HEAPS
459
460 void gc_heap::add_to_history_per_heap()
461 {
462 #ifdef GC_HISTORY
463     gc_history* current_hist = &gchist_per_heap[gchist_index_per_heap];
464     current_hist->gc_index = settings.gc_index;
465     current_hist->current_bgc_state = current_bgc_state;
466     size_t elapsed = dd_gc_elapsed_time (dynamic_data_of (0));
467     current_hist->gc_time_ms = (uint32_t)elapsed;
468     current_hist->gc_efficiency = (elapsed ? (total_promoted_bytes / elapsed) : total_promoted_bytes);
469     current_hist->eph_low = generation_allocation_start (generation_of (max_generation-1));
470     current_hist->gen0_start = generation_allocation_start (generation_of (0));
471     current_hist->eph_high = heap_segment_allocated (ephemeral_heap_segment);
472 #ifdef BACKGROUND_GC
473     current_hist->bgc_lowest = background_saved_lowest_address;
474     current_hist->bgc_highest = background_saved_highest_address;
475 #endif //BACKGROUND_GC
476     current_hist->fgc_lowest = lowest_address;
477     current_hist->fgc_highest = highest_address;
478     current_hist->g_lowest = g_gc_lowest_address;
479     current_hist->g_highest = g_gc_highest_address;
480
481     gchist_index_per_heap++;
482     if (gchist_index_per_heap == max_history_count)
483     {
484         gchist_index_per_heap = 0;
485     }
486 #endif //GC_HISTORY
487 }
488
489 void gc_heap::add_to_history()
490 {
491 #ifdef GC_HISTORY
492     gc_mechanisms_store* current_settings = &gchist[gchist_index];
493     current_settings->store (&settings);
494
495     gchist_index++;
496     if (gchist_index == max_history_count)
497     {
498         gchist_index = 0;
499     }
500 #endif //GC_HISTORY
501 }
502
503 #endif //DACCESS_COMPILE
504 #endif //BACKGROUND_GC
505
506 #if defined(TRACE_GC) && !defined(DACCESS_COMPILE)
507 BOOL   gc_log_on = TRUE;
508 FILE* gc_log = NULL;
509 size_t gc_log_file_size = 0;
510
511 size_t gc_buffer_index = 0;
512 size_t max_gc_buffers = 0;
513
514 static CLRCriticalSection gc_log_lock;
515
516 // we keep this much in a buffer and only flush when the buffer is full
517 #define gc_log_buffer_size (1024*1024)
518 uint8_t* gc_log_buffer = 0;
519 size_t gc_log_buffer_offset = 0;
520
521 void log_va_msg(const char *fmt, va_list args)
522 {
523     gc_log_lock.Enter();
524
525     const int BUFFERSIZE = 512;
526     static char rgchBuffer[BUFFERSIZE];
527     char *  pBuffer  = &rgchBuffer[0];
528
529     pBuffer[0] = '\n';
530     int buffer_start = 1;
531     int pid_len = sprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, "[%5d]", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging());
532     buffer_start += pid_len;
533     memset(&pBuffer[buffer_start], '-', BUFFERSIZE - buffer_start);
534     int msg_len = _vsnprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args);
535     if (msg_len == -1)
536     {
537         msg_len = BUFFERSIZE - buffer_start;
538     }
539
540     msg_len += buffer_start;
541
542     if ((gc_log_buffer_offset + msg_len) > (gc_log_buffer_size - 12))
543     {
544         char index_str[8];
545         memset (index_str, '-', 8);
546         sprintf_s (index_str, _countof(index_str), "%d", (int)gc_buffer_index);
547         gc_log_buffer[gc_log_buffer_offset] = '\n';
548         memcpy (gc_log_buffer + (gc_log_buffer_offset + 1), index_str, 8);
549
550         gc_buffer_index++;
551         if (gc_buffer_index > max_gc_buffers)
552         {
553             fseek (gc_log, 0, SEEK_SET);
554             gc_buffer_index = 0;
555         }
556         fwrite(gc_log_buffer, gc_log_buffer_size, 1, gc_log);
557         fflush(gc_log);
558         memset (gc_log_buffer, '*', gc_log_buffer_size);
559         gc_log_buffer_offset = 0;
560     }
561
562     memcpy (gc_log_buffer + gc_log_buffer_offset, pBuffer, msg_len);
563     gc_log_buffer_offset += msg_len;
564
565     gc_log_lock.Leave();
566 }
567
568 void GCLog (const char *fmt, ... )
569 {
570     if (gc_log_on && (gc_log != NULL))
571     {
572         va_list     args;
573         va_start(args, fmt);
574         log_va_msg (fmt, args);
575         va_end(args);
576     }
577 }
578 #endif // TRACE_GC && !DACCESS_COMPILE
579
580 #if defined(GC_CONFIG_DRIVEN) && !defined(DACCESS_COMPILE)
581
582 BOOL   gc_config_log_on = FALSE;
583 FILE* gc_config_log = NULL;
584
585 // we keep this much in a buffer and only flush when the buffer is full
586 #define gc_config_log_buffer_size (1*1024) // TEMP
587 uint8_t* gc_config_log_buffer = 0;
588 size_t gc_config_log_buffer_offset = 0;
589
590 // For config since we log so little we keep the whole history. Also it's only
591 // ever logged by one thread so no need to synchronize.
592 void log_va_msg_config(const char *fmt, va_list args)
593 {
594     const int BUFFERSIZE = 256;
595     static char rgchBuffer[BUFFERSIZE];
596     char *  pBuffer  = &rgchBuffer[0];
597
598     pBuffer[0] = '\n';
599     int buffer_start = 1;
600     int msg_len = _vsnprintf_s (&pBuffer[buffer_start], BUFFERSIZE - buffer_start, _TRUNCATE, fmt, args );
601     assert (msg_len != -1);
602     msg_len += buffer_start;
603
604     if ((gc_config_log_buffer_offset + msg_len) > gc_config_log_buffer_size)
605     {
606         fwrite(gc_config_log_buffer, gc_config_log_buffer_offset, 1, gc_config_log);
607         fflush(gc_config_log);
608         gc_config_log_buffer_offset = 0;
609     }
610
611     memcpy (gc_config_log_buffer + gc_config_log_buffer_offset, pBuffer, msg_len);
612     gc_config_log_buffer_offset += msg_len;
613 }
614
615 void GCLogConfig (const char *fmt, ... )
616 {
617     if (gc_config_log_on && (gc_config_log != NULL))
618     {
619         va_list     args;
620         va_start( args, fmt );
621         log_va_msg_config (fmt, args);
622     }
623 }
624 #endif // GC_CONFIG_DRIVEN && !DACCESS_COMPILE
625
626 #ifdef SYNCHRONIZATION_STATS
627
628 // Number of GCs have we done since we last logged.
629 static unsigned int         gc_count_during_log;
630  // In ms. This is how often we print out stats.
631 static const unsigned int   log_interval = 5000;
632 // Time (in ms) when we start a new log interval.
633 static unsigned int         log_start_tick;
634 static unsigned int         gc_lock_contended;
635 static int64_t              log_start_hires;
636 // Cycles accumulated in SuspendEE during log_interval.
637 static uint64_t             suspend_ee_during_log;
638 // Cycles accumulated in RestartEE during log_interval.
639 static uint64_t             restart_ee_during_log;
640 static uint64_t             gc_during_log;
641
642 #endif //SYNCHRONIZATION_STATS
643
644 void
645 init_sync_log_stats()
646 {
647 #ifdef SYNCHRONIZATION_STATS
648     if (gc_count_during_log == 0)
649     {
650         gc_heap::init_sync_stats();
651         suspend_ee_during_log = 0;
652         restart_ee_during_log = 0;
653         gc_during_log = 0;
654         gc_lock_contended = 0;
655
656         log_start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
657         log_start_hires = GCToOSInterface::QueryPerformanceCounter();
658     }
659     gc_count_during_log++;
660 #endif //SYNCHRONIZATION_STATS
661 }
662
663 void
664 process_sync_log_stats()
665 {
666 #ifdef SYNCHRONIZATION_STATS
667
668     unsigned int log_elapsed = GCToOSInterface::GetLowPrecisionTimeStamp() - log_start_tick;
669
670     if (log_elapsed > log_interval)
671     {
672         uint64_t total = GCToOSInterface::QueryPerformanceCounter() - log_start_hires;
673         // Print out the cycles we spent on average in each suspend and restart.
674         printf("\n_________________________________________________________________________________\n"
675             "Past %d(s): #%3d GCs; Total gc_lock contended: %8u; GC: %12u\n"
676             "SuspendEE: %8u; RestartEE: %8u GC %.3f%%\n",
677             log_interval / 1000,
678             gc_count_during_log,
679             gc_lock_contended,
680             (unsigned int)(gc_during_log / gc_count_during_log),
681             (unsigned int)(suspend_ee_during_log / gc_count_during_log),
682             (unsigned int)(restart_ee_during_log / gc_count_during_log),
683             (double)(100.0f * gc_during_log / total));
684         gc_heap::print_sync_stats(gc_count_during_log);
685
686         gc_count_during_log = 0;
687     }
688 #endif //SYNCHRONIZATION_STATS
689 }
690
691 #ifdef MULTIPLE_HEAPS
692
693 enum gc_join_stage
694 {
695     gc_join_init_cpu_mapping = 0,
696     gc_join_done = 1,
697     gc_join_generation_determined = 2,
698     gc_join_begin_mark_phase = 3,
699     gc_join_scan_dependent_handles = 4,
700     gc_join_rescan_dependent_handles = 5,
701     gc_join_scan_sizedref_done = 6,
702     gc_join_null_dead_short_weak = 7,
703     gc_join_scan_finalization = 8,
704     gc_join_null_dead_long_weak = 9, 
705     gc_join_null_dead_syncblk = 10, 
706     gc_join_decide_on_compaction = 11, 
707     gc_join_rearrange_segs_compaction = 12, 
708     gc_join_adjust_handle_age_compact = 13,
709     gc_join_adjust_handle_age_sweep = 14,
710     gc_join_begin_relocate_phase = 15,
711     gc_join_relocate_phase_done = 16,
712     gc_join_verify_objects_done = 17,
713     gc_join_start_bgc = 18,
714     gc_join_restart_ee = 19,
715     gc_join_concurrent_overflow = 20,
716     gc_join_suspend_ee = 21,
717     gc_join_bgc_after_ephemeral = 22,
718     gc_join_allow_fgc = 23,
719     gc_join_bgc_sweep = 24,
720     gc_join_suspend_ee_verify = 25,
721     gc_join_restart_ee_verify = 26,
722     gc_join_set_state_free = 27,
723     gc_r_join_update_card_bundle = 28,
724     gc_join_after_absorb = 29, 
725     gc_join_verify_copy_table = 30,
726     gc_join_after_reset = 31,
727     gc_join_after_ephemeral_sweep = 32,
728     gc_join_after_profiler_heap_walk = 33,
729     gc_join_minimal_gc = 34,
730     gc_join_after_commit_soh_no_gc = 35,
731     gc_join_expand_loh_no_gc = 36,
732     gc_join_final_no_gc = 37,
733     gc_join_disable_software_write_watch = 38,
734     gc_join_max = 39
735 };
736
737 enum gc_join_flavor
738 {
739     join_flavor_server_gc = 0,
740     join_flavor_bgc = 1
741 };
742   
743 #define first_thread_arrived 2
744 #pragma warning(push)
745 #pragma warning(disable:4324) // don't complain if DECLSPEC_ALIGN actually pads
746 struct DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE) join_structure
747 {
748     // Shared non volatile keep on separate line to prevent eviction
749     int n_threads;
750
751     // Keep polling/wait structures on separate line write once per join
752     DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE)
753     GCEvent joined_event[3]; // the last event in the array is only used for first_thread_arrived.
754     Volatile<int> lock_color;
755     VOLATILE(BOOL) wait_done;
756     VOLATILE(BOOL) joined_p;
757
758     // Keep volatile counted locks on separate cache line write many per join
759     DECLSPEC_ALIGN(HS_CACHE_LINE_SIZE)
760     VOLATILE(int32_t) join_lock;
761     VOLATILE(int32_t) r_join_lock;
762
763 };
764 #pragma warning(pop)
765
766 enum join_type 
767 {
768     type_last_join = 0, 
769     type_join = 1, 
770     type_restart = 2, 
771     type_first_r_join = 3, 
772     type_r_join = 4
773 };
774
775 enum join_time 
776 {
777     time_start = 0, 
778     time_end = 1
779 };
780
781 enum join_heap_index
782 {
783     join_heap_restart = 100,
784     join_heap_r_restart = 200
785 };
786
787 struct join_event
788 {
789     uint32_t heap;
790     join_time time;
791     join_type type;
792 };
793
794 class t_join
795 {
796     join_structure join_struct;
797
798     int id;
799     gc_join_flavor flavor;
800
801 #ifdef JOIN_STATS
802     uint64_t start[MAX_SUPPORTED_CPUS], end[MAX_SUPPORTED_CPUS], start_seq;
803     // remember join id and last thread to arrive so restart can use these
804     int thd;
805     // we want to print statistics every 10 seconds - this is to remember the start of the 10 sec interval
806     uint32_t start_tick;
807     // counters for joins, in 1000's of clock cycles
808     uint64_t elapsed_total[gc_join_max], wake_total[gc_join_max], seq_loss_total[gc_join_max], par_loss_total[gc_join_max], in_join_total[gc_join_max];
809 #endif //JOIN_STATS
810
811 public:
812     BOOL init (int n_th, gc_join_flavor f)
813     {
814         dprintf (JOIN_LOG, ("Initializing join structure"));
815         join_struct.n_threads = n_th;
816         join_struct.lock_color = 0;
817         for (int i = 0; i < 3; i++)
818         {
819             if (!join_struct.joined_event[i].IsValid())
820             {
821                 join_struct.joined_p = FALSE;
822                 dprintf (JOIN_LOG, ("Creating join event %d", i));
823                 // TODO - changing this to a non OS event
824                 // because this is also used by BGC threads which are 
825                 // managed threads and WaitEx does not allow you to wait
826                 // for an OS event on a managed thread.
827                 // But we are not sure if this plays well in the hosting 
828                 // environment.
829                 //join_struct.joined_event[i].CreateOSManualEventNoThrow(FALSE);
830                 if (!join_struct.joined_event[i].CreateManualEventNoThrow(FALSE))
831                     return FALSE;
832             }
833         }
834         join_struct.join_lock = join_struct.n_threads;
835         join_struct.r_join_lock = join_struct.n_threads;
836         join_struct.wait_done = FALSE;
837         flavor = f;
838
839 #ifdef JOIN_STATS
840         start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
841 #endif //JOIN_STATS
842
843         return TRUE;
844     }
845     
846     void destroy ()
847     {
848         dprintf (JOIN_LOG, ("Destroying join structure"));
849         for (int i = 0; i < 3; i++)
850         {
851             if (join_struct.joined_event[i].IsValid())
852                 join_struct.joined_event[i].CloseEvent();
853         }
854     }
855
856     inline void fire_event (int heap, join_time time, join_type type, int join_id)
857     {
858         FIRE_EVENT(GCJoin_V2, heap, time, type, join_id);
859     }
860
861     void join (gc_heap* gch, int join_id)
862     {
863 #ifdef JOIN_STATS
864         // parallel execution ends here
865         end[gch->heap_number] = get_ts();
866 #endif //JOIN_STATS
867
868         assert (!join_struct.joined_p);
869         int color = join_struct.lock_color.LoadWithoutBarrier();
870
871         if (Interlocked::Decrement(&join_struct.join_lock) != 0)
872         {
873             dprintf (JOIN_LOG, ("join%d(%d): Join() Waiting...join_lock is now %d", 
874                 flavor, join_id, (int32_t)(join_struct.join_lock)));
875
876             fire_event (gch->heap_number, time_start, type_join, join_id);
877
878             //busy wait around the color
879             if (color == join_struct.lock_color.LoadWithoutBarrier())
880             {
881 respin:
882                 int spin_count = 128 * yp_spin_count_unit;
883                 for (int j = 0; j < spin_count; j++)
884                 {
885                     if (color != join_struct.lock_color.LoadWithoutBarrier())
886                     {
887                         break;
888                     }
889                     YieldProcessor();           // indicate to the processor that we are spinning
890                 }
891
892                 // we've spun, and if color still hasn't changed, fall into hard wait
893                 if (color == join_struct.lock_color.LoadWithoutBarrier())
894                 {
895                     dprintf (JOIN_LOG, ("join%d(%d): Join() hard wait on reset event %d, join_lock is now %d", 
896                         flavor, join_id, color, (int32_t)(join_struct.join_lock)));
897
898                     //Thread* current_thread = GCToEEInterface::GetThread();
899                     //BOOL cooperative_mode = gc_heap::enable_preemptive ();
900                     uint32_t dwJoinWait = join_struct.joined_event[color].Wait(INFINITE, FALSE);
901                     //gc_heap::disable_preemptive (cooperative_mode);
902
903                     if (dwJoinWait != WAIT_OBJECT_0)
904                     {
905                         STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
906                         FATAL_GC_ERROR ();
907                     }
908                 }
909
910                 // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
911                 if (color == join_struct.lock_color.LoadWithoutBarrier())
912                 {
913                     goto respin;
914                 }
915
916                 dprintf (JOIN_LOG, ("join%d(%d): Join() done, join_lock is %d", 
917                     flavor, join_id, (int32_t)(join_struct.join_lock)));
918             }
919
920             fire_event (gch->heap_number, time_end, type_join, join_id);
921
922 #ifdef JOIN_STATS
923             // parallel execution starts here
924             start[gch->heap_number] = get_ts();
925             Interlocked::ExchangeAdd(&in_join_total[join_id], (start[gch->heap_number] - end[gch->heap_number]));
926 #endif //JOIN_STATS
927         }
928         else
929         {
930             fire_event (gch->heap_number, time_start, type_last_join, join_id);
931
932             join_struct.joined_p = TRUE;
933             dprintf (JOIN_LOG, ("join%d(%d): Last thread to complete the join, setting id", flavor, join_id));
934             join_struct.joined_event[!color].Reset();
935             id = join_id;
936             // this one is alone so it can proceed
937 #ifdef JOIN_STATS
938             // remember the join id, the last thread arriving, the start of the sequential phase,
939             // and keep track of the cycles spent waiting in the join
940             thd = gch->heap_number;
941             start_seq = get_ts();
942             Interlocked::ExchangeAdd(&in_join_total[join_id], (start_seq - end[gch->heap_number]));
943 #endif //JOIN_STATS
944         }
945     }
946
947     // Reverse join - first thread gets here does the work; other threads will only proceed
948     // after the work is done.
949     // Note that you cannot call this twice in a row on the same thread. Plus there's no 
950     // need to call it twice in row - you should just merge the work.
951     BOOL r_join (gc_heap* gch, int join_id)
952     {
953
954         if (join_struct.n_threads == 1)
955         {
956             return TRUE;
957         }
958
959         if (Interlocked::CompareExchange(&join_struct.r_join_lock, 0, join_struct.n_threads) == 0)
960         {
961             if (!join_struct.wait_done)
962             {
963                 dprintf (JOIN_LOG, ("r_join() Waiting..."));
964
965                 fire_event (gch->heap_number, time_start, type_join, join_id);
966
967                 //busy wait around the color
968                 if (!join_struct.wait_done)
969                 {
970         respin:
971                     int spin_count = 256 * yp_spin_count_unit;
972                     for (int j = 0; j < spin_count; j++)
973                     {
974                         if (join_struct.wait_done)
975                         {
976                             break;
977                         }
978                         YieldProcessor();           // indicate to the processor that we are spinning
979                     }
980
981                     // we've spun, and if color still hasn't changed, fall into hard wait
982                     if (!join_struct.wait_done)
983                     {
984                         dprintf (JOIN_LOG, ("Join() hard wait on reset event %d", first_thread_arrived));
985                         uint32_t dwJoinWait = join_struct.joined_event[first_thread_arrived].Wait(INFINITE, FALSE);
986                         if (dwJoinWait != WAIT_OBJECT_0)
987                         {
988                             STRESS_LOG1 (LF_GC, LL_FATALERROR, "joined event wait failed with code: %Ix", dwJoinWait);
989                             FATAL_GC_ERROR ();
990                         }
991                     }
992
993                     // avoid race due to the thread about to reset the event (occasionally) being preempted before ResetEvent()
994                     if (!join_struct.wait_done)
995                     {
996                         goto respin;
997                     }
998
999                     dprintf (JOIN_LOG, ("r_join() done"));
1000                 }
1001
1002                 fire_event (gch->heap_number, time_end, type_join, join_id);
1003             }
1004
1005             return FALSE;
1006         }
1007         else
1008         {
1009             fire_event (gch->heap_number, time_start, type_first_r_join, join_id);
1010             return TRUE;
1011         }
1012     }
1013
1014 #ifdef JOIN_STATS
1015     uint64_t get_ts()
1016     {
1017         return GCToOSInterface::QueryPerformanceCounter();
1018     }
1019
1020     void start_ts (gc_heap* gch)
1021     {
1022         // parallel execution ends here
1023         start[gch->heap_number] = get_ts();
1024     }
1025 #endif //JOIN_STATS
1026
1027     void restart()
1028     {
1029 #ifdef JOIN_STATS
1030         uint64_t elapsed_seq = get_ts() - start_seq;
1031         uint64_t max = 0, sum = 0, wake = 0;
1032         uint64_t min_ts = start[0];
1033         for (int i = 1; i < join_struct.n_threads; i++)
1034         {
1035             if(min_ts > start[i]) min_ts = start[i];
1036         }
1037
1038         for (int i = 0; i < join_struct.n_threads; i++)
1039         {
1040             uint64_t wake_delay = start[i] - min_ts;
1041             uint64_t elapsed = end[i] - start[i];
1042             if (max < elapsed)
1043                 max = elapsed;
1044             sum += elapsed;
1045             wake += wake_delay;
1046         }
1047         uint64_t seq_loss = (join_struct.n_threads - 1)*elapsed_seq;
1048         uint64_t par_loss = join_struct.n_threads*max - sum;
1049         double efficiency = 0.0;
1050         if (max > 0)
1051             efficiency = sum*100.0/(join_struct.n_threads*max);
1052
1053         const double ts_scale = 1e-6;
1054
1055         // enable this printf to get statistics on each individual join as it occurs
1056 //      printf("join #%3d  seq_loss = %5g   par_loss = %5g  efficiency = %3.0f%%\n", join_id, ts_scale*seq_loss, ts_scale*par_loss, efficiency);
1057
1058         elapsed_total[id] += sum;
1059         wake_total[id] += wake;
1060         seq_loss_total[id] += seq_loss;
1061         par_loss_total[id] += par_loss;
1062
1063         // every 10 seconds, print a summary of the time spent in each type of join
1064         if (GCToOSInterface::GetLowPrecisionTimeStamp() - start_tick > 10*1000)
1065         {
1066             printf("**** summary *****\n");
1067             for (int i = 0; i < 16; i++)
1068             {
1069                 printf("join #%3d  elapsed_total = %8g wake_loss = %8g seq_loss = %8g  par_loss = %8g  in_join_total = %8g\n",
1070                    i,
1071                    ts_scale*elapsed_total[i],
1072                    ts_scale*wake_total[i],
1073                    ts_scale*seq_loss_total[i],
1074                    ts_scale*par_loss_total[i],
1075                    ts_scale*in_join_total[i]);
1076                 elapsed_total[i] = wake_total[i] = seq_loss_total[i] = par_loss_total[i] = in_join_total[i] = 0;
1077             }
1078             start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
1079         }
1080 #endif //JOIN_STATS
1081
1082         fire_event (join_heap_restart, time_start, type_restart, -1);
1083         assert (join_struct.joined_p);
1084         join_struct.joined_p = FALSE;
1085         join_struct.join_lock = join_struct.n_threads;
1086         dprintf (JOIN_LOG, ("join%d(%d): Restarting from join: join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
1087 //        printf("restart from join #%d at cycle %u from start of gc\n", join_id, GetCycleCount32() - gc_start);
1088         int color = join_struct.lock_color.LoadWithoutBarrier();
1089         join_struct.lock_color = !color;
1090         join_struct.joined_event[color].Set();
1091
1092 //        printf("Set joined_event %d\n", !join_struct.lock_color);
1093
1094         fire_event (join_heap_restart, time_end, type_restart, -1);
1095
1096 #ifdef JOIN_STATS
1097         start[thd] = get_ts();
1098 #endif //JOIN_STATS
1099     }
1100     
1101     BOOL joined()
1102     {
1103         dprintf (JOIN_LOG, ("join%d(%d): joined, join_lock is %d", flavor, id, (int32_t)(join_struct.join_lock)));
1104         return join_struct.joined_p;
1105     }
1106
1107     void r_restart()
1108     {
1109         if (join_struct.n_threads != 1)
1110         {
1111             fire_event (join_heap_r_restart, time_start, type_restart, -1);
1112             join_struct.wait_done = TRUE;
1113             join_struct.joined_event[first_thread_arrived].Set();
1114             fire_event (join_heap_r_restart, time_end, type_restart, -1);
1115         }
1116     }
1117
1118     void r_init()
1119     {
1120         if (join_struct.n_threads != 1)
1121         {
1122             join_struct.r_join_lock = join_struct.n_threads;
1123             join_struct.wait_done = FALSE;
1124             join_struct.joined_event[first_thread_arrived].Reset();
1125         }
1126     }
1127 };
1128
1129 t_join gc_t_join;
1130
1131 #ifdef BACKGROUND_GC
1132 t_join bgc_t_join;
1133 #endif //BACKGROUND_GC
1134
1135 #endif //MULTIPLE_HEAPS
1136
1137 #define spin_and_switch(count_to_spin, expr) \
1138 { \
1139     for (int j = 0; j < count_to_spin; j++) \
1140     { \
1141         if (expr) \
1142         { \
1143             break;\
1144         } \
1145         YieldProcessor(); \
1146     } \
1147     if (!(expr)) \
1148     { \
1149         GCToOSInterface::YieldThread(0); \
1150     } \
1151 }
1152
1153 #ifndef DACCESS_COMPILE
1154 #ifdef BACKGROUND_GC
1155
1156 #define max_pending_allocs 64
1157
1158 class exclusive_sync
1159 {
1160     // TODO - verify that this is the right syntax for Volatile.
1161     VOLATILE(uint8_t*) rwp_object;
1162     VOLATILE(int32_t) needs_checking;
1163     
1164     int spin_count;
1165
1166     uint8_t cache_separator[HS_CACHE_LINE_SIZE - sizeof (int) - sizeof (int32_t)];
1167
1168     // TODO - perhaps each object should be on its own cache line...
1169     VOLATILE(uint8_t*) alloc_objects[max_pending_allocs];
1170
1171     int find_free_index ()
1172     {
1173         for (int i = 0; i < max_pending_allocs; i++)
1174         {
1175             if (alloc_objects [i] == (uint8_t*)0)
1176             {
1177                 return i;
1178             }
1179         }
1180  
1181         return -1;
1182     }
1183
1184 public:
1185     void init()
1186     {
1187         spin_count = 32 * (g_num_processors - 1);
1188         rwp_object = 0;
1189         needs_checking = 0;
1190         for (int i = 0; i < max_pending_allocs; i++)
1191         {
1192             alloc_objects [i] = (uint8_t*)0;
1193         }
1194     }
1195
1196     void check()
1197     {
1198         for (int i = 0; i < max_pending_allocs; i++)
1199         {
1200             if (alloc_objects [i] != (uint8_t*)0)
1201             {
1202                 GCToOSInterface::DebugBreak();
1203             }
1204         }
1205     }
1206
1207     void bgc_mark_set (uint8_t* obj)
1208     {
1209         dprintf (3, ("cm: probing %Ix", obj));
1210 retry:
1211         if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0)
1212         {
1213             // If we spend too much time spending all the allocs,
1214             // consider adding a high water mark and scan up
1215             // to that; we'll need to interlock in done when
1216             // we update the high watermark.
1217             for (int i = 0; i < max_pending_allocs; i++)
1218             {
1219                 if (obj == alloc_objects[i])
1220                 {
1221                     needs_checking = 0;
1222                     dprintf (3, ("cm: will spin"));
1223                     spin_and_switch (spin_count, (obj != alloc_objects[i]));
1224                     goto retry;
1225                 }
1226             }
1227
1228             rwp_object = obj;
1229             needs_checking = 0;
1230             dprintf (3, ("cm: set %Ix", obj));
1231             return;
1232         }
1233         else
1234         {
1235             spin_and_switch (spin_count, (needs_checking == 0));
1236             goto retry;
1237         }
1238     }
1239
1240     int loh_alloc_set (uint8_t* obj)
1241     {
1242         if (!gc_heap::cm_in_progress)
1243         {
1244             return -1;
1245         }
1246
1247 retry:
1248         dprintf (3, ("loh alloc: probing %Ix", obj));
1249
1250         if (Interlocked::CompareExchange(&needs_checking, 1, 0) == 0)
1251         {
1252             if (obj == rwp_object)
1253             {
1254                 needs_checking = 0;
1255                 spin_and_switch (spin_count, (obj != rwp_object));
1256                 goto retry;
1257             }
1258             else
1259             {
1260                 int cookie = find_free_index();
1261
1262                 if (cookie != -1)
1263                 {
1264                     alloc_objects[cookie] = obj;
1265                     needs_checking = 0;
1266                     //if (cookie >= 4)
1267                     //{
1268                     //    GCToOSInterface::DebugBreak();
1269                     //}
1270
1271                     dprintf (3, ("loh alloc: set %Ix at %d", obj, cookie));
1272                     return cookie;
1273                 } 
1274                 else 
1275                 {
1276                     needs_checking = 0;
1277                     dprintf (3, ("loh alloc: setting %Ix will spin to acquire a free index", obj));
1278                     spin_and_switch (spin_count, (find_free_index () != -1));
1279                     goto retry;
1280                 }
1281             }
1282         }
1283         else
1284         {
1285             dprintf (3, ("loh alloc: will spin on checking %Ix", obj));
1286             spin_and_switch (spin_count, (needs_checking == 0));
1287             goto retry;
1288         }
1289     }
1290
1291     void bgc_mark_done ()
1292     {
1293         dprintf (3, ("cm: release lock on %Ix", (uint8_t *)rwp_object));
1294         rwp_object = 0;
1295     }
1296
1297     void loh_alloc_done_with_index (int index)
1298     {
1299         dprintf (3, ("loh alloc: release lock on %Ix based on %d", (uint8_t *)alloc_objects[index], index));
1300         assert ((index >= 0) && (index < max_pending_allocs)); 
1301         alloc_objects[index] = (uint8_t*)0;
1302     }
1303
1304     void loh_alloc_done (uint8_t* obj)
1305     {
1306 #ifdef BACKGROUND_GC
1307         if (!gc_heap::cm_in_progress)
1308         {
1309             return;
1310         }
1311
1312         for (int i = 0; i < max_pending_allocs; i++)
1313         {
1314             if (alloc_objects [i] == obj)
1315             {
1316                 dprintf (3, ("loh alloc: release lock on %Ix at %d", (uint8_t *)alloc_objects[i], i));
1317                 alloc_objects[i] = (uint8_t*)0;
1318                 return;
1319             }
1320         }
1321 #endif //BACKGROUND_GC
1322     }
1323 };
1324
1325 // Note that this class was written assuming just synchronization between
1326 // one background GC thread and multiple user threads that might request 
1327 // an FGC - it does not take into account what kind of locks the multiple
1328 // user threads might be holding at the time (eg, there could only be one
1329 // user thread requesting an FGC because it needs to take gc_lock first)
1330 // so you'll see checks that may not be necessary if you take those conditions
1331 // into consideration.
1332 //
1333 // With the introduction of Server Background GC we no longer use this
1334 // class to do synchronization between FGCs and BGC.
1335 class recursive_gc_sync
1336 {
1337     static VOLATILE(int32_t) foreground_request_count;//initial state 0
1338     static VOLATILE(BOOL) gc_background_running; //initial state FALSE
1339     static VOLATILE(int32_t) foreground_count; // initial state 0;
1340     static VOLATILE(uint32_t) foreground_gate; // initial state FALSE;
1341     static GCEvent foreground_complete;//Auto Reset
1342     static GCEvent foreground_allowed;//Auto Reset
1343 public:
1344     static void begin_background();
1345     static void end_background();
1346     static void begin_foreground();
1347     static void end_foreground();
1348     BOOL allow_foreground ();
1349     static BOOL init();
1350     static void shutdown();
1351     static BOOL background_running_p() {return gc_background_running;}
1352 };
1353
1354 VOLATILE(int32_t) recursive_gc_sync::foreground_request_count = 0;//initial state 0
1355 VOLATILE(int32_t) recursive_gc_sync::foreground_count = 0; // initial state 0;
1356 VOLATILE(BOOL) recursive_gc_sync::gc_background_running = FALSE; //initial state FALSE
1357 VOLATILE(uint32_t) recursive_gc_sync::foreground_gate = 0;
1358 GCEvent recursive_gc_sync::foreground_complete;//Auto Reset
1359 GCEvent recursive_gc_sync::foreground_allowed;//Manual Reset
1360
1361 BOOL recursive_gc_sync::init ()
1362 {
1363     foreground_request_count = 0;
1364     foreground_count = 0;
1365     gc_background_running = FALSE;
1366     foreground_gate = 0;
1367
1368     if (!foreground_complete.CreateOSAutoEventNoThrow(FALSE))
1369     {
1370         goto error;
1371     }
1372     if (!foreground_allowed.CreateManualEventNoThrow(FALSE))
1373     {
1374         goto error;
1375     }
1376     return TRUE;
1377
1378 error:
1379     shutdown();
1380     return FALSE;
1381
1382 }
1383
1384 void recursive_gc_sync::shutdown()
1385 {
1386     if (foreground_complete.IsValid())
1387         foreground_complete.CloseEvent();
1388     if (foreground_allowed.IsValid())
1389         foreground_allowed.CloseEvent();
1390 }
1391
1392 void recursive_gc_sync::begin_background()
1393 {
1394     dprintf (2, ("begin background"));
1395     foreground_request_count = 1;
1396     foreground_count = 1;
1397     foreground_allowed.Reset();
1398     gc_background_running = TRUE;
1399 }
1400 void recursive_gc_sync::end_background()
1401 {
1402     dprintf (2, ("end background"));
1403     gc_background_running = FALSE;
1404     foreground_gate = 1;
1405     foreground_allowed.Set();
1406 }
1407
1408 void recursive_gc_sync::begin_foreground()
1409 {
1410     dprintf (2, ("begin_foreground"));
1411
1412     bool cooperative_mode = false;
1413     if (gc_background_running)
1414     {
1415         gc_heap::fire_alloc_wait_event_begin (awr_fgc_wait_for_bgc);
1416         gc_heap::alloc_wait_event_p = TRUE;
1417
1418 try_again_top:
1419
1420         Interlocked::Increment (&foreground_request_count);
1421
1422 try_again_no_inc:
1423         dprintf(2, ("Waiting sync gc point"));
1424         assert (foreground_allowed.IsValid());
1425         assert (foreground_complete.IsValid());
1426
1427         cooperative_mode = gc_heap::enable_preemptive ();
1428
1429         foreground_allowed.Wait(INFINITE, FALSE);
1430
1431         dprintf(2, ("Waiting sync gc point is done"));
1432
1433         gc_heap::disable_preemptive (cooperative_mode);
1434
1435         if (foreground_gate)
1436         {
1437             Interlocked::Increment (&foreground_count);
1438             dprintf (2, ("foreground_count: %d", (int32_t)foreground_count));
1439             if (foreground_gate)
1440             {
1441                 gc_heap::settings.concurrent = FALSE;
1442                 return;
1443             }
1444             else
1445             {
1446                 end_foreground();
1447                 goto try_again_top;
1448             }
1449         }
1450         else
1451         {
1452             goto try_again_no_inc;
1453         }
1454     }
1455 }
1456
1457 void recursive_gc_sync::end_foreground()
1458 {
1459     dprintf (2, ("end_foreground"));
1460     if (gc_background_running)
1461     {
1462         Interlocked::Decrement (&foreground_request_count);
1463         dprintf (2, ("foreground_count before decrement: %d", (int32_t)foreground_count));
1464         if (Interlocked::Decrement (&foreground_count) == 0)
1465         {
1466             //c_write ((BOOL*)&foreground_gate, 0);
1467             // TODO - couldn't make the syntax work with Volatile<T>
1468             foreground_gate = 0;
1469             if (foreground_count == 0)
1470             {
1471                 foreground_allowed.Reset ();
1472                 dprintf(2, ("setting foreground complete event"));
1473                 foreground_complete.Set();
1474             }
1475         }
1476     }
1477 }
1478
1479 inline
1480 BOOL recursive_gc_sync::allow_foreground()
1481 {
1482     assert (gc_heap::settings.concurrent);
1483     dprintf (100, ("enter allow_foreground, f_req_count: %d, f_count: %d",
1484                    (int32_t)foreground_request_count, (int32_t)foreground_count));
1485
1486     BOOL did_fgc = FALSE;
1487
1488     //if we have suspended the EE, just return because
1489     //some thread could be waiting on this to proceed.
1490     if (!GCHeap::GcInProgress)
1491     {
1492         //TODO BACKGROUND_GC This is to stress the concurrency between
1493         //background and foreground
1494 //        gc_heap::disallow_new_allocation (0);
1495
1496         //GCToOSInterface::YieldThread(0);
1497
1498         //END of TODO
1499         if (foreground_request_count != 0)
1500         {
1501             //foreground wants to run
1502             //save the important settings
1503             //TODO BACKGROUND_GC be more selective about the important settings.
1504             gc_mechanisms saved_settings = gc_heap::settings;
1505             do
1506             {
1507                 did_fgc = TRUE;
1508                 //c_write ((BOOL*)&foreground_gate, 1);
1509                 // TODO - couldn't make the syntax work with Volatile<T>
1510                 foreground_gate = 1;
1511                 foreground_allowed.Set ();
1512                 foreground_complete.Wait (INFINITE, FALSE);
1513             }while (/*foreground_request_count ||*/ foreground_gate);
1514
1515             assert (!foreground_gate);
1516
1517             //restore the important settings
1518             gc_heap::settings = saved_settings;
1519             GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
1520             //the background GC shouldn't be using gc_high and gc_low
1521             //gc_low = lowest_address;
1522             //gc_high = highest_address;
1523         }
1524
1525         //TODO BACKGROUND_GC This is to stress the concurrency between
1526         //background and foreground
1527 //        gc_heap::allow_new_allocation (0);
1528         //END of TODO
1529     }
1530
1531     dprintf (100, ("leave allow_foreground"));
1532     assert (gc_heap::settings.concurrent);
1533     return did_fgc;
1534 }
1535
1536 #endif //BACKGROUND_GC
1537 #endif //DACCESS_COMPILE
1538
1539
1540 #if  defined(COUNT_CYCLES)
1541 #ifdef _MSC_VER
1542 #pragma warning(disable:4035)
1543 #endif //_MSC_VER
1544
1545 static
1546 unsigned        GetCycleCount32()        // enough for about 40 seconds
1547 {
1548 __asm   push    EDX
1549 __asm   _emit   0x0F
1550 __asm   _emit   0x31
1551 __asm   pop     EDX
1552 };
1553
1554 #pragma warning(default:4035)
1555
1556 #endif //COUNT_CYCLES
1557
1558 #ifdef TIME_GC
1559 int mark_time, plan_time, sweep_time, reloc_time, compact_time;
1560 #endif //TIME_GC
1561
1562 #ifndef MULTIPLE_HEAPS
1563
1564 #endif // MULTIPLE_HEAPS
1565
1566 void reset_memory (uint8_t* o, size_t sizeo);
1567
1568 #ifdef WRITE_WATCH
1569
1570 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1571 static bool virtual_alloc_hardware_write_watch = false;
1572 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1573
1574 static bool hardware_write_watch_capability = false;
1575
1576 #ifndef DACCESS_COMPILE
1577
1578 //check if the write watch APIs are supported.
1579
1580 void hardware_write_watch_api_supported()
1581 {
1582     if (GCToOSInterface::SupportsWriteWatch())
1583     {
1584         hardware_write_watch_capability = true;
1585         dprintf (2, ("WriteWatch supported"));
1586     }
1587     else
1588     {
1589         dprintf (2,("WriteWatch not supported"));
1590     }
1591 }
1592
1593 #endif //!DACCESS_COMPILE
1594
1595 inline bool can_use_hardware_write_watch()
1596 {
1597     return hardware_write_watch_capability;
1598 }
1599
1600 inline bool can_use_write_watch_for_gc_heap()
1601 {
1602 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1603     return true;
1604 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1605     return can_use_hardware_write_watch();
1606 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
1607 }
1608
1609 inline bool can_use_write_watch_for_card_table()
1610 {
1611 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
1612     return true;
1613 #else
1614     return can_use_hardware_write_watch();
1615 #endif
1616 }
1617
1618 #else
1619 #define mem_reserve (MEM_RESERVE)
1620 #endif //WRITE_WATCH
1621
1622 //check if the low memory notification is supported
1623
1624 #ifndef DACCESS_COMPILE
1625
1626 void WaitLongerNoInstru (int i)
1627 {
1628     // every 8th attempt:
1629     bool bToggleGC = GCToEEInterface::EnablePreemptiveGC();
1630
1631     // if we're waiting for gc to finish, we should block immediately
1632     if (g_fSuspensionPending == 0)
1633     {
1634         if  (g_num_processors > 1)
1635         {
1636             YieldProcessor();           // indicate to the processor that we are spinning
1637             if  (i & 0x01f)
1638                 GCToOSInterface::YieldThread (0);
1639             else
1640                 GCToOSInterface::Sleep (5);
1641         }
1642         else
1643             GCToOSInterface::Sleep (5);
1644     }
1645
1646     // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1647     // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1648     // It is important that the thread is going to wait for GC.  Otherwise the thread
1649     // is in a tight loop.  If the thread has high priority, the perf is going to be very BAD.
1650     if (bToggleGC)
1651     {
1652 #ifdef _DEBUG
1653         // In debug builds, all enter_spin_lock operations go through this code.  If a GC has
1654         // started, it is important to block until the GC thread calls set_gc_done (since it is
1655         // guaranteed to have cleared g_TrapReturningThreads by this point).  This avoids livelock
1656         // conditions which can otherwise occur if threads are allowed to spin in this function
1657         // (and therefore starve the GC thread) between the point when the GC thread sets the
1658         // WaitForGC event and the point when the GC thread clears g_TrapReturningThreads.
1659         if (gc_heap::gc_started)
1660         {
1661             gc_heap::wait_for_gc_done();
1662         }
1663 #endif // _DEBUG
1664         GCToEEInterface::DisablePreemptiveGC();
1665     }
1666     else if (g_fSuspensionPending > 0)
1667     {
1668         g_theGCHeap->WaitUntilGCComplete();
1669     }
1670 }
1671
1672 inline
1673 static void safe_switch_to_thread()
1674 {
1675     bool cooperative_mode = gc_heap::enable_preemptive();
1676
1677     GCToOSInterface::YieldThread(0);
1678
1679     gc_heap::disable_preemptive(cooperative_mode);
1680 }
1681
1682 //
1683 // We need the following methods to have volatile arguments, so that they can accept
1684 // raw pointers in addition to the results of the & operator on Volatile<T>.
1685 //
1686 inline
1687 static void enter_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
1688 {
1689 retry:
1690
1691     if (Interlocked::CompareExchange(lock, 0, -1) >= 0)
1692     {
1693         unsigned int i = 0;
1694         while (VolatileLoad(lock) >= 0)
1695         {
1696             if ((++i & 7) && !IsGCInProgress())
1697             {
1698                 if  (g_num_processors > 1)
1699                 {
1700 #ifndef MULTIPLE_HEAPS
1701                     int spin_count = 32 * yp_spin_count_unit;
1702 #else //!MULTIPLE_HEAPS
1703                     int spin_count = yp_spin_count_unit;
1704 #endif //!MULTIPLE_HEAPS
1705                     for (int j = 0; j < spin_count; j++)
1706                     {
1707                         if  (VolatileLoad(lock) < 0 || IsGCInProgress())
1708                             break;
1709                         YieldProcessor();           // indicate to the processor that we are spinning
1710                     }
1711                     if  (VolatileLoad(lock) >= 0 && !IsGCInProgress())
1712                     {
1713                         safe_switch_to_thread();
1714                     }
1715                 }
1716                 else
1717                 {
1718                     safe_switch_to_thread();
1719                 }
1720             }
1721             else
1722             {
1723                 WaitLongerNoInstru(i);
1724             }
1725         }
1726         goto retry;
1727     }
1728 }
1729
1730 inline
1731 static BOOL try_enter_spin_lock_noinstru(RAW_KEYWORD(volatile) int32_t* lock)
1732 {
1733     return (Interlocked::CompareExchange(&*lock, 0, -1) < 0);
1734 }
1735
1736 inline
1737 static void leave_spin_lock_noinstru (RAW_KEYWORD(volatile) int32_t* lock)
1738 {
1739     VolatileStore<int32_t>((int32_t*)lock, -1);
1740 }
1741
1742 #ifdef _DEBUG
1743
1744 inline
1745 static void enter_spin_lock(GCSpinLock *pSpinLock)
1746 {
1747     enter_spin_lock_noinstru(&pSpinLock->lock);
1748     assert (pSpinLock->holding_thread == (Thread*)-1);
1749     pSpinLock->holding_thread = GCToEEInterface::GetThread();
1750 }
1751
1752 inline
1753 static BOOL try_enter_spin_lock(GCSpinLock *pSpinLock)
1754 {
1755     BOOL ret = try_enter_spin_lock_noinstru(&pSpinLock->lock);
1756     if (ret)
1757         pSpinLock->holding_thread = GCToEEInterface::GetThread();
1758     return ret;
1759 }
1760
1761 inline
1762 static void leave_spin_lock(GCSpinLock *pSpinLock)
1763 {
1764     bool gc_thread_p = GCToEEInterface::WasCurrentThreadCreatedByGC();
1765 //    _ASSERTE((pSpinLock->holding_thread == GCToEEInterface::GetThread()) || gc_thread_p || pSpinLock->released_by_gc_p);
1766     pSpinLock->released_by_gc_p = gc_thread_p;
1767     pSpinLock->holding_thread = (Thread*) -1;
1768     if (pSpinLock->lock != -1)
1769         leave_spin_lock_noinstru(&pSpinLock->lock);
1770 }
1771
1772 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock) \
1773     _ASSERTE((pSpinLock)->holding_thread == GCToEEInterface::GetThread());
1774
1775 #define ASSERT_NOT_HOLDING_SPIN_LOCK(pSpinLock) \
1776     _ASSERTE((pSpinLock)->holding_thread != GCToEEInterface::GetThread());
1777
1778 #else //_DEBUG
1779
1780 //In the concurrent version, the Enable/DisablePreemptiveGC is optional because
1781 //the gc thread call WaitLonger.
1782 void WaitLonger (int i
1783 #ifdef SYNCHRONIZATION_STATS
1784     , GCSpinLock* spin_lock
1785 #endif //SYNCHRONIZATION_STATS
1786     )
1787 {
1788 #ifdef SYNCHRONIZATION_STATS
1789     (spin_lock->num_wait_longer)++;
1790 #endif //SYNCHRONIZATION_STATS
1791
1792     // every 8th attempt:
1793     bool bToggleGC = GCToEEInterface::EnablePreemptiveGC();
1794     assert (bToggleGC);
1795
1796     // if we're waiting for gc to finish, we should block immediately
1797     if (!gc_heap::gc_started)
1798     {
1799 #ifdef SYNCHRONIZATION_STATS
1800         (spin_lock->num_switch_thread_w)++;
1801 #endif //SYNCHRONIZATION_STATS
1802         if  (g_num_processors > 1)
1803         {
1804             YieldProcessor();           // indicate to the processor that we are spinning
1805             if  (i & 0x01f)
1806                 GCToOSInterface::YieldThread (0);
1807             else
1808                 GCToOSInterface::Sleep (5);
1809         }
1810         else
1811             GCToOSInterface::Sleep (5);
1812     }
1813
1814     // If CLR is hosted, a thread may reach here while it is in preemptive GC mode,
1815     // or it has no Thread object, in order to force a task to yield, or to triger a GC.
1816     // It is important that the thread is going to wait for GC.  Otherwise the thread
1817     // is in a tight loop.  If the thread has high priority, the perf is going to be very BAD. 
1818     if (gc_heap::gc_started)
1819     {
1820         gc_heap::wait_for_gc_done();
1821     }
1822
1823     if (bToggleGC)
1824     {
1825 #ifdef SYNCHRONIZATION_STATS
1826         (spin_lock->num_disable_preemptive_w)++;
1827 #endif //SYNCHRONIZATION_STATS
1828         GCToEEInterface::DisablePreemptiveGC();
1829     }
1830 }
1831
1832 inline
1833 static void enter_spin_lock (GCSpinLock* spin_lock)
1834 {
1835 retry:
1836
1837     if (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) >= 0)
1838     {
1839         unsigned int i = 0;
1840         while (spin_lock->lock >= 0)
1841         {
1842             if ((++i & 7) && !gc_heap::gc_started)
1843             {
1844                 if  (g_num_processors > 1)
1845                 {
1846 #ifndef MULTIPLE_HEAPS
1847                     int spin_count = 32 * yp_spin_count_unit;
1848 #else //!MULTIPLE_HEAPS
1849                     int spin_count = yp_spin_count_unit;
1850 #endif //!MULTIPLE_HEAPS
1851                     for (int j = 0; j < spin_count; j++)
1852                     {
1853                         if  (spin_lock->lock < 0 || gc_heap::gc_started)
1854                             break;
1855                         YieldProcessor();           // indicate to the processor that we are spinning
1856                     }
1857                     if  (spin_lock->lock >= 0 && !gc_heap::gc_started)
1858                     {
1859 #ifdef SYNCHRONIZATION_STATS
1860                         (spin_lock->num_switch_thread)++;
1861 #endif //SYNCHRONIZATION_STATS
1862                         bool cooperative_mode = gc_heap::enable_preemptive ();
1863
1864                         GCToOSInterface::YieldThread(0);
1865
1866                         gc_heap::disable_preemptive (cooperative_mode);
1867                     }
1868                 }
1869                 else
1870                     GCToOSInterface::YieldThread(0);
1871             }
1872             else
1873             {
1874                 WaitLonger(i
1875 #ifdef SYNCHRONIZATION_STATS
1876                         , spin_lock
1877 #endif //SYNCHRONIZATION_STATS
1878                     );
1879             }
1880         }
1881         goto retry;
1882     }
1883 }
1884
1885 inline BOOL try_enter_spin_lock(GCSpinLock* spin_lock)
1886 {
1887     return (Interlocked::CompareExchange(&spin_lock->lock, 0, -1) < 0);
1888 }
1889
1890 inline
1891 static void leave_spin_lock (GCSpinLock * spin_lock)
1892 {
1893     spin_lock->lock = -1;
1894 }
1895
1896 #define ASSERT_HOLDING_SPIN_LOCK(pSpinLock)
1897
1898 #endif //_DEBUG
1899
1900 bool gc_heap::enable_preemptive ()
1901 {
1902     return GCToEEInterface::EnablePreemptiveGC();
1903 }
1904
1905 void gc_heap::disable_preemptive (bool restore_cooperative)
1906 {
1907     if (restore_cooperative)
1908     {
1909         GCToEEInterface::DisablePreemptiveGC();
1910     }
1911 }
1912
1913 #endif // !DACCESS_COMPILE
1914
1915 typedef void **  PTR_PTR;
1916 //This function clears a piece of memory
1917 // size has to be Dword aligned
1918
1919 inline
1920 void memclr ( uint8_t* mem, size_t size)
1921 {
1922     dprintf (3, ("MEMCLR: %Ix, %d", mem, size));
1923     assert ((size & (sizeof(PTR_PTR)-1)) == 0);
1924     assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1925
1926 #if 0
1927     // The compiler will recognize this pattern and replace it with memset call. We can as well just call 
1928     // memset directly to make it obvious what's going on.
1929     PTR_PTR m = (PTR_PTR) mem;
1930     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
1931         *(m++) = 0;
1932 #endif
1933
1934     memset (mem, 0, size);
1935 }
1936
1937 void memcopy (uint8_t* dmem, uint8_t* smem, size_t size)
1938 {
1939     const size_t sz4ptr = sizeof(PTR_PTR)*4;
1940     const size_t sz2ptr = sizeof(PTR_PTR)*2;
1941     const size_t sz1ptr = sizeof(PTR_PTR)*1;
1942
1943     // size must be a multiple of the pointer size
1944     assert ((size & (sizeof (PTR_PTR)-1)) == 0);
1945     assert (sizeof(PTR_PTR) == DATA_ALIGNMENT);
1946
1947     // copy in groups of four pointer sized things at a time
1948     if (size >= sz4ptr)
1949     {
1950         do
1951         {
1952             ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1953             ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1954             ((PTR_PTR)dmem)[2] = ((PTR_PTR)smem)[2];
1955             ((PTR_PTR)dmem)[3] = ((PTR_PTR)smem)[3];
1956             dmem += sz4ptr;
1957             smem += sz4ptr;
1958         }
1959         while ((size -= sz4ptr) >= sz4ptr);
1960     }
1961
1962     // still two pointer sized things or more left to copy?
1963     if (size & sz2ptr)
1964     {
1965         ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1966         ((PTR_PTR)dmem)[1] = ((PTR_PTR)smem)[1];
1967         dmem += sz2ptr;
1968         smem += sz2ptr;
1969     }
1970
1971     // still one pointer sized thing left to copy?
1972     if (size & sz1ptr)
1973     {
1974         ((PTR_PTR)dmem)[0] = ((PTR_PTR)smem)[0];
1975         // dmem += sz1ptr;
1976         // smem += sz1ptr;
1977     }
1978
1979 }
1980
1981 inline
1982 ptrdiff_t round_down (ptrdiff_t add, int pitch)
1983 {
1984     return ((add / pitch) * pitch);
1985 }
1986
1987 #if defined(FEATURE_STRUCTALIGN) && defined(RESPECT_LARGE_ALIGNMENT)
1988 // FEATURE_STRUCTALIGN allows the compiler to dictate the alignment,
1989 // i.e, if a larger alignment matters or is beneficial, the compiler
1990 // generated info tells us so.  RESPECT_LARGE_ALIGNMENT is just the
1991 // converse - it's a heuristic for the GC to use a larger alignment.
1992 #error FEATURE_STRUCTALIGN should imply !RESPECT_LARGE_ALIGNMENT
1993 #endif
1994
1995 #if defined(FEATURE_STRUCTALIGN) && defined(FEATURE_LOH_COMPACTION)
1996 #error FEATURE_STRUCTALIGN and FEATURE_LOH_COMPACTION are mutually exclusive
1997 #endif
1998
1999 #if defined(GROWABLE_SEG_MAPPING_TABLE) && !defined(SEG_MAPPING_TABLE)
2000 #error if GROWABLE_SEG_MAPPING_TABLE is defined, SEG_MAPPING_TABLE must be defined
2001 #endif
2002
2003 inline
2004 BOOL same_large_alignment_p (uint8_t* p1, uint8_t* p2)
2005 {
2006 #ifdef RESPECT_LARGE_ALIGNMENT
2007     return ((((size_t)p1 ^ (size_t)p2) & 7) == 0);
2008 #else
2009     UNREFERENCED_PARAMETER(p1);
2010     UNREFERENCED_PARAMETER(p2);
2011     return TRUE;
2012 #endif //RESPECT_LARGE_ALIGNMENT
2013 }
2014
2015 inline 
2016 size_t switch_alignment_size (BOOL already_padded_p)
2017 {
2018     if (already_padded_p)
2019         return DATA_ALIGNMENT;
2020     else
2021         return (Align (min_obj_size) +((Align (min_obj_size)&DATA_ALIGNMENT)^DATA_ALIGNMENT));
2022 }
2023
2024
2025 #ifdef FEATURE_STRUCTALIGN
2026 void set_node_aligninfo (uint8_t *node, int requiredAlignment, ptrdiff_t pad);
2027 void clear_node_aligninfo (uint8_t *node);
2028 #else // FEATURE_STRUCTALIGN
2029 #define node_realigned(node)    (((plug_and_reloc*)(node))[-1].reloc & 1)
2030 void set_node_realigned (uint8_t* node);
2031 void clear_node_realigned(uint8_t* node);
2032 #endif // FEATURE_STRUCTALIGN
2033
2034 inline
2035 size_t AlignQword (size_t nbytes)
2036 {
2037 #ifdef FEATURE_STRUCTALIGN
2038     // This function is used to align everything on the large object
2039     // heap to an 8-byte boundary, to reduce the number of unaligned
2040     // accesses to (say) arrays of doubles.  With FEATURE_STRUCTALIGN,
2041     // the compiler dictates the optimal alignment instead of having
2042     // a heuristic in the GC.
2043     return Align (nbytes);
2044 #else // FEATURE_STRUCTALIGN
2045     return (nbytes + 7) & ~7;
2046 #endif // FEATURE_STRUCTALIGN
2047 }
2048
2049 inline
2050 BOOL Aligned (size_t n)
2051 {
2052     return (n & ALIGNCONST) == 0;
2053 }
2054
2055 #define OBJECT_ALIGNMENT_OFFSET (sizeof(MethodTable *))
2056
2057 #ifdef FEATURE_STRUCTALIGN
2058 #define MAX_STRUCTALIGN OS_PAGE_SIZE
2059 #else // FEATURE_STRUCTALIGN
2060 #define MAX_STRUCTALIGN 0
2061 #endif // FEATURE_STRUCTALIGN
2062
2063 #ifdef FEATURE_STRUCTALIGN
2064 inline
2065 ptrdiff_t AdjustmentForMinPadSize(ptrdiff_t pad, int requiredAlignment)
2066 {
2067     // The resulting alignpad must be either 0 or at least min_obj_size.
2068     // Note that by computing the following difference on unsigned types,
2069     // we can do the range check 0 < alignpad < min_obj_size with a
2070     // single conditional branch.
2071     if ((size_t)(pad - DATA_ALIGNMENT) < Align (min_obj_size) - DATA_ALIGNMENT)
2072     {
2073         return requiredAlignment;
2074     }
2075     return 0;
2076 }
2077
2078 inline
2079 uint8_t* StructAlign (uint8_t* origPtr, int requiredAlignment, ptrdiff_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
2080 {
2081     // required alignment must be a power of two
2082     _ASSERTE(((size_t)origPtr & ALIGNCONST) == 0);
2083     _ASSERTE(((requiredAlignment - 1) & requiredAlignment) == 0);
2084     _ASSERTE(requiredAlignment >= sizeof(void *));
2085     _ASSERTE(requiredAlignment <= MAX_STRUCTALIGN);
2086
2087     // When this method is invoked for individual objects (i.e., alignmentOffset
2088     // is just the size of the PostHeader), what needs to be aligned when
2089     // we're done is the pointer to the payload of the object (which means
2090     // the actual resulting object pointer is typically not aligned).
2091
2092     uint8_t* result = (uint8_t*)Align ((size_t)origPtr + alignmentOffset, requiredAlignment-1) - alignmentOffset;
2093     ptrdiff_t alignpad = result - origPtr;
2094
2095     return result + AdjustmentForMinPadSize (alignpad, requiredAlignment);
2096 }
2097
2098 inline
2099 ptrdiff_t ComputeStructAlignPad (uint8_t* plug, int requiredAlignment, size_t alignmentOffset=OBJECT_ALIGNMENT_OFFSET)
2100 {
2101     return StructAlign (plug, requiredAlignment, alignmentOffset) - plug;
2102 }
2103
2104 BOOL IsStructAligned (uint8_t *ptr, int requiredAlignment)
2105 {
2106     return StructAlign (ptr, requiredAlignment) == ptr;
2107 }
2108
2109 inline
2110 ptrdiff_t ComputeMaxStructAlignPad (int requiredAlignment)
2111 {
2112     if (requiredAlignment == DATA_ALIGNMENT)
2113         return 0;
2114     // Since a non-zero alignment padding cannot be less than min_obj_size (so we can fit the
2115     // alignment padding object), the worst-case alignment padding is correspondingly larger
2116     // than the required alignment.
2117     return requiredAlignment + Align (min_obj_size) - DATA_ALIGNMENT;
2118 }
2119
2120 inline
2121 ptrdiff_t ComputeMaxStructAlignPadLarge (int requiredAlignment)
2122 {
2123     if (requiredAlignment <= get_alignment_constant (TRUE)+1)
2124         return 0;
2125     // This is the same as ComputeMaxStructAlignPad, except that in addition to leaving space
2126     // for padding before the actual object, it also leaves space for filling a gap after the
2127     // actual object.  This is needed on the large object heap, as the outer allocation functions
2128     // don't operate on an allocation context (which would have left space for the final gap).
2129     return requiredAlignment + Align (min_obj_size) * 2 - DATA_ALIGNMENT;
2130 }
2131
2132 uint8_t* gc_heap::pad_for_alignment (uint8_t* newAlloc, int requiredAlignment, size_t size, alloc_context* acontext)
2133 {
2134     uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment);
2135     if (alignedPtr != newAlloc) {
2136         make_unused_array (newAlloc, alignedPtr - newAlloc);
2137     }
2138     acontext->alloc_ptr = alignedPtr + Align (size);
2139     return alignedPtr;
2140 }
2141
2142 uint8_t* gc_heap::pad_for_alignment_large (uint8_t* newAlloc, int requiredAlignment, size_t size)
2143 {
2144     uint8_t* alignedPtr = StructAlign (newAlloc, requiredAlignment);
2145     if (alignedPtr != newAlloc) {
2146         make_unused_array (newAlloc, alignedPtr - newAlloc);
2147     }
2148     if (alignedPtr < newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment)) {
2149         make_unused_array (alignedPtr + AlignQword (size), newAlloc + ComputeMaxStructAlignPadLarge (requiredAlignment) - alignedPtr);
2150     }
2151     return alignedPtr;
2152 }
2153 #else // FEATURE_STRUCTALIGN
2154 #define ComputeMaxStructAlignPad(requiredAlignment) 0
2155 #define ComputeMaxStructAlignPadLarge(requiredAlignment) 0
2156 #endif // FEATURE_STRUCTALIGN
2157
2158 //CLR_SIZE  is the max amount of bytes from gen0 that is set to 0 in one chunk
2159 #ifdef SERVER_GC
2160 #define CLR_SIZE ((size_t)(8*1024))
2161 #else //SERVER_GC
2162 #define CLR_SIZE ((size_t)(8*1024))
2163 #endif //SERVER_GC
2164
2165 #define END_SPACE_AFTER_GC (loh_size_threshold + MAX_STRUCTALIGN)
2166
2167 #ifdef BACKGROUND_GC
2168 #define SEGMENT_INITIAL_COMMIT (2*OS_PAGE_SIZE)
2169 #else
2170 #define SEGMENT_INITIAL_COMMIT (OS_PAGE_SIZE)
2171 #endif //BACKGROUND_GC
2172
2173 // This is always power of 2.
2174 const size_t min_segment_size_hard_limit = 1024*1024*16;
2175
2176 #ifdef SERVER_GC
2177
2178 #ifdef BIT64
2179
2180 #define INITIAL_ALLOC ((size_t)((size_t)4*1024*1024*1024))
2181 #define LHEAP_ALLOC   ((size_t)(1024*1024*256))
2182
2183 #else
2184
2185 #define INITIAL_ALLOC ((size_t)(1024*1024*64))
2186 #define LHEAP_ALLOC   ((size_t)(1024*1024*32))
2187
2188 #endif  // BIT64
2189
2190 #else //SERVER_GC
2191
2192 #ifdef BIT64
2193
2194 #define INITIAL_ALLOC ((size_t)(1024*1024*256))
2195 #define LHEAP_ALLOC   ((size_t)(1024*1024*128))
2196
2197 #else
2198
2199 #define INITIAL_ALLOC ((size_t)(1024*1024*16))
2200 #define LHEAP_ALLOC   ((size_t)(1024*1024*16))
2201
2202 #endif  // BIT64
2203
2204 #endif //SERVER_GC
2205
2206 //amount in bytes of the etw allocation tick
2207 const size_t etw_allocation_tick = 100*1024;
2208
2209 const size_t low_latency_alloc = 256*1024;
2210
2211 const size_t fgn_check_quantum = 2*1024*1024;
2212
2213 #ifdef MH_SC_MARK
2214 const int max_snoop_level = 128;
2215 #endif //MH_SC_MARK
2216
2217
2218 #ifdef CARD_BUNDLE
2219 //threshold of heap size to turn on card bundles.
2220 #define SH_TH_CARD_BUNDLE  (40*1024*1024)
2221 #define MH_TH_CARD_BUNDLE  (180*1024*1024)
2222 #endif //CARD_BUNDLE
2223
2224 #define GC_EPHEMERAL_DECOMMIT_TIMEOUT 5000
2225
2226 inline
2227 size_t align_on_page (size_t add)
2228 {
2229     return ((add + OS_PAGE_SIZE - 1) & ~((size_t)OS_PAGE_SIZE - 1));
2230 }
2231
2232 inline
2233 uint8_t* align_on_page (uint8_t* add)
2234 {
2235     return (uint8_t*)align_on_page ((size_t) add);
2236 }
2237
2238 inline
2239 size_t align_lower_page (size_t add)
2240 {
2241     return (add & ~((size_t)OS_PAGE_SIZE - 1));
2242 }
2243
2244 inline
2245 uint8_t* align_lower_page (uint8_t* add)
2246 {
2247     return (uint8_t*)align_lower_page ((size_t)add);
2248 }
2249
2250 inline
2251 size_t align_write_watch_lower_page (size_t add)
2252 {
2253     return (add & ~(WRITE_WATCH_UNIT_SIZE - 1));
2254 }
2255
2256 inline
2257 uint8_t* align_write_watch_lower_page (uint8_t* add)
2258 {
2259     return (uint8_t*)align_lower_page ((size_t)add);
2260 }
2261
2262
2263 inline
2264 BOOL power_of_two_p (size_t integer)
2265 {
2266     return !(integer & (integer-1));
2267 }
2268
2269 inline
2270 BOOL oddp (size_t integer)
2271 {
2272     return (integer & 1) != 0;
2273 }
2274
2275 // we only ever use this for WORDs.
2276 size_t logcount (size_t word)
2277 {
2278     //counts the number of high bits in a 16 bit word.
2279     assert (word < 0x10000);
2280     size_t count;
2281     count = (word & 0x5555) + ( (word >> 1 ) & 0x5555);
2282     count = (count & 0x3333) + ( (count >> 2) & 0x3333);
2283     count = (count & 0x0F0F) + ( (count >> 4) & 0x0F0F);
2284     count = (count & 0x00FF) + ( (count >> 8) & 0x00FF);
2285     return count;
2286 }
2287
2288 #ifndef DACCESS_COMPILE
2289
2290 void stomp_write_barrier_resize(bool is_runtime_suspended, bool requires_upper_bounds_check)
2291 {
2292     WriteBarrierParameters args = {};
2293     args.operation = WriteBarrierOp::StompResize;
2294     args.is_runtime_suspended = is_runtime_suspended;
2295     args.requires_upper_bounds_check = requires_upper_bounds_check;
2296
2297     args.card_table = g_gc_card_table;
2298 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
2299     args.card_bundle_table = g_gc_card_bundle_table;
2300 #endif
2301
2302     args.lowest_address = g_gc_lowest_address;
2303     args.highest_address = g_gc_highest_address;
2304
2305 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
2306     if (SoftwareWriteWatch::IsEnabledForGCHeap())
2307     {
2308         args.write_watch_table = g_gc_sw_ww_table;
2309     }
2310 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
2311
2312     GCToEEInterface::StompWriteBarrier(&args);
2313 }
2314
2315 void stomp_write_barrier_ephemeral(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
2316 {
2317     WriteBarrierParameters args = {};
2318     args.operation = WriteBarrierOp::StompEphemeral;
2319     args.is_runtime_suspended = true;
2320     args.ephemeral_low = ephemeral_low;
2321     args.ephemeral_high = ephemeral_high;
2322     GCToEEInterface::StompWriteBarrier(&args);
2323 }
2324
2325 void stomp_write_barrier_initialize(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
2326 {
2327     WriteBarrierParameters args = {};
2328     args.operation = WriteBarrierOp::Initialize;
2329     args.is_runtime_suspended = true;
2330     args.requires_upper_bounds_check = false;
2331     args.card_table = g_gc_card_table;
2332
2333 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
2334     args.card_bundle_table = g_gc_card_bundle_table;
2335 #endif
2336     
2337     args.lowest_address = g_gc_lowest_address;
2338     args.highest_address = g_gc_highest_address;
2339     args.ephemeral_low = ephemeral_low;
2340     args.ephemeral_high = ephemeral_high;
2341     GCToEEInterface::StompWriteBarrier(&args);
2342 }
2343
2344 #endif // DACCESS_COMPILE
2345
2346 //extract the low bits [0,low[ of a uint32_t
2347 #define lowbits(wrd, bits) ((wrd) & ((1 << (bits))-1))
2348 //extract the high bits [high, 32] of a uint32_t
2349 #define highbits(wrd, bits) ((wrd) & ~((1 << (bits))-1))
2350
2351 // Things we need to manually initialize:
2352 // gen0 min_size - based on cache
2353 // gen0/1 max_size - based on segment size
2354 static static_data static_data_table[latency_level_last - latency_level_first + 1][NUMBERGENERATIONS] = 
2355 {
2356     // latency_level_memory_footprint
2357     {
2358         // gen0
2359         {0, 0, 40000, 0.5f, 9.0f, 20.0f, 1000, 1},
2360         // gen1
2361         {160*1024, 0, 80000, 0.5f, 2.0f, 7.0f, 10000, 10},
2362         // gen2
2363         {256*1024, SSIZE_T_MAX, 200000, 0.25f, 1.2f, 1.8f, 100000, 100},
2364         // gen3
2365         {3*1024*1024, SSIZE_T_MAX, 0, 0.0f, 1.25f, 4.5f, 0, 0}
2366     },
2367
2368     // latency_level_balanced
2369     {
2370         // gen0
2371         {0, 0, 40000, 0.5f,
2372 #ifdef MULTIPLE_HEAPS
2373             20.0f, 40.0f,
2374 #else
2375             9.0f, 20.0f,
2376 #endif //MULTIPLE_HEAPS
2377             1000, 1},
2378         // gen1
2379         {256*1024, 0, 80000, 0.5f, 2.0f, 7.0f, 10000, 10},
2380         // gen2
2381         {256*1024, SSIZE_T_MAX, 200000, 0.25f, 1.2f, 1.8f, 100000, 100},
2382         // gen3
2383         {3*1024*1024, SSIZE_T_MAX, 0, 0.0f, 1.25f, 4.5f, 0, 0}
2384     },
2385 };
2386
2387 class mark;
2388 class generation;
2389 class heap_segment;
2390 class CObjectHeader;
2391 class dynamic_data;
2392 class l_heap;
2393 class sorted_table;
2394 class c_synchronize;
2395
2396 #ifdef FEATURE_PREMORTEM_FINALIZATION
2397 #ifndef DACCESS_COMPILE
2398 static
2399 HRESULT AllocateCFinalize(CFinalize **pCFinalize);
2400 #endif //!DACCESS_COMPILE
2401 #endif // FEATURE_PREMORTEM_FINALIZATION
2402
2403 uint8_t* tree_search (uint8_t* tree, uint8_t* old_address);
2404
2405
2406 #ifdef USE_INTROSORT
2407 #define _sort introsort::sort
2408 #else //USE_INTROSORT
2409 #define _sort qsort1
2410 void qsort1(uint8_t** low, uint8_t** high, unsigned int depth);
2411 #endif //USE_INTROSORT
2412
2413 void* virtual_alloc (size_t size);
2414 void virtual_free (void* add, size_t size);
2415
2416 /* per heap static initialization */
2417 #ifdef MARK_ARRAY
2418 #ifndef MULTIPLE_HEAPS
2419 uint32_t*   gc_heap::mark_array;
2420 #endif //MULTIPLE_HEAPS
2421 #endif //MARK_ARRAY
2422
2423 #ifdef MARK_LIST
2424 uint8_t**   gc_heap::g_mark_list;
2425
2426 #ifdef PARALLEL_MARK_LIST_SORT
2427 uint8_t**   gc_heap::g_mark_list_copy;
2428 #endif //PARALLEL_MARK_LIST_SORT
2429
2430 size_t      gc_heap::mark_list_size;
2431 #endif //MARK_LIST
2432
2433 #ifdef SEG_MAPPING_TABLE
2434 seg_mapping* seg_mapping_table;
2435 #endif //SEG_MAPPING_TABLE
2436
2437 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
2438 sorted_table* gc_heap::seg_table;
2439 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
2440
2441 #ifdef MULTIPLE_HEAPS
2442 GCEvent     gc_heap::ee_suspend_event;
2443 size_t      gc_heap::min_balance_threshold = 0;
2444 #endif //MULTIPLE_HEAPS
2445
2446 VOLATILE(BOOL) gc_heap::gc_started;
2447
2448 #ifdef MULTIPLE_HEAPS
2449
2450 GCEvent     gc_heap::gc_start_event;
2451 bool        gc_heap::gc_thread_no_affinitize_p = false;
2452 uintptr_t   process_mask = 0;
2453
2454 int         gc_heap::n_heaps;
2455
2456 gc_heap**   gc_heap::g_heaps;
2457
2458 size_t*     gc_heap::g_promoted;
2459
2460 #ifdef MH_SC_MARK
2461 int*        gc_heap::g_mark_stack_busy;
2462 #endif //MH_SC_MARK
2463
2464
2465 #ifdef BACKGROUND_GC
2466 size_t*     gc_heap::g_bpromoted;
2467 #endif //BACKGROUND_GC
2468
2469 #else  //MULTIPLE_HEAPS
2470
2471 size_t      gc_heap::g_promoted;
2472
2473 #ifdef BACKGROUND_GC
2474 size_t      gc_heap::g_bpromoted;
2475 #endif //BACKGROUND_GC
2476
2477 #endif //MULTIPLE_HEAPS
2478
2479 size_t      gc_heap::reserved_memory = 0;
2480 size_t      gc_heap::reserved_memory_limit = 0;
2481 BOOL        gc_heap::g_low_memory_status;
2482
2483 #ifndef DACCESS_COMPILE
2484 static gc_reason gc_trigger_reason = reason_empty;
2485 #endif //DACCESS_COMPILE
2486
2487 gc_latency_level gc_heap::latency_level = latency_level_default;
2488
2489 gc_mechanisms  gc_heap::settings;
2490
2491 gc_history_global gc_heap::gc_data_global;
2492
2493 size_t      gc_heap::gc_last_ephemeral_decommit_time = 0;
2494
2495 size_t      gc_heap::gc_gen0_desired_high;
2496
2497 CLRCriticalSection gc_heap::check_commit_cs;
2498
2499 size_t      gc_heap::current_total_committed = 0;
2500
2501 size_t      gc_heap::current_total_committed_bookkeeping = 0;
2502
2503 #ifdef SHORT_PLUGS
2504 double       gc_heap::short_plugs_pad_ratio = 0;
2505 #endif //SHORT_PLUGS
2506
2507 #if defined(BIT64)
2508 #define MAX_ALLOWED_MEM_LOAD 85
2509
2510 // consider putting this in dynamic data -
2511 // we may want different values for workstation
2512 // and server GC.
2513 #define MIN_YOUNGEST_GEN_DESIRED (16*1024*1024)
2514
2515 size_t      gc_heap::youngest_gen_desired_th;
2516 #endif //BIT64
2517
2518 uint32_t    gc_heap::last_gc_memory_load = 0;
2519
2520 size_t      gc_heap::last_gc_heap_size = 0;
2521
2522 size_t      gc_heap::last_gc_fragmentation = 0;
2523
2524 uint64_t    gc_heap::mem_one_percent = 0;
2525
2526 uint32_t    gc_heap::high_memory_load_th = 0;
2527
2528 uint32_t    gc_heap::m_high_memory_load_th;
2529
2530 uint32_t    gc_heap::v_high_memory_load_th;
2531
2532 uint64_t    gc_heap::total_physical_mem = 0;
2533
2534 uint64_t    gc_heap::entry_available_physical_mem = 0;
2535
2536 size_t      gc_heap::heap_hard_limit = 0;
2537
2538 #ifdef BACKGROUND_GC
2539 GCEvent     gc_heap::bgc_start_event;
2540
2541 gc_mechanisms gc_heap::saved_bgc_settings;
2542
2543 GCEvent     gc_heap::background_gc_done_event;
2544
2545 GCEvent     gc_heap::ee_proceed_event;
2546
2547 bool        gc_heap::gc_can_use_concurrent = false;
2548
2549 bool        gc_heap::temp_disable_concurrent_p = false;
2550
2551 uint32_t    gc_heap::cm_in_progress = FALSE;
2552
2553 BOOL        gc_heap::dont_restart_ee_p = FALSE;
2554
2555 BOOL        gc_heap::keep_bgc_threads_p = FALSE;
2556
2557 GCEvent     gc_heap::bgc_threads_sync_event;
2558
2559 BOOL        gc_heap::do_ephemeral_gc_p = FALSE;
2560
2561 BOOL        gc_heap::do_concurrent_p = FALSE;
2562
2563 size_t      gc_heap::ephemeral_fgc_counts[max_generation];
2564
2565 BOOL        gc_heap::alloc_wait_event_p = FALSE;
2566
2567 VOLATILE(c_gc_state) gc_heap::current_c_gc_state = c_gc_state_free;
2568
2569 #endif //BACKGROUND_GC
2570
2571 #ifndef MULTIPLE_HEAPS
2572 #ifdef SPINLOCK_HISTORY
2573 int         gc_heap::spinlock_info_index = 0;
2574 spinlock_info gc_heap::last_spinlock_info[max_saved_spinlock_info + 8];
2575 #endif //SPINLOCK_HISTORY
2576
2577 size_t      gc_heap::fgn_last_alloc = 0;
2578
2579 int         gc_heap::generation_skip_ratio = 100;
2580
2581 uint64_t    gc_heap::loh_alloc_since_cg = 0;
2582
2583 BOOL        gc_heap::elevation_requested = FALSE;
2584
2585 BOOL        gc_heap::last_gc_before_oom = FALSE;
2586
2587 BOOL        gc_heap::sufficient_gen0_space_p = FALSE;
2588
2589 #ifdef BACKGROUND_GC
2590 uint8_t*    gc_heap::background_saved_lowest_address = 0;
2591 uint8_t*    gc_heap::background_saved_highest_address = 0;
2592 uint8_t*    gc_heap::next_sweep_obj = 0;
2593 uint8_t*    gc_heap::current_sweep_pos = 0;
2594 exclusive_sync* gc_heap::bgc_alloc_lock;
2595 #endif //BACKGROUND_GC
2596
2597 oom_history gc_heap::oom_info;
2598
2599 fgm_history gc_heap::fgm_result;
2600
2601 size_t      gc_heap::allocated_since_last_gc = 0;
2602
2603 BOOL        gc_heap::ro_segments_in_range;
2604
2605 size_t      gc_heap::gen0_big_free_spaces = 0;
2606
2607 uint8_t*    gc_heap::ephemeral_low;
2608
2609 uint8_t*    gc_heap::ephemeral_high;
2610
2611 uint8_t*    gc_heap::lowest_address;
2612
2613 uint8_t*    gc_heap::highest_address;
2614
2615 BOOL        gc_heap::ephemeral_promotion;
2616
2617 uint8_t*    gc_heap::saved_ephemeral_plan_start[NUMBERGENERATIONS-1];
2618 size_t      gc_heap::saved_ephemeral_plan_start_size[NUMBERGENERATIONS-1];
2619
2620 short*      gc_heap::brick_table;
2621
2622 uint32_t*   gc_heap::card_table;
2623
2624 #ifdef CARD_BUNDLE
2625 uint32_t*   gc_heap::card_bundle_table;
2626 #endif //CARD_BUNDLE
2627
2628 uint8_t*    gc_heap::gc_low;
2629
2630 uint8_t*    gc_heap::gc_high;
2631
2632 uint8_t*    gc_heap::demotion_low;
2633
2634 uint8_t*    gc_heap::demotion_high;
2635
2636 BOOL        gc_heap::demote_gen1_p = TRUE;
2637
2638 uint8_t*    gc_heap::last_gen1_pin_end;
2639
2640 gen_to_condemn_tuning gc_heap::gen_to_condemn_reasons;
2641
2642 size_t      gc_heap::etw_allocation_running_amount[2];
2643
2644 int         gc_heap::gc_policy = 0;
2645
2646 size_t      gc_heap::allocation_running_time;
2647
2648 size_t      gc_heap::allocation_running_amount;
2649
2650 heap_segment* gc_heap::ephemeral_heap_segment = 0;
2651
2652 BOOL        gc_heap::blocking_collection = FALSE;
2653
2654 heap_segment* gc_heap::freeable_large_heap_segment = 0;
2655
2656 size_t      gc_heap::time_bgc_last = 0;
2657
2658 size_t      gc_heap::mark_stack_tos = 0;
2659
2660 size_t      gc_heap::mark_stack_bos = 0;
2661
2662 size_t      gc_heap::mark_stack_array_length = 0;
2663
2664 mark*       gc_heap::mark_stack_array = 0;
2665
2666 #if defined (_DEBUG) && defined (VERIFY_HEAP)
2667 BOOL        gc_heap::verify_pinned_queue_p = FALSE;
2668 #endif // defined (_DEBUG) && defined (VERIFY_HEAP)
2669
2670 uint8_t*    gc_heap::oldest_pinned_plug = 0;
2671
2672 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
2673 size_t      gc_heap::num_pinned_objects = 0;
2674 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
2675
2676 #ifdef FEATURE_LOH_COMPACTION
2677 size_t      gc_heap::loh_pinned_queue_tos = 0;
2678
2679 size_t      gc_heap::loh_pinned_queue_bos = 0;
2680
2681 size_t      gc_heap::loh_pinned_queue_length = 0;
2682
2683 mark*       gc_heap::loh_pinned_queue = 0;
2684
2685 BOOL        gc_heap::loh_compacted_p = FALSE;
2686 #endif //FEATURE_LOH_COMPACTION
2687
2688 #ifdef BACKGROUND_GC
2689
2690 EEThreadId  gc_heap::bgc_thread_id;
2691
2692 uint8_t*    gc_heap::background_written_addresses [array_size+2];
2693
2694 heap_segment* gc_heap::freeable_small_heap_segment = 0;
2695
2696 size_t      gc_heap::bgc_overflow_count = 0;
2697
2698 size_t      gc_heap::bgc_begin_loh_size = 0;
2699 size_t      gc_heap::end_loh_size = 0;
2700
2701 uint32_t    gc_heap::bgc_alloc_spin_loh = 0;
2702
2703 size_t      gc_heap::bgc_loh_size_increased = 0;
2704
2705 size_t      gc_heap::bgc_loh_allocated_in_free = 0;
2706
2707 size_t      gc_heap::background_soh_alloc_count = 0;
2708
2709 size_t      gc_heap::background_loh_alloc_count = 0;
2710
2711 uint8_t**   gc_heap::background_mark_stack_tos = 0;
2712
2713 uint8_t**   gc_heap::background_mark_stack_array = 0;
2714
2715 size_t      gc_heap::background_mark_stack_array_length = 0;
2716
2717 uint8_t*    gc_heap::background_min_overflow_address =0;
2718
2719 uint8_t*    gc_heap::background_max_overflow_address =0;
2720
2721 BOOL        gc_heap::processed_soh_overflow_p = FALSE;
2722
2723 uint8_t*    gc_heap::background_min_soh_overflow_address =0;
2724
2725 uint8_t*    gc_heap::background_max_soh_overflow_address =0;
2726
2727 heap_segment* gc_heap::saved_sweep_ephemeral_seg = 0;
2728
2729 uint8_t*    gc_heap::saved_sweep_ephemeral_start = 0;
2730
2731 heap_segment* gc_heap::saved_overflow_ephemeral_seg = 0;
2732
2733 Thread*     gc_heap::bgc_thread = 0;
2734
2735 BOOL        gc_heap::expanded_in_fgc = FALSE;
2736
2737 uint8_t**   gc_heap::c_mark_list = 0;
2738
2739 size_t      gc_heap::c_mark_list_length = 0;
2740
2741 size_t      gc_heap::c_mark_list_index = 0;
2742
2743 gc_history_per_heap gc_heap::bgc_data_per_heap;
2744
2745 BOOL    gc_heap::bgc_thread_running;
2746
2747 CLRCriticalSection gc_heap::bgc_threads_timeout_cs;
2748
2749 GCEvent gc_heap::gc_lh_block_event;
2750
2751 #endif //BACKGROUND_GC
2752
2753 #ifdef MARK_LIST
2754 uint8_t**   gc_heap::mark_list;
2755 uint8_t**   gc_heap::mark_list_index;
2756 uint8_t**   gc_heap::mark_list_end;
2757 #endif //MARK_LIST
2758
2759 #ifdef SNOOP_STATS
2760 snoop_stats_data gc_heap::snoop_stat;
2761 #endif //SNOOP_STATS
2762
2763 uint8_t*    gc_heap::min_overflow_address = MAX_PTR;
2764
2765 uint8_t*    gc_heap::max_overflow_address = 0;
2766
2767 uint8_t*    gc_heap::shigh = 0;
2768
2769 uint8_t*    gc_heap::slow = MAX_PTR;
2770
2771 size_t      gc_heap::ordered_free_space_indices[MAX_NUM_BUCKETS];
2772
2773 size_t      gc_heap::saved_ordered_free_space_indices[MAX_NUM_BUCKETS];
2774
2775 size_t      gc_heap::ordered_plug_indices[MAX_NUM_BUCKETS];
2776
2777 size_t      gc_heap::saved_ordered_plug_indices[MAX_NUM_BUCKETS];
2778
2779 BOOL        gc_heap::ordered_plug_indices_init = FALSE;
2780
2781 BOOL        gc_heap::use_bestfit = FALSE;
2782
2783 uint8_t*    gc_heap::bestfit_first_pin = 0;
2784
2785 BOOL        gc_heap::commit_end_of_seg = FALSE;
2786
2787 size_t      gc_heap::max_free_space_items = 0;
2788
2789 size_t      gc_heap::free_space_buckets = 0;
2790
2791 size_t      gc_heap::free_space_items = 0;
2792
2793 int         gc_heap::trimmed_free_space_index = 0;
2794
2795 size_t      gc_heap::total_ephemeral_plugs = 0;
2796
2797 seg_free_spaces* gc_heap::bestfit_seg = 0;
2798
2799 size_t      gc_heap::total_ephemeral_size = 0;
2800
2801 #ifdef HEAP_ANALYZE
2802
2803 size_t      gc_heap::internal_root_array_length = initial_internal_roots;
2804
2805 uint8_t**   gc_heap::internal_root_array = 0;
2806
2807 size_t      gc_heap::internal_root_array_index = 0;
2808
2809 BOOL        gc_heap::heap_analyze_success = TRUE;
2810
2811 uint8_t*    gc_heap::current_obj = 0;
2812 size_t      gc_heap::current_obj_size = 0;
2813
2814 #endif //HEAP_ANALYZE
2815
2816 #ifdef GC_CONFIG_DRIVEN
2817 size_t gc_heap::interesting_data_per_gc[max_idp_count];
2818 //size_t gc_heap::interesting_data_per_heap[max_idp_count];
2819 //size_t gc_heap::interesting_mechanisms_per_heap[max_im_count];
2820 #endif //GC_CONFIG_DRIVEN
2821 #endif //MULTIPLE_HEAPS
2822
2823 no_gc_region_info gc_heap::current_no_gc_region_info;
2824 BOOL gc_heap::proceed_with_gc_p = FALSE;
2825 GCSpinLock gc_heap::gc_lock;
2826
2827 size_t gc_heap::eph_gen_starts_size = 0;
2828 heap_segment* gc_heap::segment_standby_list;
2829 size_t        gc_heap::last_gc_index = 0;
2830 #ifdef SEG_MAPPING_TABLE
2831 size_t        gc_heap::min_segment_size = 0;
2832 size_t        gc_heap::min_segment_size_shr = 0;
2833 #endif //SEG_MAPPING_TABLE
2834 size_t        gc_heap::soh_segment_size = 0;
2835 size_t        gc_heap::min_loh_segment_size = 0;
2836 size_t        gc_heap::segment_info_size = 0;
2837
2838 #ifdef GC_CONFIG_DRIVEN
2839 size_t gc_heap::time_init = 0;
2840 size_t gc_heap::time_since_init = 0;
2841 size_t gc_heap::compact_or_sweep_gcs[2];
2842 #endif //GC_CONFIG_DRIVEN
2843
2844 #ifdef FEATURE_LOH_COMPACTION
2845 BOOL                   gc_heap::loh_compaction_always_p = FALSE;
2846 gc_loh_compaction_mode gc_heap::loh_compaction_mode = loh_compaction_default;
2847 int                    gc_heap::loh_pinned_queue_decay = LOH_PIN_DECAY;
2848
2849 #endif //FEATURE_LOH_COMPACTION
2850
2851 GCEvent gc_heap::full_gc_approach_event;
2852
2853 GCEvent gc_heap::full_gc_end_event;
2854
2855 uint32_t gc_heap::fgn_maxgen_percent = 0;
2856
2857 uint32_t gc_heap::fgn_loh_percent = 0;
2858
2859 #ifdef BACKGROUND_GC
2860 BOOL gc_heap::fgn_last_gc_was_concurrent = FALSE;
2861 #endif //BACKGROUND_GC
2862
2863 VOLATILE(bool) gc_heap::full_gc_approach_event_set;
2864
2865 size_t gc_heap::full_gc_counts[gc_type_max];
2866
2867 bool gc_heap::maxgen_size_inc_p = false;
2868
2869 BOOL gc_heap::should_expand_in_full_gc = FALSE;
2870
2871 // Provisional mode related stuff.
2872 bool gc_heap::provisional_mode_triggered = false;
2873 bool gc_heap::pm_trigger_full_gc = false;
2874 size_t gc_heap::provisional_triggered_gc_count = 0;
2875 size_t gc_heap::provisional_off_gc_count = 0;
2876 size_t gc_heap::num_provisional_triggered = 0;
2877 bool   gc_heap::pm_stress_on = false;
2878
2879 #ifdef HEAP_ANALYZE
2880 BOOL        gc_heap::heap_analyze_enabled = FALSE;
2881 #endif //HEAP_ANALYZE
2882
2883 #ifndef MULTIPLE_HEAPS
2884
2885 alloc_list gc_heap::loh_alloc_list [NUM_LOH_ALIST-1];
2886 alloc_list gc_heap::gen2_alloc_list[NUM_GEN2_ALIST-1];
2887
2888 dynamic_data gc_heap::dynamic_data_table [NUMBERGENERATIONS+1];
2889 gc_history_per_heap gc_heap::gc_data_per_heap;
2890 size_t gc_heap::maxgen_pinned_compact_before_advance = 0;
2891
2892 uint8_t* gc_heap::alloc_allocated = 0;
2893
2894 size_t gc_heap::allocation_quantum = CLR_SIZE;
2895
2896 GCSpinLock gc_heap::more_space_lock_soh;
2897 GCSpinLock gc_heap::more_space_lock_loh;
2898 VOLATILE(int32_t) gc_heap::loh_alloc_thread_count = 0;
2899
2900 #ifdef SYNCHRONIZATION_STATS
2901 unsigned int gc_heap::good_suspension = 0;
2902 unsigned int gc_heap::bad_suspension = 0;
2903 uint64_t     gc_heap::total_msl_acquire = 0;
2904 unsigned int gc_heap::num_msl_acquired = 0;
2905 unsigned int gc_heap::num_high_msl_acquire = 0;
2906 unsigned int gc_heap::num_low_msl_acquire = 0;
2907 #endif //SYNCHRONIZATION_STATS
2908
2909 size_t   gc_heap::alloc_contexts_used = 0;
2910 size_t   gc_heap::soh_allocation_no_gc = 0;
2911 size_t   gc_heap::loh_allocation_no_gc = 0;
2912 bool     gc_heap::no_gc_oom_p = false;
2913 heap_segment* gc_heap::saved_loh_segment_no_gc = 0;
2914
2915 #endif //MULTIPLE_HEAPS
2916
2917 #ifndef MULTIPLE_HEAPS
2918
2919 BOOL        gc_heap::gen0_bricks_cleared = FALSE;
2920
2921 #ifdef FFIND_OBJECT
2922 int         gc_heap::gen0_must_clear_bricks = 0;
2923 #endif //FFIND_OBJECT
2924
2925 #ifdef FEATURE_PREMORTEM_FINALIZATION
2926 CFinalize*  gc_heap::finalize_queue = 0;
2927 #endif // FEATURE_PREMORTEM_FINALIZATION
2928
2929 generation gc_heap::generation_table [NUMBERGENERATIONS + 1];
2930
2931 size_t     gc_heap::interesting_data_per_heap[max_idp_count];
2932
2933 size_t     gc_heap::compact_reasons_per_heap[max_compact_reasons_count];
2934
2935 size_t     gc_heap::expand_mechanisms_per_heap[max_expand_mechanisms_count];
2936
2937 size_t     gc_heap::interesting_mechanism_bits_per_heap[max_gc_mechanism_bits_count];
2938
2939 #endif // MULTIPLE_HEAPS
2940
2941 /* end of per heap static initialization */
2942
2943 /* end of static initialization */
2944
2945 #ifndef DACCESS_COMPILE
2946
2947 void gen_to_condemn_tuning::print (int heap_num)
2948 {
2949 #ifdef DT_LOG
2950     dprintf (DT_LOG_0, ("condemned reasons (%d %d)", condemn_reasons_gen, condemn_reasons_condition));
2951     dprintf (DT_LOG_0, ("%s", record_condemn_reasons_gen_header));
2952     gc_condemn_reason_gen r_gen;
2953     for (int i = 0; i < gcrg_max; i++)
2954     {
2955         r_gen = (gc_condemn_reason_gen)(i);
2956         str_reasons_gen[i * 2] = get_gen_char (get_gen (r_gen));
2957     }
2958     dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_gen));
2959
2960     dprintf (DT_LOG_0, ("%s", record_condemn_reasons_condition_header));
2961     gc_condemn_reason_condition r_condition;
2962     for (int i = 0; i < gcrc_max; i++)
2963     {
2964         r_condition = (gc_condemn_reason_condition)(i);
2965         str_reasons_condition[i * 2] = get_condition_char (get_condition (r_condition));
2966     }
2967
2968     dprintf (DT_LOG_0, ("[%2d]%s", heap_num, str_reasons_condition));
2969 #else
2970     UNREFERENCED_PARAMETER(heap_num);
2971 #endif //DT_LOG
2972 }
2973
2974 void gc_generation_data::print (int heap_num, int gen_num)
2975 {
2976 #if defined(SIMPLE_DPRINTF) && defined(DT_LOG)
2977     dprintf (DT_LOG_0, ("[%2d]gen%d beg %Id fl %Id fo %Id end %Id fl %Id fo %Id in %Id p %Id np %Id alloc %Id",
2978                 heap_num, gen_num, 
2979                 size_before, 
2980                 free_list_space_before, free_obj_space_before,
2981                 size_after, 
2982                 free_list_space_after, free_obj_space_after, 
2983                 in, pinned_surv, npinned_surv,
2984                 new_allocation));
2985 #else
2986     UNREFERENCED_PARAMETER(heap_num);
2987     UNREFERENCED_PARAMETER(gen_num);
2988 #endif //SIMPLE_DPRINTF && DT_LOG
2989 }
2990
2991 void gc_history_per_heap::set_mechanism (gc_mechanism_per_heap mechanism_per_heap, uint32_t value)
2992 {
2993     uint32_t* mechanism = &mechanisms[mechanism_per_heap];
2994     *mechanism = 0;
2995     *mechanism |= mechanism_mask;
2996     *mechanism |= (1 << value);
2997
2998 #ifdef DT_LOG
2999     gc_mechanism_descr* descr = &gc_mechanisms_descr[mechanism_per_heap];
3000     dprintf (DT_LOG_0, ("setting %s: %s", 
3001             descr->name,
3002             (descr->descr)[value]));
3003 #endif //DT_LOG
3004 }
3005
3006 void gc_history_per_heap::print()
3007 {
3008 #if defined(SIMPLE_DPRINTF) && defined(DT_LOG)
3009     for (int i = 0; i < (sizeof (gen_data)/sizeof (gc_generation_data)); i++)
3010     {
3011         gen_data[i].print (heap_index, i);
3012     }
3013
3014     dprintf (DT_LOG_0, ("fla %Id flr %Id esa %Id ca %Id pa %Id paa %Id, rfle %d, ec %Id", 
3015                     maxgen_size_info.free_list_allocated,
3016                     maxgen_size_info.free_list_rejected,
3017                     maxgen_size_info.end_seg_allocated,
3018                     maxgen_size_info.condemned_allocated,
3019                     maxgen_size_info.pinned_allocated,
3020                     maxgen_size_info.pinned_allocated_advance,
3021                     maxgen_size_info.running_free_list_efficiency,
3022                     extra_gen0_committed));
3023
3024     int mechanism = 0;
3025     gc_mechanism_descr* descr = 0;
3026
3027     for (int i = 0; i < max_mechanism_per_heap; i++)
3028     {
3029         mechanism = get_mechanism ((gc_mechanism_per_heap)i);
3030
3031         if (mechanism >= 0)
3032         {
3033             descr = &gc_mechanisms_descr[(gc_mechanism_per_heap)i];
3034             dprintf (DT_LOG_0, ("[%2d]%s%s", 
3035                         heap_index,
3036                         descr->name, 
3037                         (descr->descr)[mechanism]));
3038         }
3039     }
3040 #endif //SIMPLE_DPRINTF && DT_LOG
3041 }
3042
3043 void gc_history_global::print()
3044 {
3045 #ifdef DT_LOG
3046     char str_settings[64];
3047     memset (str_settings, '|', sizeof (char) * 64);
3048     str_settings[max_global_mechanisms_count*2] = 0;
3049
3050     for (int i = 0; i < max_global_mechanisms_count; i++)
3051     {
3052         str_settings[i * 2] = (get_mechanism_p ((gc_global_mechanism_p)i) ? 'Y' : 'N');
3053     }
3054
3055     dprintf (DT_LOG_0, ("[hp]|c|p|o|d|b|e|"));
3056
3057     dprintf (DT_LOG_0, ("%4d|%s", num_heaps, str_settings));
3058     dprintf (DT_LOG_0, ("Condemned gen%d(reason: %s; mode: %s), youngest budget %Id(%d), memload %d",
3059                         condemned_generation,
3060                         str_gc_reasons[reason],
3061                         str_gc_pause_modes[pause_mode],                        
3062                         final_youngest_desired,
3063                         gen0_reduction_count,
3064                         mem_pressure));
3065 #endif //DT_LOG
3066 }
3067
3068 void gc_heap::fire_per_heap_hist_event (gc_history_per_heap* current_gc_data_per_heap, int heap_num)
3069 {
3070     maxgen_size_increase* maxgen_size_info = &(current_gc_data_per_heap->maxgen_size_info);
3071     FIRE_EVENT(GCPerHeapHistory_V3, 
3072                (void *)(maxgen_size_info->free_list_allocated),
3073                (void *)(maxgen_size_info->free_list_rejected),                              
3074                (void *)(maxgen_size_info->end_seg_allocated),
3075                (void *)(maxgen_size_info->condemned_allocated),
3076                (void *)(maxgen_size_info->pinned_allocated),
3077                (void *)(maxgen_size_info->pinned_allocated_advance),
3078                maxgen_size_info->running_free_list_efficiency,
3079                current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons0(),
3080                current_gc_data_per_heap->gen_to_condemn_reasons.get_reasons1(),
3081                current_gc_data_per_heap->mechanisms[gc_heap_compact],
3082                current_gc_data_per_heap->mechanisms[gc_heap_expand],
3083                current_gc_data_per_heap->heap_index,
3084                (void *)(current_gc_data_per_heap->extra_gen0_committed),
3085                (max_generation + 2),
3086                (uint32_t)(sizeof (gc_generation_data)),
3087                (void *)&(current_gc_data_per_heap->gen_data[0]));
3088
3089     current_gc_data_per_heap->print();
3090     current_gc_data_per_heap->gen_to_condemn_reasons.print (heap_num);
3091 }
3092
3093 void gc_heap::fire_pevents()
3094 {
3095     settings.record (&gc_data_global);
3096     gc_data_global.print();
3097
3098     FIRE_EVENT(GCGlobalHeapHistory_V2, 
3099                gc_data_global.final_youngest_desired, 
3100                gc_data_global.num_heaps, 
3101                gc_data_global.condemned_generation, 
3102                gc_data_global.gen0_reduction_count, 
3103                gc_data_global.reason, 
3104                gc_data_global.global_mechanims_p, 
3105                gc_data_global.pause_mode, 
3106                gc_data_global.mem_pressure);
3107
3108 #ifdef MULTIPLE_HEAPS
3109     for (int i = 0; i < gc_heap::n_heaps; i++)
3110     {
3111         gc_heap* hp = gc_heap::g_heaps[i];
3112         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
3113         fire_per_heap_hist_event (current_gc_data_per_heap, hp->heap_number);
3114     }
3115 #else
3116     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
3117     fire_per_heap_hist_event (current_gc_data_per_heap, heap_number);
3118 #endif    
3119 }
3120
3121 inline BOOL
3122 gc_heap::dt_low_ephemeral_space_p (gc_tuning_point tp)
3123 {
3124     BOOL ret = FALSE;
3125
3126     switch (tp)
3127     {
3128         case tuning_deciding_condemned_gen:
3129         case tuning_deciding_compaction:
3130         case tuning_deciding_expansion:
3131         case tuning_deciding_full_gc:
3132         {
3133             ret = (!ephemeral_gen_fit_p (tp));
3134             break;
3135         }
3136         case tuning_deciding_promote_ephemeral:
3137         {
3138             size_t new_gen0size = approximate_new_allocation();
3139             ptrdiff_t plan_ephemeral_size = total_ephemeral_size;
3140             
3141             dprintf (GTC_LOG, ("h%d: plan eph size is %Id, new gen0 is %Id", 
3142                 heap_number, plan_ephemeral_size, new_gen0size));
3143             // If we were in no_gc_region we could have allocated a larger than normal segment,
3144             // and the next seg we allocate will be a normal sized seg so if we can't fit the new
3145             // ephemeral generations there, do an ephemeral promotion.
3146             ret = ((soh_segment_size - segment_info_size) < (plan_ephemeral_size + new_gen0size));
3147             break;
3148         }
3149         default:
3150             break;
3151     }
3152
3153     return ret;
3154 }
3155
3156 BOOL 
3157 gc_heap::dt_high_frag_p (gc_tuning_point tp, 
3158                          int gen_number, 
3159                          BOOL elevate_p)
3160 {
3161     BOOL ret = FALSE;
3162
3163     switch (tp)
3164     {
3165         case tuning_deciding_condemned_gen:
3166         {
3167             dynamic_data* dd = dynamic_data_of (gen_number);
3168             float fragmentation_burden = 0;
3169
3170             if (elevate_p)
3171             {
3172                 ret = (dd_fragmentation (dynamic_data_of (max_generation)) >= dd_max_size(dd));
3173                 dprintf (GTC_LOG, ("h%d: frag is %Id, max size is %Id",
3174                     heap_number, dd_fragmentation (dd), dd_max_size(dd)));
3175             }
3176             else
3177             {
3178 #ifndef MULTIPLE_HEAPS
3179                 if (gen_number == max_generation)
3180                 {
3181                     float frag_ratio = (float)(dd_fragmentation (dynamic_data_of (max_generation))) / (float)generation_size (max_generation);
3182                     if (frag_ratio > 0.65)
3183                     {
3184                         dprintf (GTC_LOG, ("g2 FR: %d%%", (int)(frag_ratio*100)));
3185                         return TRUE;
3186                     }
3187                 }
3188 #endif //!MULTIPLE_HEAPS
3189                 size_t fr = generation_unusable_fragmentation (generation_of (gen_number));
3190                 ret = (fr > dd_fragmentation_limit(dd));
3191                 if (ret)
3192                 {
3193                     fragmentation_burden = (float)fr / generation_size (gen_number);
3194                     ret = (fragmentation_burden > dd_v_fragmentation_burden_limit (dd));
3195                 }
3196                 dprintf (GTC_LOG, ("h%d: gen%d, frag is %Id, alloc effi: %d%%, unusable frag is %Id, ratio is %d",
3197                     heap_number, gen_number, dd_fragmentation (dd), 
3198                     (int)(100*generation_allocator_efficiency (generation_of (gen_number))),
3199                     fr, (int)(fragmentation_burden*100)));
3200             }
3201             break;
3202         }
3203         default:
3204             break;
3205     }
3206
3207     return ret;
3208 }
3209
3210 inline BOOL 
3211 gc_heap::dt_estimate_reclaim_space_p (gc_tuning_point tp, int gen_number)
3212 {
3213     BOOL ret = FALSE;
3214
3215     switch (tp)
3216     {
3217         case tuning_deciding_condemned_gen:
3218         {
3219             if (gen_number == max_generation)
3220             {
3221                 size_t est_maxgen_free = estimated_reclaim (gen_number);
3222
3223                 uint32_t num_heaps = 1;
3224 #ifdef MULTIPLE_HEAPS
3225                 num_heaps = gc_heap::n_heaps;
3226 #endif //MULTIPLE_HEAPS
3227
3228                 size_t min_frag_th = min_reclaim_fragmentation_threshold (num_heaps);
3229                 dprintf (GTC_LOG, ("h%d, min frag is %Id", heap_number, min_frag_th));
3230                 ret = (est_maxgen_free >= min_frag_th);
3231             }
3232             else
3233             {
3234                 assert (0);
3235             }
3236             break;
3237         }
3238
3239         default:
3240             break;
3241     }
3242
3243     return ret;
3244 }
3245
3246 // DTREVIEW: Right now we only estimate gen2 fragmentation. 
3247 // on 64-bit though we should consider gen1 or even gen0 fragmentatioin as
3248 // well 
3249 inline BOOL 
3250 gc_heap::dt_estimate_high_frag_p (gc_tuning_point tp, int gen_number, uint64_t available_mem)
3251 {
3252     BOOL ret = FALSE;
3253
3254     switch (tp)
3255     {
3256         case tuning_deciding_condemned_gen:
3257         {
3258             if (gen_number == max_generation)
3259             {
3260                 dynamic_data* dd = dynamic_data_of (gen_number);
3261                 float est_frag_ratio = 0;
3262                 if (dd_current_size (dd) == 0)
3263                 {
3264                     est_frag_ratio = 1;
3265                 }
3266                 else if ((dd_fragmentation (dd) == 0) || (dd_fragmentation (dd) + dd_current_size (dd) == 0))
3267                 {
3268                     est_frag_ratio = 0;
3269                 }
3270                 else
3271                 {
3272                     est_frag_ratio = (float)dd_fragmentation (dd) / (float)(dd_fragmentation (dd) + dd_current_size (dd));
3273                 }
3274                 
3275                 size_t est_frag = (dd_fragmentation (dd) + (size_t)((dd_desired_allocation (dd) - dd_new_allocation (dd)) * est_frag_ratio));
3276                 dprintf (GTC_LOG, ("h%d: gen%d: current_size is %Id, frag is %Id, est_frag_ratio is %d%%, estimated frag is %Id", 
3277                     heap_number,
3278                     gen_number,
3279                     dd_current_size (dd),
3280                     dd_fragmentation (dd),
3281                     (int)(est_frag_ratio*100),
3282                     est_frag));
3283
3284                 uint32_t num_heaps = 1;
3285
3286 #ifdef MULTIPLE_HEAPS
3287                 num_heaps = gc_heap::n_heaps;
3288 #endif //MULTIPLE_HEAPS
3289                 uint64_t min_frag_th = min_high_fragmentation_threshold(available_mem, num_heaps);
3290                 //dprintf (GTC_LOG, ("h%d, min frag is %I64d", heap_number, min_frag_th));
3291                 ret = (est_frag >= min_frag_th);
3292             }
3293             else
3294             {
3295                 assert (0);
3296             }
3297             break;
3298         }
3299
3300         default:
3301             break;
3302     }
3303
3304     return ret;
3305 }
3306
3307 inline BOOL 
3308 gc_heap::dt_low_card_table_efficiency_p (gc_tuning_point tp)
3309 {
3310     BOOL ret = FALSE;
3311
3312     switch (tp)
3313     {
3314     case tuning_deciding_condemned_gen:
3315     {
3316         /* promote into max-generation if the card table has too many
3317         * generation faults besides the n -> 0
3318         */
3319         ret = (generation_skip_ratio < 30);
3320         break;
3321     }
3322
3323     default:
3324         break;
3325     }
3326
3327     return ret;
3328 }
3329
3330 inline BOOL
3331 in_range_for_segment(uint8_t* add, heap_segment* seg)
3332 {
3333     return ((add >= heap_segment_mem (seg)) && (add < heap_segment_reserved (seg)));
3334 }
3335
3336 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
3337 // The array we allocate is organized as follows:
3338 // 0th element is the address of the last array we allocated.
3339 // starting from the 1st element are the segment addresses, that's
3340 // what buckets() returns.
3341 struct bk
3342 {
3343     uint8_t* add;
3344     size_t val;
3345 };
3346
3347 class sorted_table
3348 {
3349 private:
3350     ptrdiff_t size;
3351     ptrdiff_t count;
3352     bk* slots;
3353     bk* buckets() { return (slots + 1); }
3354     uint8_t*& last_slot (bk* arr) { return arr[0].add; }
3355     bk* old_slots;
3356 public:
3357     static  sorted_table* make_sorted_table ();
3358     BOOL    insert (uint8_t* add, size_t val);;
3359     size_t  lookup (uint8_t*& add);
3360     void    remove (uint8_t* add);
3361     void    clear ();
3362     void    delete_sorted_table();
3363     void    delete_old_slots();
3364     void    enqueue_old_slot(bk* sl);
3365     BOOL    ensure_space_for_insert();
3366 };
3367
3368 sorted_table*
3369 sorted_table::make_sorted_table ()
3370 {
3371     size_t size = 400;
3372
3373     // allocate one more bk to store the older slot address.
3374     sorted_table* res = (sorted_table*)new char [sizeof (sorted_table) + (size + 1) * sizeof (bk)];
3375     if (!res)
3376         return 0;
3377     res->size = size;
3378     res->slots = (bk*)(res + 1);
3379     res->old_slots = 0;
3380     res->clear();
3381     return res;
3382 }
3383
3384 void
3385 sorted_table::delete_sorted_table()
3386 {
3387     if (slots != (bk*)(this+1))
3388     {
3389         delete slots;
3390     }
3391     delete_old_slots();
3392     delete this;
3393 }
3394 void
3395 sorted_table::delete_old_slots()
3396 {
3397     uint8_t* sl = (uint8_t*)old_slots;
3398     while (sl)
3399     {
3400         uint8_t* dsl = sl;
3401         sl = last_slot ((bk*)sl);
3402         delete dsl;
3403     }
3404     old_slots = 0;
3405 }
3406 void
3407 sorted_table::enqueue_old_slot(bk* sl)
3408 {
3409     last_slot (sl) = (uint8_t*)old_slots;
3410     old_slots = sl;
3411 }
3412
3413 inline
3414 size_t
3415 sorted_table::lookup (uint8_t*& add)
3416 {
3417     ptrdiff_t high = (count-1);
3418     ptrdiff_t low = 0;
3419     ptrdiff_t ti;
3420     ptrdiff_t mid;
3421     bk* buck = buckets();
3422     while (low <= high)
3423     {
3424         mid = ((low + high)/2);
3425         ti = mid;
3426         if (buck[ti].add > add)
3427         {
3428             if ((ti > 0) && (buck[ti-1].add <= add))
3429             {
3430                 add = buck[ti-1].add;
3431                 return buck[ti - 1].val;
3432             }
3433             high = mid - 1;
3434         }
3435         else
3436         {
3437             if (buck[ti+1].add > add)
3438             {
3439                 add = buck[ti].add;
3440                 return buck[ti].val;
3441             }
3442             low = mid + 1;
3443         }
3444     }
3445     add = 0;
3446     return 0;
3447 }
3448
3449 BOOL
3450 sorted_table::ensure_space_for_insert()
3451 {
3452     if (count == size)
3453     {
3454         size = (size * 3)/2;
3455         assert((size * sizeof (bk)) > 0);
3456         bk* res = (bk*)new (nothrow) char [(size + 1) * sizeof (bk)];
3457         assert (res);
3458         if (!res)
3459             return FALSE;
3460
3461         last_slot (res) = 0;
3462         memcpy (((bk*)res + 1), buckets(), count * sizeof (bk));
3463         bk* last_old_slots = slots;
3464         slots = res;
3465         if (last_old_slots != (bk*)(this + 1))
3466             enqueue_old_slot (last_old_slots);
3467     }
3468     return TRUE;
3469 }
3470
3471 BOOL
3472 sorted_table::insert (uint8_t* add, size_t val)
3473 {
3474     //grow if no more room
3475     assert (count < size);
3476
3477     //insert sorted
3478     ptrdiff_t high = (count-1);
3479     ptrdiff_t low = 0;
3480     ptrdiff_t ti;
3481     ptrdiff_t mid;
3482     bk* buck = buckets();
3483     while (low <= high)
3484     {
3485         mid = ((low + high)/2);
3486         ti = mid;
3487         if (buck[ti].add > add)
3488         {
3489             if ((ti == 0) || (buck[ti-1].add <= add))
3490             {
3491                 // found insertion point
3492                 for (ptrdiff_t k = count; k > ti;k--)
3493                 {
3494                     buck [k] = buck [k-1];
3495                 }
3496                 buck[ti].add = add;
3497                 buck[ti].val = val;
3498                 count++;
3499                 return TRUE;
3500             }
3501             high = mid - 1;
3502         }
3503         else
3504         {
3505             if (buck[ti+1].add > add)
3506             {
3507                 //found the insertion point
3508                 for (ptrdiff_t k = count; k > ti+1;k--)
3509                 {
3510                     buck [k] = buck [k-1];
3511                 }
3512                 buck[ti+1].add = add;
3513                 buck[ti+1].val = val;
3514                 count++;
3515                 return TRUE;
3516             }
3517             low = mid + 1;
3518         }
3519     }
3520     assert (0);
3521     return TRUE;
3522 }
3523
3524 void
3525 sorted_table::remove (uint8_t* add)
3526 {
3527     ptrdiff_t high = (count-1);
3528     ptrdiff_t low = 0;
3529     ptrdiff_t ti;
3530     ptrdiff_t mid;
3531     bk* buck = buckets();
3532     while (low <= high)
3533     {
3534         mid = ((low + high)/2);
3535         ti = mid;
3536         if (buck[ti].add > add)
3537         {
3538             if (buck[ti-1].add <= add)
3539             {
3540                 // found the guy to remove
3541                 for (ptrdiff_t k = ti; k < count; k++)
3542                     buck[k-1] = buck[k];
3543                 count--;
3544                 return;
3545             }
3546             high = mid - 1;
3547         }
3548         else
3549         {
3550             if (buck[ti+1].add > add)
3551             {
3552                 // found the guy to remove
3553                 for (ptrdiff_t k = ti+1; k < count; k++)
3554                     buck[k-1] = buck[k];
3555                 count--;
3556                 return;
3557             }
3558             low = mid + 1;
3559         }
3560     }
3561     assert (0);
3562 }
3563
3564 void
3565 sorted_table::clear()
3566 {
3567     count = 1;
3568     buckets()[0].add = MAX_PTR;
3569 }
3570 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
3571
3572 #ifdef SEG_MAPPING_TABLE
3573 #ifdef GROWABLE_SEG_MAPPING_TABLE
3574 inline
3575 uint8_t* align_on_segment (uint8_t* add)
3576 {
3577     return (uint8_t*)((size_t)(add + (gc_heap::min_segment_size - 1)) & ~(gc_heap::min_segment_size - 1));
3578 }
3579
3580 inline
3581 uint8_t* align_lower_segment (uint8_t* add)
3582 {
3583     return (uint8_t*)((size_t)(add) & ~(gc_heap::min_segment_size - 1));
3584 }
3585
3586 size_t size_seg_mapping_table_of (uint8_t* from, uint8_t* end)
3587 {
3588     from = align_lower_segment (from);
3589     end = align_on_segment (end);
3590     dprintf (1, ("from: %Ix, end: %Ix, size: %Ix", from, end, sizeof (seg_mapping)*(((size_t)(end - from) >> gc_heap::min_segment_size_shr))));
3591     return sizeof (seg_mapping)*((size_t)(end - from) >> gc_heap::min_segment_size_shr);
3592 }
3593
3594 // for seg_mapping_table we want it to start from a pointer sized address.
3595 inline
3596 size_t align_for_seg_mapping_table (size_t size)
3597 {
3598     return ((size + (sizeof (uint8_t*) - 1)) &~ (sizeof (uint8_t*) - 1));
3599 }
3600
3601 inline
3602 size_t seg_mapping_word_of (uint8_t* add)
3603 {
3604     return (size_t)add >> gc_heap::min_segment_size_shr;
3605 }
3606 #else //GROWABLE_SEG_MAPPING_TABLE
3607 BOOL seg_mapping_table_init()
3608 {
3609 #ifdef BIT64
3610     uint64_t total_address_space = (uint64_t)8*1024*1024*1024*1024;
3611 #else
3612     uint64_t total_address_space = (uint64_t)4*1024*1024*1024;
3613 #endif // BIT64
3614
3615     size_t num_entries = (size_t)(total_address_space >> gc_heap::min_segment_size_shr);
3616     seg_mapping_table = new seg_mapping[num_entries];
3617
3618     if (seg_mapping_table)
3619     {
3620         memset (seg_mapping_table, 0, num_entries * sizeof (seg_mapping));
3621         dprintf (1, ("created %d entries for heap mapping (%Id bytes)", 
3622                      num_entries, (num_entries * sizeof (seg_mapping))));
3623         return TRUE;
3624     }
3625     else
3626     {
3627         dprintf (1, ("failed to create %d entries for heap mapping (%Id bytes)", 
3628                      num_entries, (num_entries * sizeof (seg_mapping))));
3629         return FALSE;
3630     }
3631 }
3632 #endif //GROWABLE_SEG_MAPPING_TABLE
3633
3634 #ifdef FEATURE_BASICFREEZE
3635 inline
3636 size_t ro_seg_begin_index (heap_segment* seg)
3637 {
3638     size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3639     begin_index = max (begin_index, (size_t)g_gc_lowest_address >> gc_heap::min_segment_size_shr);
3640     return begin_index;
3641 }
3642
3643 inline
3644 size_t ro_seg_end_index (heap_segment* seg)
3645 {
3646     size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) >> gc_heap::min_segment_size_shr;
3647     end_index = min (end_index, (size_t)g_gc_highest_address >> gc_heap::min_segment_size_shr);
3648     return end_index;
3649 }
3650
3651 void seg_mapping_table_add_ro_segment (heap_segment* seg)
3652 {
3653 #ifdef GROWABLE_SEG_MAPPING_TABLE
3654     if ((heap_segment_reserved (seg) <= g_gc_lowest_address) || (heap_segment_mem (seg) >= g_gc_highest_address))
3655         return;
3656 #endif //GROWABLE_SEG_MAPPING_TABLE
3657
3658     for (size_t entry_index = ro_seg_begin_index (seg); entry_index <= ro_seg_end_index (seg); entry_index++)
3659         seg_mapping_table[entry_index].seg1 = (heap_segment*)((size_t)seg_mapping_table[entry_index].seg1 | ro_in_entry);
3660 }
3661
3662 void seg_mapping_table_remove_ro_segment (heap_segment* seg)
3663 {
3664     UNREFERENCED_PARAMETER(seg);
3665 #if 0
3666 // POSSIBLE PERF TODO: right now we are not doing anything because we can't simply remove the flag. If it proves
3667 // to be a perf problem, we can search in the current ro segs and see if any lands in this range and only
3668 // remove the flag if none lands in this range.
3669 #endif //0
3670 }
3671
3672 heap_segment* ro_segment_lookup (uint8_t* o)
3673 {
3674     uint8_t* ro_seg_start = o;
3675     heap_segment* seg = (heap_segment*)gc_heap::seg_table->lookup (ro_seg_start);
3676
3677     if (ro_seg_start && in_range_for_segment (o, seg))
3678         return seg;
3679     else
3680         return 0;
3681 }
3682
3683 #endif //FEATURE_BASICFREEZE
3684
3685 void gc_heap::seg_mapping_table_add_segment (heap_segment* seg, gc_heap* hp)
3686 {
3687     size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3688     size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3689     seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3690     size_t end_index = seg_end >> gc_heap::min_segment_size_shr;
3691     seg_mapping* end_entry = &seg_mapping_table[end_index];
3692
3693     dprintf (1, ("adding seg %Ix(%d)-%Ix(%d)", 
3694         seg, begin_index, heap_segment_reserved (seg), end_index));
3695
3696     dprintf (1, ("before add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3697         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3698         end_index, (seg_mapping_table[end_index].boundary + 1)));
3699
3700 #ifdef MULTIPLE_HEAPS
3701 #ifdef SIMPLE_DPRINTF
3702     dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end %d: h0: %Ix(%d), h1: %Ix(%d)",
3703         begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3704         (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3705         end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3706         (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3707 #endif //SIMPLE_DPRINTF
3708     assert (end_entry->boundary == 0);
3709     assert (end_entry->h0 == 0);
3710     end_entry->h0 = hp;
3711     assert (begin_entry->h1 == 0);
3712     begin_entry->h1 = hp;
3713 #else
3714     UNREFERENCED_PARAMETER(hp);
3715 #endif //MULTIPLE_HEAPS
3716
3717     end_entry->boundary = (uint8_t*)seg_end;
3718
3719     dprintf (1, ("set entry %d seg1 and %d seg0 to %Ix", begin_index, end_index, seg));
3720     assert ((begin_entry->seg1 == 0) || ((size_t)(begin_entry->seg1) == ro_in_entry));
3721     begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) | (size_t)seg);
3722     end_entry->seg0 = seg;
3723
3724     // for every entry inbetween we need to set its heap too.
3725     for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3726     {
3727         assert (seg_mapping_table[entry_index].boundary == 0);
3728 #ifdef MULTIPLE_HEAPS
3729         assert (seg_mapping_table[entry_index].h0 == 0);
3730         seg_mapping_table[entry_index].h1 = hp;
3731 #endif //MULTIPLE_HEAPS
3732         seg_mapping_table[entry_index].seg1 = seg;
3733     }
3734
3735     dprintf (1, ("after add: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3736         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3737         end_index, (seg_mapping_table[end_index].boundary + 1)));
3738 #if defined(MULTIPLE_HEAPS) && defined(SIMPLE_DPRINTF)
3739     dprintf (1, ("begin %d: h0: %Ix(%d), h1: %Ix(%d); end: %d h0: %Ix(%d), h1: %Ix(%d)",
3740         begin_index, (uint8_t*)(begin_entry->h0), (begin_entry->h0 ? begin_entry->h0->heap_number : -1),
3741         (uint8_t*)(begin_entry->h1), (begin_entry->h1 ? begin_entry->h1->heap_number : -1),
3742         end_index, (uint8_t*)(end_entry->h0), (end_entry->h0 ? end_entry->h0->heap_number : -1),
3743         (uint8_t*)(end_entry->h1), (end_entry->h1 ? end_entry->h1->heap_number : -1)));
3744 #endif //MULTIPLE_HEAPS && SIMPLE_DPRINTF
3745 }
3746
3747 void gc_heap::seg_mapping_table_remove_segment (heap_segment* seg)
3748 {
3749     size_t seg_end = (size_t)(heap_segment_reserved (seg) - 1);
3750     size_t begin_index = (size_t)seg >> gc_heap::min_segment_size_shr;
3751     seg_mapping* begin_entry = &seg_mapping_table[begin_index];
3752     size_t end_index = seg_end >> gc_heap::min_segment_size_shr;
3753     seg_mapping* end_entry = &seg_mapping_table[end_index];
3754     dprintf (1, ("removing seg %Ix(%d)-%Ix(%d)", 
3755         seg, begin_index, heap_segment_reserved (seg), end_index));
3756
3757     assert (end_entry->boundary == (uint8_t*)seg_end);
3758     end_entry->boundary = 0;
3759
3760 #ifdef MULTIPLE_HEAPS
3761     gc_heap* hp = heap_segment_heap (seg);
3762     assert (end_entry->h0 == hp);
3763     end_entry->h0 = 0;
3764     assert (begin_entry->h1 == hp);
3765     begin_entry->h1 = 0;
3766 #endif //MULTIPLE_HEAPS
3767
3768     assert (begin_entry->seg1 != 0);
3769     begin_entry->seg1 = (heap_segment*)((size_t)(begin_entry->seg1) & ro_in_entry);
3770     end_entry->seg0 = 0;
3771
3772     // for every entry inbetween we need to reset its heap too.
3773     for (size_t entry_index = (begin_index + 1); entry_index <= (end_index - 1); entry_index++)
3774     {
3775         assert (seg_mapping_table[entry_index].boundary == 0);
3776 #ifdef MULTIPLE_HEAPS
3777         assert (seg_mapping_table[entry_index].h0 == 0);
3778         assert (seg_mapping_table[entry_index].h1 == hp);
3779         seg_mapping_table[entry_index].h1 = 0;
3780 #endif //MULTIPLE_HEAPS
3781         seg_mapping_table[entry_index].seg1 = 0;
3782     }
3783
3784     dprintf (1, ("after remove: begin entry%d: boundary: %Ix; end entry: %d: boundary: %Ix", 
3785         begin_index, (seg_mapping_table[begin_index].boundary + 1),
3786         end_index, (seg_mapping_table[end_index].boundary + 1)));
3787 #ifdef MULTIPLE_HEAPS
3788     dprintf (1, ("begin %d: h0: %Ix, h1: %Ix; end: %d h0: %Ix, h1: %Ix",
3789         begin_index, (uint8_t*)(begin_entry->h0), (uint8_t*)(begin_entry->h1),
3790         end_index, (uint8_t*)(end_entry->h0), (uint8_t*)(end_entry->h1)));
3791 #endif //MULTIPLE_HEAPS
3792 }
3793
3794 #ifdef MULTIPLE_HEAPS
3795 inline
3796 gc_heap* seg_mapping_table_heap_of_worker (uint8_t* o)
3797 {
3798     size_t index = (size_t)o >> gc_heap::min_segment_size_shr;
3799     seg_mapping* entry = &seg_mapping_table[index];
3800
3801     gc_heap* hp = ((o > entry->boundary) ? entry->h1 : entry->h0);
3802
3803     dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, h0: %Ix, seg0: %Ix, h1: %Ix, seg1: %Ix",
3804         o, index, (entry->boundary + 1), 
3805         (uint8_t*)(entry->h0), (uint8_t*)(entry->seg0),
3806         (uint8_t*)(entry->h1), (uint8_t*)(entry->seg1)));
3807
3808 #ifdef _DEBUG
3809     heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3810 #ifdef FEATURE_BASICFREEZE
3811     if ((size_t)seg & ro_in_entry)
3812         seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3813 #endif //FEATURE_BASICFREEZE
3814
3815     if (seg)
3816     {
3817         if (in_range_for_segment (o, seg))
3818         {
3819             dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, seg, (uint8_t*)heap_segment_allocated (seg)));
3820         }
3821         else
3822         {
3823             dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg", 
3824                 seg, (uint8_t*)heap_segment_allocated (seg), o));
3825         }
3826     }
3827     else
3828     {
3829         dprintf (2, ("could not find obj %Ix in any existing segments", o));
3830     }
3831 #endif //_DEBUG
3832
3833     return hp;
3834 }
3835
3836 gc_heap* seg_mapping_table_heap_of (uint8_t* o)
3837 {
3838 #ifdef GROWABLE_SEG_MAPPING_TABLE
3839     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3840         return 0;
3841 #endif //GROWABLE_SEG_MAPPING_TABLE
3842
3843     return seg_mapping_table_heap_of_worker (o);
3844 }
3845
3846 gc_heap* seg_mapping_table_heap_of_gc (uint8_t* o)
3847 {
3848 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3849     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3850         return 0;
3851 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3852
3853     return seg_mapping_table_heap_of_worker (o);
3854 }
3855 #endif //MULTIPLE_HEAPS
3856
3857 // Only returns a valid seg if we can actually find o on the seg.
3858 heap_segment* seg_mapping_table_segment_of (uint8_t* o)
3859 {
3860 #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE)
3861     if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address))
3862 #ifdef FEATURE_BASICFREEZE
3863         return ro_segment_lookup (o);
3864 #else
3865         return 0;
3866 #endif //FEATURE_BASICFREEZE
3867 #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE
3868
3869     size_t index = (size_t)o >> gc_heap::min_segment_size_shr;
3870     seg_mapping* entry = &seg_mapping_table[index];
3871
3872     dprintf (2, ("checking obj %Ix, index is %Id, entry: boundry: %Ix, seg0: %Ix, seg1: %Ix",
3873         o, index, (entry->boundary + 1), 
3874         (uint8_t*)(entry->seg0), (uint8_t*)(entry->seg1)));
3875
3876     heap_segment* seg = ((o > entry->boundary) ? entry->seg1 : entry->seg0);
3877 #ifdef FEATURE_BASICFREEZE
3878     if ((size_t)seg & ro_in_entry)
3879         seg = (heap_segment*)((size_t)seg & ~ro_in_entry);
3880 #endif //FEATURE_BASICFREEZE
3881
3882     if (seg)
3883     {
3884         if (in_range_for_segment (o, seg))
3885         {
3886             dprintf (2, ("obj %Ix belongs to segment %Ix(-%Ix)", o, (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg)));
3887         }
3888         else
3889         {
3890             dprintf (2, ("found seg %Ix(-%Ix) for obj %Ix, but it's not on the seg, setting it to 0", 
3891                 (uint8_t*)heap_segment_mem(seg), (uint8_t*)heap_segment_reserved(seg), o));
3892             seg = 0;
3893         }
3894     }
3895     else
3896     {
3897         dprintf (2, ("could not find obj %Ix in any existing segments", o));
3898     }
3899
3900 #ifdef FEATURE_BASICFREEZE
3901     // TODO: This was originally written assuming that the seg_mapping_table would always contain entries for ro 
3902     // segments whenever the ro segment falls into the [g_gc_lowest_address,g_gc_highest_address) range.  I.e., it had an 
3903     // extra "&& (size_t)(entry->seg1) & ro_in_entry" expression.  However, at the moment, grow_brick_card_table does 
3904     // not correctly go through the ro segments and add them back to the seg_mapping_table when the [lowest,highest) 
3905     // range changes.  We should probably go ahead and modify grow_brick_card_table and put back the 
3906     // "&& (size_t)(entry->seg1) & ro_in_entry" here.
3907     if (!seg)
3908     {
3909         seg = ro_segment_lookup (o);
3910         if (seg && !in_range_for_segment (o, seg))
3911             seg = 0;
3912     }
3913 #endif //FEATURE_BASICFREEZE
3914
3915     return seg;
3916 }
3917 #endif //SEG_MAPPING_TABLE
3918
3919 size_t gcard_of ( uint8_t*);
3920
3921 #define memref(i) *(uint8_t**)(i)
3922
3923 //GC Flags
3924 #define GC_MARKED       (size_t)0x1
3925 #define slot(i, j) ((uint8_t**)(i))[j+1]
3926
3927 #define free_object_base_size (plug_skew + sizeof(ArrayBase))
3928
3929 class CObjectHeader : public Object
3930 {
3931 public:
3932
3933 #if defined(FEATURE_REDHAWK) || defined(BUILD_AS_STANDALONE)
3934     // The GC expects the following methods that are provided by the Object class in the CLR but not provided
3935     // by Redhawk's version of Object.
3936     uint32_t GetNumComponents()
3937     {
3938         return ((ArrayBase *)this)->GetNumComponents();
3939     }
3940
3941     void Validate(BOOL bDeep=TRUE, BOOL bVerifyNextHeader = TRUE)
3942     {
3943         UNREFERENCED_PARAMETER(bVerifyNextHeader);
3944
3945         if (this == NULL)
3946             return;
3947
3948         MethodTable * pMT = GetMethodTable();
3949
3950         _ASSERTE(pMT->SanityCheck());
3951
3952         bool noRangeChecks =
3953             (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_RANGE_CHECKS) == GCConfig::HEAPVERIFY_NO_RANGE_CHECKS;
3954
3955         BOOL fSmallObjectHeapPtr = FALSE, fLargeObjectHeapPtr = FALSE;
3956         if (!noRangeChecks)
3957         {
3958             fSmallObjectHeapPtr = g_theGCHeap->IsHeapPointer(this, TRUE);
3959             if (!fSmallObjectHeapPtr)
3960                 fLargeObjectHeapPtr = g_theGCHeap->IsHeapPointer(this);
3961
3962             _ASSERTE(fSmallObjectHeapPtr || fLargeObjectHeapPtr);
3963         }
3964
3965 #ifdef FEATURE_STRUCTALIGN
3966         _ASSERTE(IsStructAligned((uint8_t *)this, GetMethodTable()->GetBaseAlignment()));
3967 #endif // FEATURE_STRUCTALIGN
3968
3969 #ifdef FEATURE_64BIT_ALIGNMENT
3970         if (pMT->RequiresAlign8())
3971         {
3972             _ASSERTE((((size_t)this) & 0x7) == (pMT->IsValueType() ? 4U : 0U));
3973         }
3974 #endif // FEATURE_64BIT_ALIGNMENT
3975
3976 #ifdef VERIFY_HEAP
3977         if (bDeep && (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC))
3978             g_theGCHeap->ValidateObjectMember(this);
3979 #endif
3980         if (fSmallObjectHeapPtr)
3981         {
3982 #ifdef FEATURE_BASICFREEZE
3983             _ASSERTE(!g_theGCHeap->IsLargeObject(pMT) || g_theGCHeap->IsInFrozenSegment(this));
3984 #else
3985             _ASSERTE(!g_theGCHeap->IsLargeObject(pMT));
3986 #endif
3987         }
3988     }
3989
3990     void ValidatePromote(ScanContext *sc, uint32_t flags)
3991     {
3992         UNREFERENCED_PARAMETER(sc);
3993         UNREFERENCED_PARAMETER(flags);
3994
3995         Validate();
3996     }
3997
3998     void ValidateHeap(Object *from, BOOL bDeep)
3999     {
4000         UNREFERENCED_PARAMETER(from);
4001
4002         Validate(bDeep, FALSE);
4003     }
4004
4005 #endif //FEATURE_REDHAWK || BUILD_AS_STANDALONE
4006
4007     /////
4008     //
4009     // Header Status Information
4010     //
4011
4012     MethodTable    *GetMethodTable() const
4013     {
4014         return( (MethodTable *) (((size_t) RawGetMethodTable()) & (~(GC_MARKED))));
4015     }
4016
4017     void SetMarked()
4018     {
4019         RawSetMethodTable((MethodTable *) (((size_t) RawGetMethodTable()) | GC_MARKED));
4020     }
4021
4022     BOOL IsMarked() const
4023     {
4024         return !!(((size_t)RawGetMethodTable()) & GC_MARKED);
4025     }
4026
4027     void SetPinned()
4028     {
4029         assert (!(gc_heap::settings.concurrent));
4030         GetHeader()->SetGCBit();
4031     }
4032
4033     BOOL IsPinned() const
4034     {
4035         return !!((((CObjectHeader*)this)->GetHeader()->GetBits()) & BIT_SBLK_GC_RESERVE);
4036     }
4037
4038     void ClearMarked()
4039     {
4040         RawSetMethodTable( GetMethodTable() );
4041     }
4042
4043     CGCDesc *GetSlotMap ()
4044     {
4045         assert (GetMethodTable()->ContainsPointers());
4046         return CGCDesc::GetCGCDescFromMT(GetMethodTable());
4047     }
4048
4049     void SetFree(size_t size)
4050     {
4051         assert (size >= free_object_base_size);
4052
4053         assert (g_gc_pFreeObjectMethodTable->GetBaseSize() == free_object_base_size);
4054         assert (g_gc_pFreeObjectMethodTable->RawGetComponentSize() == 1);
4055
4056         RawSetMethodTable( g_gc_pFreeObjectMethodTable );
4057
4058         size_t* numComponentsPtr = (size_t*) &((uint8_t*) this)[ArrayBase::GetOffsetOfNumComponents()];
4059         *numComponentsPtr = size - free_object_base_size;
4060 #ifdef VERIFY_HEAP
4061         //This introduces a bug in the free list management. 
4062         //((void**) this)[-1] = 0;    // clear the sync block,
4063         assert (*numComponentsPtr >= 0);
4064         if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
4065             memset (((uint8_t*)this)+sizeof(ArrayBase), 0xcc, *numComponentsPtr);
4066 #endif //VERIFY_HEAP
4067     }
4068
4069     void UnsetFree()
4070     {
4071         size_t size = free_object_base_size - plug_skew;
4072
4073         // since we only need to clear 2 ptr size, we do it manually
4074         PTR_PTR m = (PTR_PTR) this;
4075         for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
4076             *(m++) = 0;
4077     }
4078
4079     BOOL IsFree () const
4080     {
4081         return (GetMethodTable() == g_gc_pFreeObjectMethodTable);
4082     }
4083
4084 #ifdef FEATURE_STRUCTALIGN
4085     int GetRequiredAlignment () const
4086     {
4087         return GetMethodTable()->GetRequiredAlignment();
4088     }
4089 #endif // FEATURE_STRUCTALIGN
4090
4091     BOOL ContainsPointers() const
4092     {
4093         return GetMethodTable()->ContainsPointers();
4094     }
4095
4096 #ifdef COLLECTIBLE_CLASS
4097     BOOL Collectible() const
4098     {
4099         return GetMethodTable()->Collectible();
4100     }
4101
4102     FORCEINLINE BOOL ContainsPointersOrCollectible() const
4103     {
4104         MethodTable *pMethodTable = GetMethodTable();
4105         return (pMethodTable->ContainsPointers() || pMethodTable->Collectible());
4106     }
4107 #endif //COLLECTIBLE_CLASS
4108
4109     Object* GetObjectBase() const
4110     {
4111         return (Object*) this;
4112     }
4113 };
4114
4115 #define header(i) ((CObjectHeader*)(i))
4116
4117 #define free_list_slot(x) ((uint8_t**)(x))[2]
4118 #define free_list_undo(x) ((uint8_t**)(x))[-1]
4119 #define UNDO_EMPTY ((uint8_t*)1)
4120
4121 #ifdef SHORT_PLUGS
4122 inline 
4123 void set_plug_padded (uint8_t* node)
4124 {
4125     header(node)->SetMarked();
4126 }
4127 inline
4128 void clear_plug_padded (uint8_t* node)
4129 {
4130     header(node)->ClearMarked();
4131 }
4132 inline
4133 BOOL is_plug_padded (uint8_t* node)
4134 {
4135     return header(node)->IsMarked();
4136 }
4137 #else //SHORT_PLUGS
4138 inline void set_plug_padded (uint8_t* node){}
4139 inline void clear_plug_padded (uint8_t* node){}
4140 inline
4141 BOOL is_plug_padded (uint8_t* node){return FALSE;}
4142 #endif //SHORT_PLUGS
4143
4144
4145 inline size_t unused_array_size(uint8_t * p)
4146 {
4147     assert(((CObjectHeader*)p)->IsFree());
4148
4149     size_t* numComponentsPtr = (size_t*)(p + ArrayBase::GetOffsetOfNumComponents());
4150     return free_object_base_size + *numComponentsPtr;
4151 }
4152
4153 heap_segment* heap_segment_rw (heap_segment* ns)
4154 {
4155     if ((ns == 0) || !heap_segment_read_only_p (ns))
4156     {
4157         return ns;
4158     }
4159     else
4160     {
4161         do
4162         {
4163             ns = heap_segment_next (ns);
4164         } while ((ns != 0) && heap_segment_read_only_p (ns));
4165         return ns;
4166     }
4167 }
4168
4169 //returns the next non ro segment.
4170 heap_segment* heap_segment_next_rw (heap_segment* seg)
4171 {
4172     heap_segment* ns = heap_segment_next (seg);
4173     return heap_segment_rw (ns);
4174 }
4175
4176 // returns the segment before seg.
4177 heap_segment* heap_segment_prev_rw (heap_segment* begin, heap_segment* seg)
4178 {
4179     assert (begin != 0);
4180     heap_segment* prev = begin;
4181     heap_segment* current = heap_segment_next_rw (begin);
4182
4183     while (current && current != seg)
4184     {
4185         prev = current;
4186         current = heap_segment_next_rw (current);
4187     }
4188
4189     if (current == seg)
4190     {
4191         return prev;
4192     }
4193     else
4194     {
4195         return 0;
4196     }
4197 }
4198
4199 // returns the segment before seg.
4200 heap_segment* heap_segment_prev (heap_segment* begin, heap_segment* seg)
4201 {
4202     assert (begin != 0);
4203     heap_segment* prev = begin;
4204     heap_segment* current = heap_segment_next (begin);
4205
4206     while (current && current != seg)
4207     {
4208         prev = current;
4209         current = heap_segment_next (current);
4210     }
4211
4212     if (current == seg)
4213     {
4214         return prev;
4215     }
4216     else
4217     {
4218         return 0;
4219     }
4220 }
4221
4222 heap_segment* heap_segment_in_range (heap_segment* ns)
4223 {
4224     if ((ns == 0) || heap_segment_in_range_p (ns))
4225     {
4226         return ns;
4227     }
4228     else
4229     {
4230         do
4231         {
4232             ns = heap_segment_next (ns);
4233         } while ((ns != 0) && !heap_segment_in_range_p (ns));
4234         return ns;
4235     }
4236 }
4237
4238 heap_segment* heap_segment_next_in_range (heap_segment* seg)
4239 {
4240     heap_segment* ns = heap_segment_next (seg);
4241     return heap_segment_in_range (ns);
4242 }
4243
4244 typedef struct
4245 {
4246     uint8_t* memory_base;
4247 } imemory_data;
4248
4249 typedef struct
4250 {
4251     imemory_data *initial_memory;
4252     imemory_data *initial_normal_heap; // points into initial_memory_array
4253     imemory_data *initial_large_heap;  // points into initial_memory_array
4254
4255     size_t block_size_normal;
4256     size_t block_size_large;
4257
4258     size_t block_count;                // # of blocks in each
4259     size_t current_block_normal;
4260     size_t current_block_large;
4261
4262     enum 
4263     { 
4264         ALLATONCE = 1, 
4265         TWO_STAGE, 
4266         EACH_BLOCK 
4267     };
4268
4269     size_t allocation_pattern;
4270 } initial_memory_details;
4271
4272 initial_memory_details memory_details;
4273
4274 BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_heaps)
4275 {
4276     BOOL reserve_success = FALSE;
4277
4278     // should only be called once
4279     assert (memory_details.initial_memory == 0);
4280
4281     memory_details.initial_memory = new (nothrow) imemory_data[num_heaps*2];
4282     if (memory_details.initial_memory == 0)
4283     {
4284         dprintf (2, ("failed to reserve %Id bytes for imemory_data", num_heaps*2*sizeof(imemory_data)));
4285         return FALSE;
4286     }
4287
4288     memory_details.initial_normal_heap = memory_details.initial_memory;
4289     memory_details.initial_large_heap = memory_details.initial_memory + num_heaps;
4290     memory_details.block_size_normal = normal_size;
4291     memory_details.block_size_large = large_size;
4292     memory_details.block_count = num_heaps;
4293
4294     memory_details.current_block_normal = 0;
4295     memory_details.current_block_large = 0;
4296
4297     g_gc_lowest_address = MAX_PTR;
4298     g_gc_highest_address = 0;
4299
4300     if (((size_t)MAX_PTR - large_size) < normal_size)
4301     {
4302         // we are already overflowing with just one heap.
4303         dprintf (2, ("0x%Ix + 0x%Ix already overflow", normal_size, large_size));
4304         return FALSE;
4305     }
4306
4307     if (((size_t)MAX_PTR / memory_details.block_count) < (normal_size + large_size))
4308     {
4309         dprintf (2, ("(0x%Ix + 0x%Ix)*0x%Ix overflow", normal_size, large_size, memory_details.block_count));
4310         return FALSE;
4311     }
4312
4313     size_t requestedMemory = memory_details.block_count * (normal_size + large_size);
4314
4315     uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory);
4316     if (allatonce_block)
4317     {
4318         g_gc_lowest_address =  allatonce_block;
4319         g_gc_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size));
4320         memory_details.allocation_pattern = initial_memory_details::ALLATONCE;
4321
4322         for(size_t i = 0; i < memory_details.block_count; i++)
4323         {
4324             memory_details.initial_normal_heap[i].memory_base = allatonce_block + (i*normal_size);
4325             memory_details.initial_large_heap[i].memory_base = allatonce_block +
4326                             (memory_details.block_count*normal_size) + (i*large_size);
4327             reserve_success = TRUE;
4328         }
4329     }
4330     else
4331     {
4332         // try to allocate 2 blocks
4333         uint8_t* b1 = 0;
4334         uint8_t* b2 = 0;
4335         b1 = (uint8_t*)virtual_alloc (memory_details.block_count * normal_size);
4336         if (b1)
4337         {
4338             b2 = (uint8_t*)virtual_alloc (memory_details.block_count * large_size);
4339             if (b2)
4340             {
4341                 memory_details.allocation_pattern = initial_memory_details::TWO_STAGE;
4342                 g_gc_lowest_address = min(b1,b2);
4343                 g_gc_highest_address = max(b1 + memory_details.block_count*normal_size,
4344                                         b2 + memory_details.block_count*large_size);
4345                 for(size_t i = 0; i < memory_details.block_count; i++)
4346                 {
4347                     memory_details.initial_normal_heap[i].memory_base = b1 + (i*normal_size);
4348                     memory_details.initial_large_heap[i].memory_base = b2 + (i*large_size);
4349                     reserve_success = TRUE;
4350                 }
4351             }
4352             else
4353             {
4354                 // b2 allocation failed, we'll go on to try allocating each block.
4355                 // We could preserve the b1 alloc, but code complexity increases
4356                 virtual_free (b1, memory_details.block_count * normal_size);
4357             }
4358         }
4359
4360         if ((b2==NULL) && ( memory_details.block_count > 1))
4361         {
4362             memory_details.allocation_pattern = initial_memory_details::EACH_BLOCK;
4363
4364             imemory_data *current_block = memory_details.initial_memory;
4365             for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4366             {
4367                 size_t block_size = ((i < memory_details.block_count) ?
4368                                      memory_details.block_size_normal :
4369                                      memory_details.block_size_large);
4370                 current_block->memory_base =
4371                     (uint8_t*)virtual_alloc (block_size);
4372                 if (current_block->memory_base == 0)
4373                 {
4374                     // Free the blocks that we've allocated so far
4375                     current_block = memory_details.initial_memory;
4376                     for(size_t j = 0; j < i; j++, current_block++){
4377                         if (current_block->memory_base != 0){
4378                             block_size = ((j < memory_details.block_count) ?
4379                                      memory_details.block_size_normal :
4380                                      memory_details.block_size_large);
4381                              virtual_free (current_block->memory_base , block_size);
4382                         }
4383                     }
4384                     reserve_success = FALSE;
4385                     break;
4386                 }
4387                 else
4388                 {
4389                     if (current_block->memory_base < g_gc_lowest_address)
4390                         g_gc_lowest_address =  current_block->memory_base;
4391                     if (((uint8_t *) current_block->memory_base + block_size) > g_gc_highest_address)
4392                         g_gc_highest_address = (current_block->memory_base + block_size);
4393                 }
4394                 reserve_success = TRUE;
4395             }
4396         }
4397     }
4398
4399     return reserve_success;
4400 }
4401
4402 void destroy_initial_memory()
4403 {
4404     if (memory_details.initial_memory != NULL)
4405     {
4406         if (memory_details.allocation_pattern == initial_memory_details::ALLATONCE)
4407         {
4408             virtual_free(memory_details.initial_memory[0].memory_base,
4409                 memory_details.block_count*(memory_details.block_size_normal +
4410                 memory_details.block_size_large));
4411         }
4412         else if (memory_details.allocation_pattern == initial_memory_details::TWO_STAGE)
4413         {
4414             virtual_free (memory_details.initial_normal_heap[0].memory_base,
4415                 memory_details.block_count*memory_details.block_size_normal);
4416
4417             virtual_free (memory_details.initial_large_heap[0].memory_base,
4418                 memory_details.block_count*memory_details.block_size_large);
4419         }
4420         else
4421         {
4422             assert (memory_details.allocation_pattern == initial_memory_details::EACH_BLOCK);
4423             imemory_data *current_block = memory_details.initial_memory;
4424             for(size_t i = 0; i < (memory_details.block_count*2); i++, current_block++)
4425             {
4426                 size_t block_size = (i < memory_details.block_count) ? memory_details.block_size_normal :
4427                                                                        memory_details.block_size_large;
4428                 if (current_block->memory_base != NULL)
4429                 {
4430                     virtual_free (current_block->memory_base, block_size);
4431                 }
4432             }
4433         }
4434
4435         delete [] memory_details.initial_memory;
4436         memory_details.initial_memory = NULL;
4437         memory_details.initial_normal_heap = NULL;
4438         memory_details.initial_large_heap = NULL;
4439     }
4440 }
4441
4442 void* next_initial_memory (size_t size)
4443 {
4444     assert ((size == memory_details.block_size_normal) || (size == memory_details.block_size_large));
4445     void *res = NULL;
4446
4447     if ((size != memory_details.block_size_normal) ||
4448         ((memory_details.current_block_normal == memory_details.block_count) &&
4449          (memory_details.block_size_normal == memory_details.block_size_large)))
4450     {
4451         // If the block sizes are the same, flow block requests from normal to large
4452         assert (memory_details.current_block_large < memory_details.block_count);
4453         assert (memory_details.initial_large_heap != 0);
4454
4455         res = memory_details.initial_large_heap[memory_details.current_block_large].memory_base;
4456         memory_details.current_block_large++;
4457     }
4458     else
4459     {
4460         assert (memory_details.current_block_normal < memory_details.block_count);
4461         assert (memory_details.initial_normal_heap != NULL);
4462
4463         res = memory_details.initial_normal_heap[memory_details.current_block_normal].memory_base;
4464         memory_details.current_block_normal++;
4465     }
4466
4467     return res;
4468 }
4469
4470 heap_segment* get_initial_segment (size_t size, int h_number)
4471 {
4472     void* mem = next_initial_memory (size);
4473     heap_segment* res = gc_heap::make_heap_segment ((uint8_t*)mem, size , h_number);
4474
4475     return res;
4476 }
4477
4478 void* virtual_alloc (size_t size)
4479 {
4480     size_t requested_size = size;
4481
4482     if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4483     {
4484         gc_heap::reserved_memory_limit =
4485             GCScan::AskForMoreReservedMemory (gc_heap::reserved_memory_limit, requested_size);
4486         if ((gc_heap::reserved_memory_limit - gc_heap::reserved_memory) < requested_size)
4487         {
4488             return 0;
4489         }
4490     }
4491
4492     uint32_t flags = VirtualReserveFlags::None;
4493 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4494     if (virtual_alloc_hardware_write_watch)
4495     {
4496         flags = VirtualReserveFlags::WriteWatch;
4497     }
4498 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
4499     void* prgmem = GCToOSInterface::VirtualReserve (requested_size, card_size * card_word_width, flags);
4500     void *aligned_mem = prgmem;
4501
4502     // We don't want (prgmem + size) to be right at the end of the address space 
4503     // because we'd have to worry about that everytime we do (address + size).
4504     // We also want to make sure that we leave loh_size_threshold at the end 
4505     // so we allocate a small object we don't need to worry about overflow there
4506     // when we do alloc_ptr+size.
4507     if (prgmem)
4508     {
4509         uint8_t* end_mem = (uint8_t*)prgmem + requested_size;
4510
4511         if ((end_mem == 0) || ((size_t)(MAX_PTR - end_mem) <= END_SPACE_AFTER_GC))
4512         {
4513             GCToOSInterface::VirtualRelease (prgmem, requested_size);
4514             dprintf (2, ("Virtual Alloc size %Id returned memory right against 4GB [%Ix, %Ix[ - discarding",
4515                         requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size)));
4516             prgmem = 0;
4517             aligned_mem = 0;
4518         }
4519     }
4520
4521     if (prgmem)
4522     {
4523         gc_heap::reserved_memory += requested_size;
4524     }
4525
4526     dprintf (2, ("Virtual Alloc size %Id: [%Ix, %Ix[",
4527                  requested_size, (size_t)prgmem, (size_t)((uint8_t*)prgmem+requested_size)));
4528
4529     return aligned_mem;
4530 }
4531
4532 void virtual_free (void* add, size_t size)
4533 {
4534     GCToOSInterface::VirtualRelease (add, size);
4535     gc_heap::reserved_memory -= size;
4536     dprintf (2, ("Virtual Free size %Id: [%Ix, %Ix[",
4537                  size, (size_t)add, (size_t)((uint8_t*)add+size)));
4538 }
4539
4540 static size_t get_valid_segment_size (BOOL large_seg=FALSE)
4541 {
4542     size_t seg_size, initial_seg_size;
4543
4544     if (!large_seg)
4545     {
4546         initial_seg_size = INITIAL_ALLOC;
4547         seg_size = static_cast<size_t>(GCConfig::GetSegmentSize());
4548     }
4549     else
4550     {
4551         initial_seg_size = LHEAP_ALLOC;
4552         seg_size = static_cast<size_t>(GCConfig::GetSegmentSize()) / 2;
4553     }
4554
4555 #ifdef MULTIPLE_HEAPS
4556 #ifdef BIT64
4557     if (!large_seg)
4558 #endif // BIT64
4559     {
4560         if (g_num_processors > 4)
4561             initial_seg_size /= 2;
4562         if (g_num_processors > 8)
4563             initial_seg_size /= 2;
4564     }
4565 #endif //MULTIPLE_HEAPS
4566
4567     // if seg_size is small but not 0 (0 is default if config not set)
4568     // then set the segment to the minimum size
4569     if (!g_theGCHeap->IsValidSegmentSize(seg_size))
4570     {
4571         // if requested size is between 1 byte and 4MB, use min
4572         if ((seg_size >> 1) && !(seg_size >> 22))
4573             seg_size = 1024*1024*4;
4574         else
4575             seg_size = initial_seg_size;
4576     }
4577
4578 #ifdef SEG_MAPPING_TABLE
4579 #ifdef BIT64
4580     seg_size = round_up_power2 (seg_size);
4581 #else
4582     seg_size = round_down_power2 (seg_size);
4583 #endif // BIT64
4584 #endif //SEG_MAPPING_TABLE
4585
4586     return (seg_size);
4587 }
4588
4589 void
4590 gc_heap::compute_new_ephemeral_size()
4591 {
4592     int eph_gen_max = max_generation - 1 - (settings.promotion ? 1 : 0);
4593     size_t padding_size = 0;
4594
4595     for (int i = 0; i <= eph_gen_max; i++)
4596     {
4597         dynamic_data* dd = dynamic_data_of (i);
4598         total_ephemeral_size += (dd_survived_size (dd) - dd_pinned_survived_size (dd));
4599 #ifdef RESPECT_LARGE_ALIGNMENT
4600         total_ephemeral_size += dd_num_npinned_plugs (dd) * switch_alignment_size (FALSE);
4601 #endif //RESPECT_LARGE_ALIGNMENT
4602 #ifdef FEATURE_STRUCTALIGN
4603         total_ephemeral_size += dd_num_npinned_plugs (dd) * MAX_STRUCTALIGN;
4604 #endif //FEATURE_STRUCTALIGN
4605
4606 #ifdef SHORT_PLUGS
4607         padding_size += dd_padding_size (dd);
4608 #endif //SHORT_PLUGS
4609     }
4610
4611     total_ephemeral_size += eph_gen_starts_size;
4612
4613 #ifdef RESPECT_LARGE_ALIGNMENT
4614     size_t planned_ephemeral_size = heap_segment_plan_allocated (ephemeral_heap_segment) -
4615                                        generation_plan_allocation_start (generation_of (max_generation-1));
4616     total_ephemeral_size = min (total_ephemeral_size, planned_ephemeral_size);
4617 #endif //RESPECT_LARGE_ALIGNMENT
4618
4619 #ifdef SHORT_PLUGS
4620     total_ephemeral_size = Align ((size_t)((double)total_ephemeral_size * short_plugs_pad_ratio) + 1);
4621     total_ephemeral_size += Align (DESIRED_PLUG_LENGTH);
4622 #endif //SHORT_PLUGS
4623
4624     dprintf (3, ("total ephemeral size is %Ix, padding %Ix(%Ix)", 
4625         total_ephemeral_size,
4626         padding_size, (total_ephemeral_size - padding_size)));
4627 }
4628
4629 #ifdef _MSC_VER
4630 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
4631 #endif // _MSC_VER
4632
4633 heap_segment*
4634 gc_heap::soh_get_segment_to_expand()
4635 {
4636     size_t size = soh_segment_size;
4637
4638     ordered_plug_indices_init = FALSE;
4639     use_bestfit = FALSE;
4640
4641     //compute the size of the new ephemeral heap segment.
4642     compute_new_ephemeral_size();
4643
4644     if ((settings.pause_mode != pause_low_latency) &&
4645         (settings.pause_mode != pause_no_gc)
4646 #ifdef BACKGROUND_GC
4647         && (!recursive_gc_sync::background_running_p())
4648 #endif //BACKGROUND_GC
4649         )
4650     {
4651         allocator*  gen_alloc = ((settings.condemned_generation == max_generation) ? 0 :
4652                               generation_allocator (generation_of (max_generation)));
4653         dprintf (2, ("(gen%d)soh_get_segment_to_expand", settings.condemned_generation));
4654
4655         // try to find one in the gen 2 segment list, search backwards because the first segments
4656         // tend to be more compact than the later ones.
4657         heap_segment* fseg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
4658
4659         PREFIX_ASSUME(fseg != NULL);
4660
4661 #ifdef SEG_REUSE_STATS
4662         int try_reuse = 0;
4663 #endif //SEG_REUSE_STATS
4664
4665         heap_segment* seg = ephemeral_heap_segment;
4666         while ((seg = heap_segment_prev_rw (fseg, seg)) && (seg != fseg))
4667         {
4668 #ifdef SEG_REUSE_STATS
4669         try_reuse++;
4670 #endif //SEG_REUSE_STATS
4671
4672             if (can_expand_into_p (seg, size/3, total_ephemeral_size, gen_alloc))
4673             {
4674                 get_gc_data_per_heap()->set_mechanism (gc_heap_expand, 
4675                     (use_bestfit ? expand_reuse_bestfit : expand_reuse_normal));
4676                 if (settings.condemned_generation == max_generation)
4677                 {
4678                     if (use_bestfit)
4679                     {
4680                         build_ordered_free_spaces (seg);
4681                         dprintf (GTC_LOG, ("can use best fit"));
4682                     }
4683
4684 #ifdef SEG_REUSE_STATS
4685                     dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse", 
4686                         settings.condemned_generation, try_reuse));
4687 #endif //SEG_REUSE_STATS
4688                     dprintf (GTC_LOG, ("max_gen: Found existing segment to expand into %Ix", (size_t)seg));
4689                     return seg;
4690                 }
4691                 else
4692                 {
4693 #ifdef SEG_REUSE_STATS
4694                     dprintf (SEG_REUSE_LOG_0, ("(gen%d)soh_get_segment_to_expand: found seg #%d to reuse - returning", 
4695                         settings.condemned_generation, try_reuse));
4696 #endif //SEG_REUSE_STATS
4697                     dprintf (GTC_LOG, ("max_gen-1: Found existing segment to expand into %Ix", (size_t)seg));
4698
4699                     // If we return 0 here, the allocator will think since we are short on end
4700                     // of seg we neeed to trigger a full compacting GC. So if sustained low latency 
4701                     // is set we should acquire a new seg instead, that way we wouldn't be short.
4702                     // The real solution, of course, is to actually implement seg reuse in gen1.
4703                     if (settings.pause_mode != pause_sustained_low_latency)
4704                     {
4705                         dprintf (GTC_LOG, ("max_gen-1: SustainedLowLatency is set, acquire a new seg"));
4706                         get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_next_full_gc);
4707                         return 0;
4708                     }
4709                 }
4710             }
4711         }
4712     }
4713
4714     heap_segment* result = get_segment (size, FALSE);
4715
4716     if(result)
4717     {
4718 #ifdef BACKGROUND_GC
4719         if (current_c_gc_state == c_gc_state_planning)
4720         {
4721             // When we expand heap during bgc sweep, we set the seg to be swept so 
4722             // we'll always look at cards for objects on the new segment.
4723             result->flags |= heap_segment_flags_swept;
4724         }
4725 #endif //BACKGROUND_GC
4726
4727         FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(result),
4728                                   (size_t)(heap_segment_reserved (result) - heap_segment_mem(result)),
4729                                   gc_etw_segment_small_object_heap);
4730     }
4731
4732     get_gc_data_per_heap()->set_mechanism (gc_heap_expand, (result ? expand_new_seg : expand_no_memory));
4733
4734     if (result == 0)
4735     {
4736         dprintf (2, ("h%d: failed to allocate a new segment!", heap_number));
4737     }
4738     else
4739     {
4740 #ifdef MULTIPLE_HEAPS
4741         heap_segment_heap (result) = this;
4742 #endif //MULTIPLE_HEAPS
4743     }
4744
4745     dprintf (GTC_LOG, ("(gen%d)creating new segment %Ix", settings.condemned_generation, result));
4746     return result;
4747 }
4748
4749 #ifdef _MSC_VER
4750 #pragma warning(default:4706)
4751 #endif // _MSC_VER
4752
4753 //returns 0 in case of allocation failure
4754 heap_segment*
4755 gc_heap::get_segment (size_t size, BOOL loh_p)
4756 {
4757     if (heap_hard_limit)
4758         return NULL;
4759
4760     heap_segment* result = 0;
4761
4762     if (segment_standby_list != 0)
4763     {
4764         result = segment_standby_list;
4765         heap_segment* last = 0;
4766         while (result)
4767         {
4768             size_t hs = (size_t)(heap_segment_reserved (result) - (uint8_t*)result);
4769             if ((hs >= size) && ((hs / 2) < size))
4770             {
4771                 dprintf (2, ("Hoarded segment %Ix found", (size_t) result));
4772                 if (last)
4773                 {
4774                     heap_segment_next (last) = heap_segment_next (result);
4775                 }
4776                 else
4777                 {
4778                     segment_standby_list = heap_segment_next (result);
4779                 }
4780                 break;
4781             }
4782             else
4783             {
4784                 last = result;
4785                 result = heap_segment_next (result);
4786             }
4787         }
4788     }
4789
4790     if (result)
4791     {
4792         init_heap_segment (result);
4793 #ifdef BACKGROUND_GC
4794         if (should_commit_mark_array())
4795         {
4796             dprintf (GC_TABLE_LOG, ("hoarded seg %Ix, mark_array is %Ix", result, mark_array));
4797             if (!commit_mark_array_new_seg (__this, result))
4798             {
4799                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for hoarded seg"));
4800                 // If we can't use it we need to thread it back.
4801                 if (segment_standby_list != 0)
4802                 {
4803                     heap_segment_next (result) = segment_standby_list;
4804                     segment_standby_list = result;
4805                 }
4806                 else
4807                 {
4808                     segment_standby_list = result;
4809                 }
4810
4811                 result = 0;
4812             }
4813         }
4814 #endif //BACKGROUND_GC
4815
4816 #ifdef SEG_MAPPING_TABLE
4817         if (result)
4818             seg_mapping_table_add_segment (result, __this);
4819 #endif //SEG_MAPPING_TABLE
4820     }
4821
4822     if (!result)
4823     {
4824 #ifndef SEG_MAPPING_TABLE
4825         if (!seg_table->ensure_space_for_insert ())
4826             return 0;
4827 #endif //SEG_MAPPING_TABLE
4828         void* mem = virtual_alloc (size);
4829         if (!mem)
4830         {
4831             fgm_result.set_fgm (fgm_reserve_segment, size, loh_p);
4832             return 0;
4833         }
4834
4835         result = gc_heap::make_heap_segment ((uint8_t*)mem, size, heap_number);
4836
4837         if (result)
4838         {
4839             uint8_t* start;
4840             uint8_t* end;
4841             if (mem < g_gc_lowest_address)
4842             {
4843                 start =  (uint8_t*)mem;
4844             }
4845             else
4846             {
4847                 start = (uint8_t*)g_gc_lowest_address;
4848             }
4849
4850             if (((uint8_t*)mem + size) > g_gc_highest_address)
4851             {
4852                 end = (uint8_t*)mem + size;
4853             }
4854             else
4855             {
4856                 end = (uint8_t*)g_gc_highest_address;
4857             }
4858
4859             if (gc_heap::grow_brick_card_tables (start, end, size, result, __this, loh_p) != 0)
4860             {
4861                 virtual_free (mem, size);
4862                 return 0;
4863             }
4864         }
4865         else
4866         {
4867             fgm_result.set_fgm (fgm_commit_segment_beg, SEGMENT_INITIAL_COMMIT, loh_p);
4868             virtual_free (mem, size);
4869         }
4870
4871         if (result)
4872         {
4873 #ifdef SEG_MAPPING_TABLE
4874             seg_mapping_table_add_segment (result, __this);
4875 #else //SEG_MAPPING_TABLE
4876             gc_heap::seg_table->insert ((uint8_t*)result, delta);
4877 #endif //SEG_MAPPING_TABLE
4878         }
4879     }
4880
4881 #ifdef BACKGROUND_GC
4882     if (result)
4883     {
4884         ::record_changed_seg ((uint8_t*)result, heap_segment_reserved (result), 
4885                             settings.gc_index, current_bgc_state,
4886                             seg_added);
4887         bgc_verify_mark_array_cleared (result);
4888     }
4889 #endif //BACKGROUND_GC
4890
4891     dprintf (GC_TABLE_LOG, ("h%d: new seg: %Ix-%Ix (%Id)", heap_number, result, ((uint8_t*)result + size), size));
4892     return result;
4893 }
4894
4895 void release_segment (heap_segment* sg)
4896 {
4897     ptrdiff_t delta = 0;
4898     FIRE_EVENT(GCFreeSegment_V1, heap_segment_mem(sg));
4899     virtual_free (sg, (uint8_t*)heap_segment_reserved (sg)-(uint8_t*)sg);
4900 }
4901
4902 heap_segment* gc_heap::get_segment_for_loh (size_t size
4903 #ifdef MULTIPLE_HEAPS
4904                                            , gc_heap* hp
4905 #endif //MULTIPLE_HEAPS
4906                                            )
4907 {
4908 #ifndef MULTIPLE_HEAPS
4909     gc_heap* hp = 0;
4910 #endif //MULTIPLE_HEAPS
4911     heap_segment* res = hp->get_segment (size, TRUE);
4912     if (res != 0)
4913     {
4914 #ifdef MULTIPLE_HEAPS
4915         heap_segment_heap (res) = hp;
4916 #endif //MULTIPLE_HEAPS
4917         res->flags |= heap_segment_flags_loh;
4918
4919         FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(res), (size_t)(heap_segment_reserved (res) - heap_segment_mem(res)), gc_etw_segment_large_object_heap);
4920
4921         GCToEEInterface::DiagUpdateGenerationBounds();
4922
4923 #ifdef MULTIPLE_HEAPS
4924         hp->thread_loh_segment (res);
4925 #else
4926         thread_loh_segment (res);
4927 #endif //MULTIPLE_HEAPS
4928     }
4929
4930     return res;
4931 }
4932
4933 void gc_heap::thread_loh_segment (heap_segment* new_seg)
4934 {
4935     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
4936
4937     while (heap_segment_next_rw (seg))
4938         seg = heap_segment_next_rw (seg);
4939     heap_segment_next (seg) = new_seg;
4940 }
4941
4942 heap_segment*
4943 gc_heap::get_large_segment (size_t size, BOOL* did_full_compact_gc)
4944 {
4945     *did_full_compact_gc = FALSE;
4946     size_t last_full_compact_gc_count = get_full_compact_gc_count();
4947
4948     //access to get_segment needs to be serialized
4949     add_saved_spinlock_info (true, me_release, mt_get_large_seg);
4950     leave_spin_lock (&more_space_lock_loh);
4951     enter_spin_lock (&gc_heap::gc_lock);
4952     dprintf (SPINLOCK_LOG, ("[%d]Seg: Egc", heap_number));
4953     // if a GC happened between here and before we ask for a segment in 
4954     // get_large_segment, we need to count that GC.
4955     size_t current_full_compact_gc_count = get_full_compact_gc_count();
4956
4957     if (current_full_compact_gc_count > last_full_compact_gc_count)
4958     {
4959         *did_full_compact_gc = TRUE;
4960     }
4961
4962     heap_segment* res = get_segment_for_loh (size
4963 #ifdef MULTIPLE_HEAPS
4964                                             , this
4965 #endif //MULTIPLE_HEAPS
4966                                             );
4967
4968     dprintf (SPINLOCK_LOG, ("[%d]Seg: A Lgc", heap_number));
4969     leave_spin_lock (&gc_heap::gc_lock);
4970     enter_spin_lock (&more_space_lock_loh);
4971     add_saved_spinlock_info (true, me_acquire, mt_get_large_seg);
4972
4973     return res;
4974 }
4975
4976 #if 0
4977 BOOL gc_heap::unprotect_segment (heap_segment* seg)
4978 {
4979     uint8_t* start = align_lower_page (heap_segment_mem (seg));
4980     ptrdiff_t region_size = heap_segment_allocated (seg) - start;
4981
4982     if (region_size != 0 )
4983     {
4984         dprintf (3, ("unprotecting segment %Ix:", (size_t)seg));
4985
4986         BOOL status = GCToOSInterface::VirtualUnprotect (start, region_size);
4987         assert (status);
4988         return status;
4989     }
4990     return FALSE;
4991 }
4992 #endif
4993
4994 #ifdef MULTIPLE_HEAPS
4995 #ifdef _X86_
4996 #ifdef _MSC_VER
4997 #pragma warning(disable:4035)
4998     static ptrdiff_t  get_cycle_count()
4999     {
5000         __asm   rdtsc
5001     }
5002 #pragma warning(default:4035)
5003 #elif defined(__GNUC__)
5004     static ptrdiff_t  get_cycle_count()
5005     {
5006         ptrdiff_t cycles;
5007         ptrdiff_t cyclesHi;
5008         __asm__ __volatile__
5009         ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
5010         return cycles;
5011     }
5012 #else //_MSC_VER
5013 #error Unknown compiler
5014 #endif //_MSC_VER
5015 #elif defined(_TARGET_AMD64_) 
5016 #ifdef _MSC_VER
5017 extern "C" uint64_t __rdtsc();
5018 #pragma intrinsic(__rdtsc)
5019     static ptrdiff_t get_cycle_count()
5020     {
5021         return (ptrdiff_t)__rdtsc();
5022     }
5023 #elif defined(__GNUC__)
5024     static ptrdiff_t get_cycle_count()
5025     {
5026         ptrdiff_t cycles;
5027         ptrdiff_t cyclesHi;
5028         __asm__ __volatile__
5029         ("rdtsc":"=a" (cycles), "=d" (cyclesHi));
5030         return (cyclesHi << 32) | cycles;
5031     }
5032 #else // _MSC_VER
5033     extern "C" ptrdiff_t get_cycle_count(void);
5034 #endif // _MSC_VER
5035 #elif defined(_TARGET_ARM_)
5036     static ptrdiff_t get_cycle_count()
5037     {
5038         // @ARMTODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
5039         // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
5040         // all buffer access times being reported as equal in access_time().
5041         return 0;
5042     }
5043 #elif defined(_TARGET_ARM64_)
5044     static ptrdiff_t get_cycle_count()
5045     {
5046         // @ARM64TODO: cycle counter is not exposed to user mode by CoreARM. For now (until we can show this
5047         // makes a difference on the ARM configurations on which we'll run) just return 0. This will result in
5048         // all buffer access times being reported as equal in access_time().
5049         return 0;
5050     }
5051 #else
5052 #error NYI platform: get_cycle_count
5053 #endif //_TARGET_X86_
5054
5055 class heap_select
5056 {
5057     heap_select() {}
5058     static uint8_t* sniff_buffer;
5059     static unsigned n_sniff_buffers;
5060     static unsigned cur_sniff_index;
5061
5062     static uint16_t proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
5063     static uint16_t heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
5064     static uint16_t heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
5065     static uint16_t heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
5066     static uint16_t heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
5067     static uint16_t numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
5068
5069     static int access_time(uint8_t *sniff_buffer, int heap_number, unsigned sniff_index, unsigned n_sniff_buffers)
5070     {
5071         ptrdiff_t start_cycles = get_cycle_count();
5072         uint8_t sniff = sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE];
5073         assert (sniff == 0);
5074         ptrdiff_t elapsed_cycles = get_cycle_count() - start_cycles;
5075         // add sniff here just to defeat the optimizer
5076         elapsed_cycles += sniff;
5077         return (int) elapsed_cycles;
5078     }
5079
5080 public:
5081     static BOOL init(int n_heaps)
5082     {
5083         assert (sniff_buffer == NULL && n_sniff_buffers == 0);
5084         if (!GCToOSInterface::CanGetCurrentProcessorNumber())
5085         {
5086             n_sniff_buffers = n_heaps*2+1;
5087             size_t n_cache_lines = 1 + n_heaps * n_sniff_buffers + 1;
5088             size_t sniff_buf_size = n_cache_lines * HS_CACHE_LINE_SIZE;
5089             if (sniff_buf_size / HS_CACHE_LINE_SIZE != n_cache_lines) // check for overlow
5090             {
5091                 return FALSE;
5092             }
5093
5094             sniff_buffer = new (nothrow) uint8_t[sniff_buf_size];
5095             if (sniff_buffer == 0)
5096                 return FALSE;
5097             memset(sniff_buffer, 0, sniff_buf_size*sizeof(uint8_t));
5098         }
5099
5100         //can not enable gc numa aware, force all heaps to be in
5101         //one numa node by filling the array with all 0s
5102         if (!GCToOSInterface::CanEnableGCNumaAware())
5103             memset(heap_no_to_numa_node, 0, sizeof (heap_no_to_numa_node)); 
5104
5105         return TRUE;
5106     }
5107
5108     static void init_cpu_mapping(gc_heap * /*heap*/, int heap_number)
5109     {
5110         if (GCToOSInterface::CanGetCurrentProcessorNumber())
5111         {
5112             uint32_t proc_no = GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps;
5113             // We can safely cast heap_number to a uint16_t 'cause GetCurrentProcessCpuCount
5114             // only returns up to MAX_SUPPORTED_CPUS procs right now. We only ever create at most
5115             // MAX_SUPPORTED_CPUS GC threads.
5116             proc_no_to_heap_no[proc_no] = (uint16_t)heap_number;
5117         }
5118     }
5119
5120     static void mark_heap(int heap_number)
5121     {
5122         if (GCToOSInterface::CanGetCurrentProcessorNumber())
5123             return;
5124
5125         for (unsigned sniff_index = 0; sniff_index < n_sniff_buffers; sniff_index++)
5126             sniff_buffer[(1 + heap_number*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
5127     }
5128
5129     static int select_heap(alloc_context* acontext, int /*hint*/)
5130     {
5131         UNREFERENCED_PARAMETER(acontext); // only referenced by dprintf
5132
5133         if (GCToOSInterface::CanGetCurrentProcessorNumber())
5134             return proc_no_to_heap_no[GCToOSInterface::GetCurrentProcessorNumber() % gc_heap::n_heaps];
5135
5136         unsigned sniff_index = Interlocked::Increment(&cur_sniff_index);
5137         sniff_index %= n_sniff_buffers;
5138
5139         int best_heap = 0;
5140         int best_access_time = 1000*1000*1000;
5141         int second_best_access_time = best_access_time;
5142
5143         uint8_t *l_sniff_buffer = sniff_buffer;
5144         unsigned l_n_sniff_buffers = n_sniff_buffers;
5145         for (int heap_number = 0; heap_number < gc_heap::n_heaps; heap_number++)
5146         {
5147             int this_access_time = access_time(l_sniff_buffer, heap_number, sniff_index, l_n_sniff_buffers);
5148             if (this_access_time < best_access_time)
5149             {
5150                 second_best_access_time = best_access_time;
5151                 best_access_time = this_access_time;
5152                 best_heap = heap_number;
5153             }
5154             else if (this_access_time < second_best_access_time)
5155             {
5156                 second_best_access_time = this_access_time;
5157             }
5158         }
5159
5160         if (best_access_time*2 < second_best_access_time)
5161         {
5162             sniff_buffer[(1 + best_heap*n_sniff_buffers + sniff_index)*HS_CACHE_LINE_SIZE] &= 1;
5163
5164             dprintf (3, ("select_heap yields crisp %d for context %p\n", best_heap, (void *)acontext));
5165         }
5166         else
5167         {
5168             dprintf (3, ("select_heap yields vague %d for context %p\n", best_heap, (void *)acontext ));
5169         }
5170
5171         return best_heap;
5172     }
5173
5174     static bool can_find_heap_fast()
5175     {
5176         return GCToOSInterface::CanGetCurrentProcessorNumber();
5177     }
5178
5179     static uint16_t find_proc_no_from_heap_no(int heap_number)
5180     {
5181         return heap_no_to_proc_no[heap_number];
5182     }
5183
5184     static void set_proc_no_for_heap(int heap_number, uint16_t proc_no)
5185     {
5186         heap_no_to_proc_no[heap_number] = proc_no;
5187     }
5188
5189     static uint16_t find_numa_node_from_heap_no(int heap_number)
5190     {
5191         return heap_no_to_numa_node[heap_number];
5192     }
5193
5194     static void set_numa_node_for_heap(int heap_number, uint16_t numa_node)
5195     {
5196         heap_no_to_numa_node[heap_number] = numa_node;
5197     }
5198
5199     static uint16_t find_cpu_group_from_heap_no(int heap_number)
5200     {
5201         return heap_no_to_cpu_group[heap_number];
5202     }
5203
5204     static void set_cpu_group_for_heap(int heap_number, uint16_t group_number)
5205     {
5206         heap_no_to_cpu_group[heap_number] = group_number;
5207     }
5208
5209     static uint16_t find_group_proc_from_heap_no(int heap_number)
5210     {
5211         return heap_no_to_group_proc[heap_number];
5212     }
5213
5214     static void set_group_proc_for_heap(int heap_number, uint16_t group_proc)
5215     {
5216         heap_no_to_group_proc[heap_number] = group_proc;
5217     }
5218
5219     static void init_numa_node_to_heap_map(int nheaps)
5220     {   // called right after GCHeap::Init() for each heap is finished
5221         // when numa is not enabled, heap_no_to_numa_node[] are all filled
5222         // with 0s during initialization, and will be treated as one node
5223         numa_node_to_heap_map[0] = 0;
5224         int node_index = 1;
5225
5226         for (int i=1; i < nheaps; i++)
5227         {
5228             if (heap_no_to_numa_node[i] != heap_no_to_numa_node[i-1])
5229                 numa_node_to_heap_map[node_index++] = (uint16_t)i;
5230         }
5231         numa_node_to_heap_map[node_index] = (uint16_t)nheaps; //mark the end with nheaps
5232     }
5233
5234     static void get_heap_range_for_heap(int hn, int* start, int* end)
5235     {   // 1-tier/no numa case: heap_no_to_numa_node[] all zeros, 
5236         // and treated as in one node. thus: start=0, end=n_heaps
5237         uint16_t numa_node = heap_no_to_numa_node[hn];
5238         *start = (int)numa_node_to_heap_map[numa_node];
5239         *end   = (int)(numa_node_to_heap_map[numa_node+1]);
5240     }
5241 };
5242 uint8_t* heap_select::sniff_buffer;
5243 unsigned heap_select::n_sniff_buffers;
5244 unsigned heap_select::cur_sniff_index;
5245 uint16_t heap_select::proc_no_to_heap_no[MAX_SUPPORTED_CPUS];
5246 uint16_t heap_select::heap_no_to_proc_no[MAX_SUPPORTED_CPUS];
5247 uint16_t heap_select::heap_no_to_numa_node[MAX_SUPPORTED_CPUS];
5248 uint16_t heap_select::heap_no_to_cpu_group[MAX_SUPPORTED_CPUS];
5249 uint16_t heap_select::heap_no_to_group_proc[MAX_SUPPORTED_CPUS];
5250 uint16_t heap_select::numa_node_to_heap_map[MAX_SUPPORTED_CPUS+4];
5251
5252 BOOL gc_heap::create_thread_support (unsigned number_of_heaps)
5253 {
5254     BOOL ret = FALSE;
5255     if (!gc_start_event.CreateOSManualEventNoThrow (FALSE))
5256     {
5257         goto cleanup;
5258     }
5259     if (!ee_suspend_event.CreateOSAutoEventNoThrow (FALSE))
5260     {
5261         goto cleanup;
5262     }
5263     if (!gc_t_join.init (number_of_heaps, join_flavor_server_gc))
5264     {
5265         goto cleanup;
5266     }
5267
5268     ret = TRUE;
5269
5270 cleanup:
5271
5272     if (!ret)
5273     {
5274         destroy_thread_support();
5275     }
5276
5277     return ret;
5278 }
5279
5280 void gc_heap::destroy_thread_support ()
5281 {
5282     if (ee_suspend_event.IsValid())
5283     {
5284         ee_suspend_event.CloseEvent();
5285     }
5286     if (gc_start_event.IsValid())
5287     {
5288         gc_start_event.CloseEvent();
5289     }
5290 }
5291
5292 void set_thread_group_affinity_for_heap(int heap_number, GCThreadAffinity* affinity)
5293 {
5294     affinity->Group = GCThreadAffinity::None;
5295     affinity->Processor = GCThreadAffinity::None;
5296
5297     uint16_t gn, gpn;
5298     GCToOSInterface::GetGroupForProcessor((uint16_t)heap_number, &gn, &gpn);
5299
5300     int bit_number = 0;
5301     for (uintptr_t mask = 1; mask !=0; mask <<=1) 
5302     {
5303         if (bit_number == gpn)
5304         {
5305             dprintf(3, ("using processor group %d, mask %Ix for heap %d\n", gn, mask, heap_number));
5306             affinity->Processor = gpn;
5307             affinity->Group = gn;
5308             heap_select::set_cpu_group_for_heap(heap_number, gn);
5309             heap_select::set_group_proc_for_heap(heap_number, gpn);
5310             if (GCToOSInterface::CanEnableGCNumaAware())
5311             {  
5312                 PROCESSOR_NUMBER proc_no;
5313                 proc_no.Group    = gn;
5314                 proc_no.Number   = (uint8_t)gpn;
5315                 proc_no.Reserved = 0;
5316
5317                 uint16_t node_no = 0;
5318                 if (GCToOSInterface::GetNumaProcessorNode(&proc_no, &node_no))
5319                     heap_select::set_numa_node_for_heap(heap_number, node_no);
5320             }
5321             else
5322             {   // no numa setting, each cpu group is treated as a node
5323                 heap_select::set_numa_node_for_heap(heap_number, gn);
5324             }
5325             return;
5326         }
5327         bit_number++;
5328     }
5329 }
5330
5331 void set_thread_affinity_mask_for_heap(int heap_number, GCThreadAffinity* affinity)
5332 {
5333     affinity->Group = GCThreadAffinity::None;
5334     affinity->Processor = GCThreadAffinity::None;
5335
5336     uintptr_t pmask = process_mask;
5337     int bit_number = 0; 
5338     uint8_t proc_number = 0;
5339     for (uintptr_t mask = 1; mask != 0; mask <<= 1)
5340     {
5341         if ((mask & pmask) != 0)
5342         {
5343             if (bit_number == heap_number)
5344             {
5345                 dprintf (3, ("Using processor %d for heap %d", proc_number, heap_number));
5346                 affinity->Processor = proc_number;
5347                 heap_select::set_proc_no_for_heap(heap_number, proc_number);
5348                 if (GCToOSInterface::CanEnableGCNumaAware())
5349                 {
5350                     uint16_t node_no = 0;
5351                     PROCESSOR_NUMBER proc_no;
5352                     proc_no.Group = 0;
5353                     proc_no.Number = (uint8_t)proc_number;
5354                     proc_no.Reserved = 0;
5355                     if (GCToOSInterface::GetNumaProcessorNode(&proc_no, &node_no))
5356                     {
5357                         heap_select::set_numa_node_for_heap(heap_number, node_no);
5358                     }
5359                 }
5360                 return;
5361             }
5362             bit_number++;
5363         }
5364         proc_number++;
5365     }
5366 }
5367
5368 bool gc_heap::create_gc_thread ()
5369 {
5370     dprintf (3, ("Creating gc thread\n"));
5371     return GCToEEInterface::CreateThread(gc_thread_stub, this, false, ".NET Server GC");
5372 }
5373
5374 #ifdef _MSC_VER
5375 #pragma warning(disable:4715) //IA64 xcompiler recognizes that without the 'break;' the while(1) will never end and therefore not return a value for that code path
5376 #endif //_MSC_VER
5377 void gc_heap::gc_thread_function ()
5378 {
5379     assert (gc_done_event.IsValid());
5380     assert (gc_start_event.IsValid());
5381     dprintf (3, ("gc thread started"));
5382
5383     heap_select::init_cpu_mapping(this, heap_number);
5384
5385     while (1)
5386     {
5387         assert (!gc_t_join.joined());
5388
5389         if (heap_number == 0)
5390         {
5391             gc_heap::ee_suspend_event.Wait(INFINITE, FALSE);
5392
5393             BEGIN_TIMING(suspend_ee_during_log);
5394             GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
5395             END_TIMING(suspend_ee_during_log);
5396
5397             proceed_with_gc_p = TRUE;
5398
5399             if (!should_proceed_with_gc())
5400             {
5401                 update_collection_counts_for_no_gc();
5402                 proceed_with_gc_p = FALSE;
5403             }
5404             else
5405             {
5406                 settings.init_mechanisms();
5407                 gc_start_event.Set();
5408             }
5409             dprintf (3, ("%d gc thread waiting...", heap_number));
5410         }
5411         else
5412         {
5413             gc_start_event.Wait(INFINITE, FALSE);
5414             dprintf (3, ("%d gc thread waiting... Done", heap_number));
5415         }
5416
5417         assert ((heap_number == 0) || proceed_with_gc_p);
5418
5419         if (proceed_with_gc_p)
5420         {
5421             garbage_collect (GCHeap::GcCondemnedGeneration);
5422
5423             if (pm_trigger_full_gc)
5424             {
5425                 garbage_collect_pm_full_gc();
5426             }
5427         }
5428
5429         if (heap_number == 0)
5430         {
5431             if (proceed_with_gc_p && (!settings.concurrent))
5432             {
5433                 do_post_gc();
5434             }
5435
5436 #ifdef BACKGROUND_GC
5437             recover_bgc_settings();
5438 #endif //BACKGROUND_GC
5439
5440 #ifdef MULTIPLE_HEAPS
5441             for (int i = 0; i < gc_heap::n_heaps; i++)
5442             {
5443                 gc_heap* hp = gc_heap::g_heaps[i];
5444                 hp->add_saved_spinlock_info (false, me_release, mt_block_gc);
5445                 leave_spin_lock(&hp->more_space_lock_soh);
5446             }
5447 #endif //MULTIPLE_HEAPS
5448
5449             gc_heap::gc_started = FALSE;
5450
5451             BEGIN_TIMING(restart_ee_during_log);
5452             GCToEEInterface::RestartEE(TRUE);
5453             END_TIMING(restart_ee_during_log);
5454             process_sync_log_stats();
5455
5456             dprintf (SPINLOCK_LOG, ("GC Lgc"));
5457             leave_spin_lock (&gc_heap::gc_lock);
5458
5459             gc_heap::internal_gc_done = true;
5460
5461             if (proceed_with_gc_p)
5462                 set_gc_done();
5463             else
5464             {
5465                 // If we didn't actually do a GC, it means we didn't wait up the other threads,
5466                 // we still need to set the gc_done_event for those threads.
5467                 for (int i = 0; i < gc_heap::n_heaps; i++)
5468                 {
5469                     gc_heap* hp = gc_heap::g_heaps[i];
5470                     hp->set_gc_done();
5471                 }
5472             }
5473         }
5474         else
5475         {
5476             int spin_count = 32 * (gc_heap::n_heaps - 1);
5477
5478             // wait until RestartEE has progressed to a stage where we can restart user threads
5479             while (!gc_heap::internal_gc_done && !GCHeap::SafeToRestartManagedThreads())
5480             {
5481                 spin_and_switch (spin_count, (gc_heap::internal_gc_done || GCHeap::SafeToRestartManagedThreads()));
5482             }
5483             set_gc_done();
5484         }
5485     }
5486 }
5487 #ifdef _MSC_VER
5488 #pragma warning(default:4715) //IA64 xcompiler recognizes that without the 'break;' the while(1) will never end and therefore not return a value for that code path
5489 #endif //_MSC_VER
5490
5491 #endif //MULTIPLE_HEAPS
5492
5493 bool gc_heap::virtual_alloc_commit_for_heap (void* addr, size_t size, int h_number)
5494 {
5495 #if defined(MULTIPLE_HEAPS) && !defined(FEATURE_REDHAWK)
5496     // Currently there is no way for us to specific the numa node to allocate on via hosting interfaces to
5497     // a host. This will need to be added later.
5498 #if !defined(FEATURE_CORECLR) && !defined(BUILD_AS_STANDALONE)
5499     if (!CLRMemoryHosted())
5500 #endif
5501     {
5502         if (GCToOSInterface::CanEnableGCNumaAware())
5503         {
5504             uint32_t numa_node = heap_select::find_numa_node_from_heap_no(h_number);
5505             if (GCToOSInterface::VirtualCommit(addr, size, numa_node))
5506                 return true;
5507         }
5508     }
5509 #else
5510     UNREFERENCED_PARAMETER(h_number);
5511 #endif
5512
5513     //numa aware not enabled, or call failed --> fallback to VirtualCommit()
5514     return GCToOSInterface::VirtualCommit(addr, size);
5515 }
5516
5517 bool gc_heap::virtual_commit (void* address, size_t size, int h_number, bool* hard_limit_exceeded_p)
5518 {
5519 #ifndef BIT64
5520     assert (heap_hard_limit == 0);
5521 #endif //!BIT64
5522
5523     if (heap_hard_limit)
5524     {
5525         bool exceeded_p = false;
5526
5527         check_commit_cs.Enter();
5528
5529         if ((current_total_committed + size) > heap_hard_limit)
5530         {
5531             dprintf (1, ("%Id + %Id = %Id > limit",
5532                 current_total_committed, size,
5533                 (current_total_committed + size),
5534                 heap_hard_limit));
5535
5536             exceeded_p = true;
5537         }
5538         else
5539         {
5540             current_total_committed += size;
5541             if (h_number < 0)
5542                 current_total_committed_bookkeeping += size;
5543         }
5544
5545         check_commit_cs.Leave();
5546
5547         if (hard_limit_exceeded_p)
5548             *hard_limit_exceeded_p = exceeded_p;
5549
5550         if (exceeded_p)
5551         {
5552             dprintf (1, ("can't commit %Ix for %Id bytes > HARD LIMIT %Id", (size_t)address, size, heap_hard_limit));
5553             return false;
5554         }
5555     }
5556
5557     // If it's a valid heap number it means it's commiting for memory on the GC heap.
5558     bool commit_succeeded_p = ((h_number >= 0) ? 
5559         virtual_alloc_commit_for_heap (address, size, h_number) : 
5560         GCToOSInterface::VirtualCommit(address, size));
5561
5562     if (!commit_succeeded_p && heap_hard_limit)
5563     {
5564         check_commit_cs.Enter();
5565         dprintf (1, ("commit failed, updating %Id to %Id",
5566                 current_total_committed, (current_total_committed - size)));
5567         current_total_committed -= size;
5568         if (h_number < 0)
5569             current_total_committed_bookkeeping -= size;
5570
5571         check_commit_cs.Leave();
5572     }
5573
5574     return commit_succeeded_p;
5575 }
5576
5577 bool gc_heap::virtual_decommit (void* address, size_t size, int h_number)
5578 {
5579 #ifndef BIT64
5580     assert (heap_hard_limit == 0);
5581 #endif //!BIT64
5582
5583     bool decommit_succeeded_p = GCToOSInterface::VirtualDecommit (address, size);
5584
5585     if (decommit_succeeded_p && heap_hard_limit)
5586     {
5587         check_commit_cs.Enter();
5588         current_total_committed -= size;
5589         if (h_number < 0)
5590             current_total_committed_bookkeeping -= size;
5591         check_commit_cs.Leave();
5592     }
5593
5594     return decommit_succeeded_p;
5595 }
5596
5597 #ifndef SEG_MAPPING_TABLE
5598 inline
5599 heap_segment* gc_heap::segment_of (uint8_t* add, ptrdiff_t& delta, BOOL verify_p)
5600 {
5601     uint8_t* sadd = add;
5602     heap_segment* hs = 0;
5603     heap_segment* hs1 = 0;
5604     if (!((add >= g_gc_lowest_address) && (add < g_gc_highest_address)))
5605     {
5606         delta = 0;
5607         return 0;
5608     }
5609     //repeat in case there is a concurrent insertion in the table.
5610     do
5611     {
5612         hs = hs1;
5613         sadd = add;
5614         seg_table->lookup (sadd);
5615         hs1 = (heap_segment*)sadd;
5616     } while (hs1 && !in_range_for_segment (add, hs1) && (hs != hs1));
5617
5618     hs = hs1;
5619
5620     if ((hs == 0) ||
5621         (verify_p && (add > heap_segment_reserved ((heap_segment*)(sadd + delta)))))
5622         delta = 0;
5623     return hs;
5624 }
5625 #endif //SEG_MAPPING_TABLE
5626
5627 class mark
5628 {
5629 public:
5630     uint8_t* first;
5631     size_t len;
5632
5633     // If we want to save space we can have a pool of plug_and_gap's instead of 
5634     // always having 2 allocated for each pinned plug.
5635     gap_reloc_pair saved_pre_plug;
5636     // If we decide to not compact, we need to restore the original values.
5637     gap_reloc_pair saved_pre_plug_reloc;
5638
5639     gap_reloc_pair saved_post_plug;
5640
5641     // Supposedly Pinned objects cannot have references but we are seeing some from pinvoke 
5642     // frames. Also if it's an artificially pinned plug created by us, it can certainly 
5643     // have references. 
5644     // We know these cases will be rare so we can optimize this to be only allocated on decommand. 
5645     gap_reloc_pair saved_post_plug_reloc;
5646
5647     // We need to calculate this after we are done with plan phase and before compact
5648     // phase because compact phase will change the bricks so relocate_address will no 
5649     // longer work.
5650     uint8_t* saved_pre_plug_info_reloc_start;
5651
5652     // We need to save this because we will have no way to calculate it, unlike the 
5653     // pre plug info start which is right before this plug.
5654     uint8_t* saved_post_plug_info_start;
5655
5656 #ifdef SHORT_PLUGS
5657     uint8_t* allocation_context_start_region;
5658 #endif //SHORT_PLUGS
5659
5660     // How the bits in these bytes are organized:
5661     // MSB --> LSB
5662     // bit to indicate whether it's a short obj | 3 bits for refs in this short obj | 2 unused bits | bit to indicate if it's collectible | last bit
5663     // last bit indicates if there's pre or post info associated with this plug. If it's not set all other bits will be 0.
5664     BOOL saved_pre_p;
5665     BOOL saved_post_p;
5666
5667 #ifdef _DEBUG
5668     // We are seeing this is getting corrupted for a PP with a NP after.
5669     // Save it when we first set it and make sure it doesn't change.
5670     gap_reloc_pair saved_post_plug_debug;
5671 #endif //_DEBUG
5672
5673     size_t get_max_short_bits()
5674     {
5675         return (sizeof (gap_reloc_pair) / sizeof (uint8_t*));
5676     }
5677
5678     // pre bits
5679     size_t get_pre_short_start_bit ()
5680     {
5681         return (sizeof (saved_pre_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*)));
5682     }
5683
5684     BOOL pre_short_p()
5685     {
5686         return (saved_pre_p & (1 << (sizeof (saved_pre_p) * 8 - 1)));
5687     }
5688
5689     void set_pre_short()
5690     {
5691         saved_pre_p |= (1 << (sizeof (saved_pre_p) * 8 - 1));
5692     }
5693
5694     void set_pre_short_bit (size_t bit)
5695     {
5696         saved_pre_p |= 1 << (get_pre_short_start_bit() + bit);
5697     }
5698
5699     BOOL pre_short_bit_p (size_t bit)
5700     {
5701         return (saved_pre_p & (1 << (get_pre_short_start_bit() + bit)));
5702     }
5703
5704 #ifdef COLLECTIBLE_CLASS
5705     void set_pre_short_collectible()
5706     {
5707         saved_pre_p |= 2;
5708     }
5709
5710     BOOL pre_short_collectible_p()
5711     {
5712         return (saved_pre_p & 2);
5713     }
5714 #endif //COLLECTIBLE_CLASS
5715
5716     // post bits
5717     size_t get_post_short_start_bit ()
5718     {
5719         return (sizeof (saved_post_p) * 8 - 1 - (sizeof (gap_reloc_pair) / sizeof (uint8_t*)));
5720     }
5721
5722     BOOL post_short_p()
5723     {
5724         return (saved_post_p & (1 << (sizeof (saved_post_p) * 8 - 1)));
5725     }
5726
5727     void set_post_short()
5728     {
5729         saved_post_p |= (1 << (sizeof (saved_post_p) * 8 - 1));
5730     }
5731
5732     void set_post_short_bit (size_t bit)
5733     {
5734         saved_post_p |= 1 << (get_post_short_start_bit() + bit);
5735     }
5736
5737     BOOL post_short_bit_p (size_t bit)
5738     {
5739         return (saved_post_p & (1 << (get_post_short_start_bit() + bit)));
5740     }
5741
5742 #ifdef COLLECTIBLE_CLASS
5743     void set_post_short_collectible()
5744     {
5745         saved_post_p |= 2;
5746     }
5747
5748     BOOL post_short_collectible_p()
5749     {
5750         return (saved_post_p & 2);
5751     }
5752 #endif //COLLECTIBLE_CLASS
5753
5754     uint8_t* get_plug_address() { return first; }
5755
5756     BOOL has_pre_plug_info() { return saved_pre_p; }
5757     BOOL has_post_plug_info() { return saved_post_p; }
5758
5759     gap_reloc_pair* get_pre_plug_reloc_info() { return &saved_pre_plug_reloc; }
5760     gap_reloc_pair* get_post_plug_reloc_info() { return &saved_post_plug_reloc; }
5761     void set_pre_plug_info_reloc_start (uint8_t* reloc) { saved_pre_plug_info_reloc_start = reloc; }
5762     uint8_t* get_post_plug_info_start() { return saved_post_plug_info_start; }
5763
5764     // We need to temporarily recover the shortened plugs for compact phase so we can
5765     // copy over the whole plug and their related info (mark bits/cards). But we will
5766     // need to set the artificial gap back so compact phase can keep reading the plug info.
5767     // We also need to recover the saved info because we'll need to recover it later.
5768     // 
5769     // So we would call swap_p*_plug_and_saved once to recover the object info; then call 
5770     // it again to recover the artificial gap.
5771     void swap_pre_plug_and_saved()
5772     {
5773         gap_reloc_pair temp;
5774         memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5775         memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5776         saved_pre_plug_reloc = temp;
5777     }
5778
5779     void swap_post_plug_and_saved()
5780     {
5781         gap_reloc_pair temp;
5782         memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5783         memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5784         saved_post_plug_reloc = temp;
5785     }
5786
5787     void swap_pre_plug_and_saved_for_profiler()
5788     {
5789         gap_reloc_pair temp;
5790         memcpy (&temp, (first - sizeof (plug_and_gap)), sizeof (temp));
5791         memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5792         saved_pre_plug = temp;
5793     }
5794
5795     void swap_post_plug_and_saved_for_profiler()
5796     {
5797         gap_reloc_pair temp;
5798         memcpy (&temp, saved_post_plug_info_start, sizeof (temp));
5799         memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5800         saved_post_plug = temp;
5801     }
5802
5803     // We should think about whether it's really necessary to have to copy back the pre plug
5804     // info since it was already copied during compacting plugs. But if a plug doesn't move
5805     // by >= 3 ptr size (the size of gap_reloc_pair), it means we'd have to recover pre plug info.
5806     void recover_plug_info() 
5807     {
5808         if (saved_pre_p)
5809         {
5810             if (gc_heap::settings.compaction)
5811             {
5812                 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix", 
5813                     first,
5814                     &saved_pre_plug_reloc, 
5815                     saved_pre_plug_info_reloc_start));
5816                 memcpy (saved_pre_plug_info_reloc_start, &saved_pre_plug_reloc, sizeof (saved_pre_plug_reloc));
5817             }
5818             else
5819             {
5820                 dprintf (3, ("%Ix: REC Pre: %Ix-%Ix", 
5821                     first,
5822                     &saved_pre_plug, 
5823                     (first - sizeof (plug_and_gap))));
5824                 memcpy ((first - sizeof (plug_and_gap)), &saved_pre_plug, sizeof (saved_pre_plug));
5825             }
5826         }
5827
5828         if (saved_post_p)
5829         {
5830             if (gc_heap::settings.compaction)
5831             {
5832                 dprintf (3, ("%Ix: REC Post: %Ix-%Ix", 
5833                     first,
5834                     &saved_post_plug_reloc, 
5835                     saved_post_plug_info_start));
5836                 memcpy (saved_post_plug_info_start, &saved_post_plug_reloc, sizeof (saved_post_plug_reloc));
5837             }
5838             else
5839             {
5840                 dprintf (3, ("%Ix: REC Post: %Ix-%Ix", 
5841                     first,
5842                     &saved_post_plug, 
5843                     saved_post_plug_info_start));
5844                 memcpy (saved_post_plug_info_start, &saved_post_plug, sizeof (saved_post_plug));
5845             }
5846         }
5847     }
5848 };
5849
5850
5851 void gc_mechanisms::init_mechanisms()
5852 {
5853     condemned_generation = 0;
5854     promotion = FALSE;//TRUE;
5855     compaction = TRUE;
5856 #ifdef FEATURE_LOH_COMPACTION
5857     loh_compaction = gc_heap::should_compact_loh();
5858 #else
5859     loh_compaction = FALSE;
5860 #endif //FEATURE_LOH_COMPACTION
5861     heap_expansion = FALSE;
5862     concurrent = FALSE;
5863     demotion = FALSE;
5864     elevation_reduced = FALSE;
5865     found_finalizers = FALSE;
5866 #ifdef BACKGROUND_GC
5867     background_p = recursive_gc_sync::background_running_p() != FALSE;
5868     allocations_allowed = TRUE;
5869 #endif //BACKGROUND_GC
5870
5871     entry_memory_load = 0;
5872     exit_memory_load = 0;
5873
5874 #ifdef STRESS_HEAP
5875     stress_induced = FALSE;
5876 #endif // STRESS_HEAP
5877 }
5878
5879 void gc_mechanisms::first_init()
5880 {
5881     gc_index = 0;
5882     gen0_reduction_count = 0;
5883     should_lock_elevation = FALSE;
5884     elevation_locked_count = 0;
5885     reason = reason_empty;
5886 #ifdef BACKGROUND_GC
5887     pause_mode = gc_heap::gc_can_use_concurrent ? pause_interactive : pause_batch;
5888 #ifdef _DEBUG
5889     int debug_pause_mode = static_cast<int>(GCConfig::GetLatencyMode());
5890     if (debug_pause_mode >= 0)
5891     {
5892         assert (debug_pause_mode <= pause_sustained_low_latency);
5893         pause_mode = (gc_pause_mode)debug_pause_mode;
5894     }
5895 #endif //_DEBUG
5896 #else //BACKGROUND_GC
5897     pause_mode = pause_batch;
5898 #endif //BACKGROUND_GC
5899
5900     init_mechanisms();
5901 }
5902
5903 void gc_mechanisms::record (gc_history_global* history)
5904 {
5905 #ifdef MULTIPLE_HEAPS
5906     history->num_heaps = gc_heap::n_heaps;
5907 #else
5908     history->num_heaps = 1;
5909 #endif //MULTIPLE_HEAPS
5910
5911     history->condemned_generation = condemned_generation;
5912     history->gen0_reduction_count = gen0_reduction_count;
5913     history->reason = reason;
5914     history->pause_mode = (int)pause_mode;
5915     history->mem_pressure = entry_memory_load;
5916     history->global_mechanims_p = 0;
5917
5918     // start setting the boolean values.
5919     if (concurrent)
5920         history->set_mechanism_p (global_concurrent);
5921     
5922     if (compaction)
5923         history->set_mechanism_p (global_compaction);
5924
5925     if (promotion)
5926         history->set_mechanism_p (global_promotion);
5927     
5928     if (demotion)
5929         history->set_mechanism_p (global_demotion);
5930
5931     if (card_bundles)
5932         history->set_mechanism_p (global_card_bundles);
5933
5934     if (elevation_reduced)
5935         history->set_mechanism_p (global_elevation);
5936 }
5937
5938 /**********************************
5939    called at the beginning of GC to fix the allocated size to
5940    what is really allocated, or to turn the free area into an unused object
5941    It needs to be called after all of the other allocation contexts have been
5942    fixed since it relies on alloc_allocated.
5943  ********************************/
5944
5945 //for_gc_p indicates that the work is being done for GC,
5946 //as opposed to concurrent heap verification
5947 void gc_heap::fix_youngest_allocation_area (BOOL for_gc_p)
5948 {
5949     UNREFERENCED_PARAMETER(for_gc_p);
5950
5951     // The gen 0 alloc context is never used for allocation in the allocator path. It's
5952     // still used in the allocation path during GCs.
5953     assert (generation_allocation_pointer (youngest_generation) == nullptr);
5954     assert (generation_allocation_limit (youngest_generation) == nullptr);
5955     heap_segment_allocated (ephemeral_heap_segment) = alloc_allocated;
5956 }
5957
5958 void gc_heap::fix_large_allocation_area (BOOL for_gc_p)
5959 {
5960     UNREFERENCED_PARAMETER(for_gc_p);
5961
5962 #ifdef _DEBUG
5963     alloc_context* acontext = 
5964 #endif // _DEBUG
5965         generation_alloc_context (large_object_generation);
5966     assert (acontext->alloc_ptr == 0);
5967     assert (acontext->alloc_limit == 0); 
5968 #if 0
5969     dprintf (3, ("Large object alloc context: ptr: %Ix, limit %Ix",
5970                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5971     fix_allocation_context (acontext, FALSE, get_alignment_constant (FALSE));
5972     if (for_gc_p)
5973     {
5974         acontext->alloc_ptr = 0;
5975         acontext->alloc_limit = acontext->alloc_ptr;
5976     }
5977 #endif //0
5978 }
5979
5980 //for_gc_p indicates that the work is being done for GC,
5981 //as opposed to concurrent heap verification
5982 void gc_heap::fix_allocation_context (alloc_context* acontext, BOOL for_gc_p,
5983                                       int align_const)
5984 {
5985     dprintf (3, ("Fixing allocation context %Ix: ptr: %Ix, limit: %Ix",
5986                  (size_t)acontext,
5987                  (size_t)acontext->alloc_ptr, (size_t)acontext->alloc_limit));
5988
5989     if (((size_t)(alloc_allocated - acontext->alloc_limit) > Align (min_obj_size, align_const)) ||
5990         !for_gc_p)
5991     {
5992         uint8_t*  point = acontext->alloc_ptr;
5993         if (point != 0)
5994         {
5995             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
5996             // the allocation area was from the free list
5997             // it was shortened by Align (min_obj_size) to make room for
5998             // at least the shortest unused object
5999             size += Align (min_obj_size, align_const);
6000             assert ((size >= Align (min_obj_size)));
6001
6002             dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point,
6003                        (size_t)point + size ));
6004             make_unused_array (point, size);
6005
6006             if (for_gc_p)
6007             {
6008                 generation_free_obj_space (generation_of (0)) += size;
6009                 alloc_contexts_used ++;
6010             }
6011         }
6012     }
6013     else if (for_gc_p)
6014     {
6015         alloc_allocated = acontext->alloc_ptr;
6016         assert (heap_segment_allocated (ephemeral_heap_segment) <=
6017                 heap_segment_committed (ephemeral_heap_segment));
6018         alloc_contexts_used ++;
6019     }
6020
6021     if (for_gc_p)
6022     {
6023         // We need to update the alloc_bytes to reflect the portion that we have not used  
6024         acontext->alloc_bytes -= (acontext->alloc_limit - acontext->alloc_ptr);  
6025         acontext->alloc_ptr = 0;
6026         acontext->alloc_limit = acontext->alloc_ptr;
6027     }
6028 }
6029
6030 //used by the heap verification for concurrent gc.
6031 //it nulls out the words set by fix_allocation_context for heap_verification
6032 void repair_allocation (gc_alloc_context* acontext, void*)
6033 {
6034     uint8_t*  point = acontext->alloc_ptr;
6035
6036     if (point != 0)
6037     {
6038         dprintf (3, ("Clearing [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
6039                      (size_t)acontext->alloc_limit+Align(min_obj_size)));
6040         memclr (acontext->alloc_ptr - plug_skew,
6041                 (acontext->alloc_limit - acontext->alloc_ptr)+Align (min_obj_size));
6042     }
6043 }
6044
6045 void void_allocation (gc_alloc_context* acontext, void*)
6046 {
6047     uint8_t*  point = acontext->alloc_ptr;
6048
6049     if (point != 0)
6050     {
6051         dprintf (3, ("Void [%Ix, %Ix[", (size_t)acontext->alloc_ptr,
6052                      (size_t)acontext->alloc_limit+Align(min_obj_size)));
6053         acontext->alloc_ptr = 0;
6054         acontext->alloc_limit = acontext->alloc_ptr;
6055     }
6056 }
6057
6058 void gc_heap::repair_allocation_contexts (BOOL repair_p)
6059 {
6060     GCToEEInterface::GcEnumAllocContexts (repair_p ? repair_allocation : void_allocation, NULL);
6061 }
6062
6063 struct fix_alloc_context_args
6064 {
6065     BOOL for_gc_p;
6066     void* heap;
6067 };
6068
6069 void fix_alloc_context (gc_alloc_context* acontext, void* param)
6070 {
6071     fix_alloc_context_args* args = (fix_alloc_context_args*)param;
6072     g_theGCHeap->FixAllocContext(acontext, (void*)(size_t)(args->for_gc_p), args->heap);
6073 }
6074
6075 void gc_heap::fix_allocation_contexts (BOOL for_gc_p)
6076 {
6077     fix_alloc_context_args args;
6078     args.for_gc_p = for_gc_p;
6079     args.heap = __this;
6080
6081     GCToEEInterface::GcEnumAllocContexts(fix_alloc_context, &args);
6082     fix_youngest_allocation_area(for_gc_p);
6083     fix_large_allocation_area(for_gc_p);
6084 }
6085
6086 void gc_heap::fix_older_allocation_area (generation* older_gen)
6087 {
6088     heap_segment* older_gen_seg = generation_allocation_segment (older_gen);
6089     if (generation_allocation_limit (older_gen) !=
6090         heap_segment_plan_allocated (older_gen_seg))
6091     {
6092         uint8_t*  point = generation_allocation_pointer (older_gen);
6093
6094         size_t  size = (generation_allocation_limit (older_gen) -
6095                                generation_allocation_pointer (older_gen));
6096         if (size != 0)
6097         {
6098             assert ((size >= Align (min_obj_size)));
6099             dprintf(3,("Making unused area [%Ix, %Ix[", (size_t)point, (size_t)point+size));
6100             make_unused_array (point, size);
6101             if (size >= min_free_list)
6102             {
6103                 generation_allocator (older_gen)->thread_item_front (point, size);
6104                 add_gen_free (older_gen->gen_num, size);
6105                 generation_free_list_space (older_gen) += size;
6106             }
6107             else
6108             {
6109                 generation_free_obj_space (older_gen) += size;
6110             }
6111         }
6112     }
6113     else
6114     {
6115         assert (older_gen_seg != ephemeral_heap_segment);
6116         heap_segment_plan_allocated (older_gen_seg) =
6117             generation_allocation_pointer (older_gen);
6118         generation_allocation_limit (older_gen) =
6119             generation_allocation_pointer (older_gen);
6120     }
6121
6122     generation_allocation_pointer (older_gen) = 0;
6123     generation_allocation_limit (older_gen) = 0;
6124 }
6125
6126 void gc_heap::set_allocation_heap_segment (generation* gen)
6127 {
6128     uint8_t* p = generation_allocation_start (gen);
6129     assert (p);
6130     heap_segment* seg = generation_allocation_segment (gen);
6131     if (in_range_for_segment (p, seg))
6132         return;
6133
6134     // try ephemeral heap segment in case of heap expansion
6135     seg = ephemeral_heap_segment;
6136     if (!in_range_for_segment (p, seg))
6137     {
6138         seg = heap_segment_rw (generation_start_segment (gen));
6139
6140         PREFIX_ASSUME(seg != NULL);
6141
6142         while (!in_range_for_segment (p, seg))
6143         {
6144             seg = heap_segment_next_rw (seg);
6145             PREFIX_ASSUME(seg != NULL);
6146         }
6147     }
6148
6149     generation_allocation_segment (gen) = seg;
6150 }
6151
6152 void gc_heap::reset_allocation_pointers (generation* gen, uint8_t* start)
6153 {
6154     assert (start);
6155     assert (Align ((size_t)start) == (size_t)start);
6156     generation_allocation_start (gen) = start;
6157     generation_allocation_pointer (gen) =  0;//start + Align (min_obj_size);
6158     generation_allocation_limit (gen) = 0;//generation_allocation_pointer (gen);
6159     set_allocation_heap_segment (gen);
6160 }
6161
6162 #ifdef BACKGROUND_GC
6163 //TODO BACKGROUND_GC this is for test only
6164 void
6165 gc_heap::disallow_new_allocation (int gen_number)
6166 {
6167     UNREFERENCED_PARAMETER(gen_number);
6168     settings.allocations_allowed = FALSE;
6169 }
6170 void
6171 gc_heap::allow_new_allocation (int gen_number)
6172 {
6173     UNREFERENCED_PARAMETER(gen_number);
6174     settings.allocations_allowed = TRUE;
6175 }
6176
6177 #endif //BACKGROUND_GC
6178
6179 bool gc_heap::new_allocation_allowed (int gen_number)
6180 {
6181 #ifdef BACKGROUND_GC
6182     //TODO BACKGROUND_GC this is for test only
6183     if (!settings.allocations_allowed)
6184     {
6185         dprintf (2, ("new allocation not allowed"));
6186         return FALSE;
6187     }
6188 #endif //BACKGROUND_GC
6189
6190     if (dd_new_allocation (dynamic_data_of (gen_number)) < 0)
6191     {
6192         if (gen_number != 0)
6193         {
6194             // For LOH we will give it more budget before we try a GC.
6195             if (settings.concurrent)
6196             {
6197                 dynamic_data* dd2 = dynamic_data_of (max_generation + 1 );
6198
6199                 if (dd_new_allocation (dd2) <= (ptrdiff_t)(-2 * dd_desired_allocation (dd2)))
6200                 {
6201                     return TRUE;
6202                 }
6203             }
6204         }
6205         return FALSE;
6206     }
6207 #ifndef MULTIPLE_HEAPS
6208     else if ((settings.pause_mode != pause_no_gc) && (gen_number == 0))
6209     {
6210         dprintf (3, ("evaluating allocation rate"));
6211         dynamic_data* dd0 = dynamic_data_of (0);
6212         if ((allocation_running_amount - dd_new_allocation (dd0)) >
6213             dd_min_size (dd0))
6214         {
6215             uint32_t ctime = GCToOSInterface::GetLowPrecisionTimeStamp();
6216             if ((ctime - allocation_running_time) > 1000)
6217             {
6218                 dprintf (2, (">1s since last gen0 gc"));
6219                 return FALSE;
6220             }
6221             else
6222             {
6223                 allocation_running_amount = dd_new_allocation (dd0);
6224             }
6225         }
6226     }
6227 #endif //MULTIPLE_HEAPS
6228     return TRUE;
6229 }
6230
6231 inline
6232 ptrdiff_t gc_heap::get_desired_allocation (int gen_number)
6233 {
6234     return dd_desired_allocation (dynamic_data_of (gen_number));
6235 }
6236
6237 inline
6238 ptrdiff_t  gc_heap::get_new_allocation (int gen_number)
6239 {
6240     return dd_new_allocation (dynamic_data_of (gen_number));
6241 }
6242
6243 //return the amount allocated so far in gen_number
6244 inline
6245 ptrdiff_t  gc_heap::get_allocation (int gen_number)
6246 {
6247     dynamic_data* dd = dynamic_data_of (gen_number);
6248
6249     return dd_desired_allocation (dd) - dd_new_allocation (dd);
6250 }
6251
6252 inline
6253 BOOL grow_mark_stack (mark*& m, size_t& len, size_t init_len)
6254 {
6255     size_t new_size = max (init_len, 2*len);
6256     mark* tmp = new (nothrow) mark [new_size];
6257     if (tmp)
6258     {
6259         memcpy (tmp, m, len * sizeof (mark));
6260         delete m;
6261         m = tmp;
6262         len = new_size;
6263         return TRUE;
6264     }
6265     else
6266     {
6267         dprintf (1, ("Failed to allocate %Id bytes for mark stack", (len * sizeof (mark))));
6268         return FALSE;
6269     }
6270 }
6271
6272 inline
6273 uint8_t* pinned_plug (mark* m)
6274 {
6275    return m->first;
6276 }
6277
6278 inline
6279 size_t& pinned_len (mark* m)
6280 {
6281     return m->len;
6282 }
6283
6284 inline
6285 void set_new_pin_info (mark* m, uint8_t* pin_free_space_start)
6286 {
6287     m->len = pinned_plug (m) - pin_free_space_start;
6288 #ifdef SHORT_PLUGS
6289     m->allocation_context_start_region = pin_free_space_start;
6290 #endif //SHORT_PLUGS
6291 }
6292
6293 #ifdef SHORT_PLUGS
6294 inline
6295 uint8_t*& pin_allocation_context_start_region (mark* m)
6296 {
6297     return m->allocation_context_start_region;
6298 }
6299
6300 uint8_t* get_plug_start_in_saved (uint8_t* old_loc, mark* pinned_plug_entry)
6301 {
6302     uint8_t* saved_pre_plug_info = (uint8_t*)(pinned_plug_entry->get_pre_plug_reloc_info());
6303     uint8_t* plug_start_in_saved = saved_pre_plug_info + (old_loc - (pinned_plug (pinned_plug_entry) - sizeof (plug_and_gap)));
6304     //dprintf (1, ("detected a very short plug: %Ix before PP %Ix, pad %Ix", 
6305     //    old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
6306     dprintf (1, ("EP: %Ix(%Ix), %Ix", old_loc, pinned_plug (pinned_plug_entry), plug_start_in_saved));
6307     return plug_start_in_saved;
6308 }
6309
6310 inline
6311 void set_padding_in_expand (uint8_t* old_loc,
6312                             BOOL set_padding_on_saved_p,
6313                             mark* pinned_plug_entry)
6314 {
6315     if (set_padding_on_saved_p)
6316     {
6317         set_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry));
6318     }
6319     else
6320     {
6321         set_plug_padded (old_loc);
6322     }
6323 }
6324
6325 inline
6326 void clear_padding_in_expand (uint8_t* old_loc,
6327                               BOOL set_padding_on_saved_p,
6328                               mark* pinned_plug_entry)
6329 {
6330     if (set_padding_on_saved_p)
6331     {
6332         clear_plug_padded (get_plug_start_in_saved (old_loc, pinned_plug_entry));
6333     }
6334     else
6335     {
6336         clear_plug_padded (old_loc);
6337     }
6338 }
6339 #endif //SHORT_PLUGS
6340
6341 void gc_heap::reset_pinned_queue()
6342 {
6343     mark_stack_tos = 0;
6344     mark_stack_bos = 0;
6345 }
6346
6347 void gc_heap::reset_pinned_queue_bos()
6348 {
6349     mark_stack_bos = 0;
6350 }
6351
6352 // last_pinned_plug is only for asserting purpose.
6353 void gc_heap::merge_with_last_pinned_plug (uint8_t* last_pinned_plug, size_t plug_size)
6354 {
6355     if (last_pinned_plug)
6356     {
6357         mark& last_m = mark_stack_array[mark_stack_tos - 1];
6358         assert (last_pinned_plug == last_m.first);
6359         if (last_m.saved_post_p)
6360         {
6361             last_m.saved_post_p = FALSE;
6362             dprintf (3, ("setting last plug %Ix post to false", last_m.first));
6363             // We need to recover what the gap has overwritten.
6364             memcpy ((last_m.first + last_m.len - sizeof (plug_and_gap)), &(last_m.saved_post_plug), sizeof (gap_reloc_pair));
6365         }
6366         last_m.len += plug_size;
6367         dprintf (3, ("recovered the last part of plug %Ix, setting its plug size to %Ix", last_m.first, last_m.len));
6368     }
6369 }
6370
6371 void gc_heap::set_allocator_next_pin (uint8_t* alloc_pointer, uint8_t*& alloc_limit)
6372 {
6373     dprintf (3, ("sanp: ptr: %Ix, limit: %Ix", alloc_pointer, alloc_limit));
6374     dprintf (3, ("oldest %Id: %Ix", mark_stack_bos, pinned_plug (oldest_pin())));
6375     if (!(pinned_plug_que_empty_p()))
6376     {
6377         mark*  oldest_entry = oldest_pin();
6378         uint8_t* plug = pinned_plug (oldest_entry);
6379         if ((plug >= alloc_pointer) && (plug < alloc_limit))
6380         {
6381             alloc_limit = pinned_plug (oldest_entry);
6382             dprintf (3, ("now setting alloc context: %Ix->%Ix(%Id)",
6383                 alloc_pointer, alloc_limit, (alloc_limit - alloc_pointer)));
6384         }
6385     }
6386 }
6387
6388 void gc_heap::set_allocator_next_pin (generation* gen)
6389 {
6390     dprintf (3, ("SANP: gen%d, ptr; %Ix, limit: %Ix", gen->gen_num, generation_allocation_pointer (gen), generation_allocation_limit (gen)));
6391     if (!(pinned_plug_que_empty_p()))
6392     {
6393         mark*  oldest_entry = oldest_pin();
6394         uint8_t* plug = pinned_plug (oldest_entry);
6395         if ((plug >= generation_allocation_pointer (gen)) &&
6396             (plug <  generation_allocation_limit (gen)))
6397         {
6398             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
6399             dprintf (3, ("SANP: get next pin free space in gen%d for alloc: %Ix->%Ix(%Id)", 
6400                 gen->gen_num,
6401                 generation_allocation_pointer (gen), generation_allocation_limit (gen),
6402                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
6403         }
6404         else
6405             assert (!((plug < generation_allocation_pointer (gen)) &&
6406                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
6407     }
6408 }
6409
6410 // After we set the info, we increase tos.
6411 void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, uint8_t* alloc_pointer, uint8_t*& alloc_limit)
6412 {
6413     UNREFERENCED_PARAMETER(last_pinned_plug);
6414
6415     mark& m = mark_stack_array[mark_stack_tos];
6416     assert (m.first == last_pinned_plug);
6417
6418     m.len = plug_len;
6419     mark_stack_tos++;
6420     set_allocator_next_pin (alloc_pointer, alloc_limit);
6421 }
6422
6423 // After we set the info, we increase tos.
6424 void gc_heap::set_pinned_info (uint8_t* last_pinned_plug, size_t plug_len, generation* gen)
6425 {
6426     UNREFERENCED_PARAMETER(last_pinned_plug);
6427
6428     mark& m = mark_stack_array[mark_stack_tos];
6429     assert (m.first == last_pinned_plug);
6430
6431     m.len = plug_len;
6432     mark_stack_tos++;
6433     assert (gen != 0);
6434     // Why are we checking here? gen is never 0.
6435     if (gen != 0)
6436     {
6437         set_allocator_next_pin (gen);
6438     }
6439 }
6440
6441 size_t gc_heap::deque_pinned_plug ()
6442 {
6443     dprintf (3, ("dequed: %Id", mark_stack_bos));
6444     size_t m = mark_stack_bos;
6445     mark_stack_bos++;
6446     return m;
6447 }
6448
6449 inline
6450 mark* gc_heap::pinned_plug_of (size_t bos)
6451 {
6452     return &mark_stack_array [ bos ];
6453 }
6454
6455 inline
6456 mark* gc_heap::oldest_pin ()
6457 {
6458     return pinned_plug_of (mark_stack_bos);
6459 }
6460
6461 inline
6462 BOOL gc_heap::pinned_plug_que_empty_p ()
6463 {
6464     return (mark_stack_bos == mark_stack_tos);
6465 }
6466
6467 inline
6468 mark* gc_heap::before_oldest_pin()
6469 {
6470     if (mark_stack_bos >= 1)
6471         return pinned_plug_of (mark_stack_bos-1);
6472     else
6473         return 0;
6474 }
6475
6476 inline
6477 BOOL gc_heap::ephemeral_pointer_p (uint8_t* o)
6478 {
6479     return ((o >= ephemeral_low) && (o < ephemeral_high));
6480 }
6481
6482 #ifdef MH_SC_MARK
6483 inline
6484 int& gc_heap::mark_stack_busy()
6485 {
6486     return  g_mark_stack_busy [(heap_number+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
6487 }
6488 #endif //MH_SC_MARK
6489
6490 void gc_heap::make_mark_stack (mark* arr)
6491 {
6492     reset_pinned_queue();
6493     mark_stack_array = arr;
6494     mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6495 #ifdef MH_SC_MARK
6496     mark_stack_busy() = 0;
6497 #endif //MH_SC_MARK
6498 }
6499
6500 #ifdef BACKGROUND_GC
6501 inline
6502 size_t& gc_heap::bpromoted_bytes(int thread)
6503 {
6504 #ifdef MULTIPLE_HEAPS
6505     return g_bpromoted [thread*16];
6506 #else //MULTIPLE_HEAPS
6507     UNREFERENCED_PARAMETER(thread);
6508     return g_bpromoted;
6509 #endif //MULTIPLE_HEAPS
6510 }
6511
6512 void gc_heap::make_background_mark_stack (uint8_t** arr)
6513 {
6514     background_mark_stack_array = arr;
6515     background_mark_stack_array_length = MARK_STACK_INITIAL_LENGTH;
6516     background_mark_stack_tos = arr;
6517 }
6518
6519 void gc_heap::make_c_mark_list (uint8_t** arr)
6520 {
6521     c_mark_list = arr;
6522     c_mark_list_index = 0;
6523     c_mark_list_length = 1 + (OS_PAGE_SIZE / MIN_OBJECT_SIZE);
6524 }
6525 #endif //BACKGROUND_GC
6526
6527
6528 #ifdef CARD_BUNDLE
6529
6530 // The card bundle keeps track of groups of card words.
6531 static const size_t card_bundle_word_width = 32;
6532
6533 // How do we express the fact that 32 bits (card_word_width) is one uint32_t?
6534 static const size_t card_bundle_size = (size_t)(GC_PAGE_SIZE / (sizeof(uint32_t)*card_bundle_word_width));
6535
6536 inline
6537 size_t card_bundle_word (size_t cardb)
6538 {
6539     return cardb / card_bundle_word_width;
6540 }
6541
6542 inline
6543 uint32_t card_bundle_bit (size_t cardb)
6544 {
6545     return (uint32_t)(cardb % card_bundle_word_width);
6546 }
6547
6548 size_t align_cardw_on_bundle (size_t cardw)
6549 {
6550     return ((size_t)(cardw + card_bundle_size - 1) & ~(card_bundle_size - 1 ));
6551 }
6552
6553 // Get the card bundle representing a card word
6554 size_t cardw_card_bundle (size_t cardw)
6555 {
6556     return cardw / card_bundle_size;
6557 }
6558
6559 // Get the first card word in a card bundle
6560 size_t card_bundle_cardw (size_t cardb)
6561 {
6562     return cardb * card_bundle_size;
6563 }
6564
6565 // Clear the specified card bundle
6566 void gc_heap::card_bundle_clear (size_t cardb)
6567 {
6568     card_bundle_table [card_bundle_word (cardb)] &= ~(1 << card_bundle_bit (cardb));
6569     dprintf (2, ("Cleared card bundle %Ix [%Ix, %Ix[", cardb, (size_t)card_bundle_cardw (cardb),
6570               (size_t)card_bundle_cardw (cardb+1)));
6571 }
6572
6573 void gc_heap::card_bundle_set (size_t cardb)
6574 {
6575     if (!card_bundle_set_p (cardb))
6576     {
6577         card_bundle_table [card_bundle_word (cardb)] |= (1 << card_bundle_bit (cardb));
6578     }
6579 }
6580
6581 // Set the card bundle bits between start_cardb and end_cardb
6582 void gc_heap::card_bundles_set (size_t start_cardb, size_t end_cardb)
6583 {
6584     if (start_cardb == end_cardb)
6585     {
6586         card_bundle_set(start_cardb);
6587         return;
6588     }
6589
6590     size_t start_word = card_bundle_word (start_cardb);
6591     size_t end_word = card_bundle_word (end_cardb);
6592
6593     if (start_word < end_word)
6594     {
6595         // Set the partial words
6596         card_bundle_table [start_word] |= highbits (~0u, card_bundle_bit (start_cardb));
6597
6598         if (card_bundle_bit (end_cardb))
6599             card_bundle_table [end_word] |= lowbits (~0u, card_bundle_bit (end_cardb));
6600
6601         // Set the full words
6602         for (size_t i = start_word + 1; i < end_word; i++)
6603             card_bundle_table [i] = ~0u;
6604     }
6605     else
6606     {
6607         card_bundle_table [start_word] |= (highbits (~0u, card_bundle_bit (start_cardb)) &
6608                                             lowbits (~0u, card_bundle_bit (end_cardb)));
6609     }
6610 }
6611
6612 // Indicates whether the specified bundle is set.
6613 BOOL gc_heap::card_bundle_set_p (size_t cardb)
6614 {
6615     return (card_bundle_table[card_bundle_word(cardb)] & (1 << card_bundle_bit (cardb)));
6616 }
6617
6618 // Returns the size (in bytes) of a card bundle representing the region from 'from' to 'end'
6619 size_t size_card_bundle_of (uint8_t* from, uint8_t* end)
6620 {
6621     // Number of heap bytes represented by a card bundle word
6622     size_t cbw_span = card_size * card_word_width * card_bundle_size * card_bundle_word_width;
6623
6624     // Align the start of the region down
6625     from = (uint8_t*)((size_t)from & ~(cbw_span - 1));
6626
6627     // Align the end of the region up
6628     end = (uint8_t*)((size_t)(end + (cbw_span - 1)) & ~(cbw_span - 1));
6629
6630     // Make sure they're really aligned
6631     assert (((size_t)from & (cbw_span - 1)) == 0);
6632     assert (((size_t)end  & (cbw_span - 1)) == 0);
6633
6634     return ((end - from) / cbw_span) * sizeof (uint32_t);
6635 }
6636
6637 // Takes a pointer to a card bundle table and an address, and returns a pointer that represents
6638 // where a theoretical card bundle table that represents every address (starting from 0) would
6639 // start if the bundle word representing the address were to be located at the pointer passed in.
6640 // The returned 'translated' pointer makes it convenient/fast to calculate where the card bundle
6641 // for a given address is using a simple shift operation on the address.
6642 uint32_t* translate_card_bundle_table (uint32_t* cb, uint8_t* lowest_address)
6643 {
6644     // The number of bytes of heap memory represented by a card bundle word
6645     const size_t heap_bytes_for_bundle_word = card_size * card_word_width * card_bundle_size * card_bundle_word_width;
6646
6647     // Each card bundle word is 32 bits
6648     return (uint32_t*)((uint8_t*)cb - (((size_t)lowest_address / heap_bytes_for_bundle_word) * sizeof (uint32_t)));
6649 }
6650
6651 void gc_heap::enable_card_bundles ()
6652 {
6653     if (can_use_write_watch_for_card_table() && (!card_bundles_enabled()))
6654     {
6655         dprintf (1, ("Enabling card bundles"));
6656
6657         // We initially set all of the card bundles
6658         card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
6659                           cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
6660         settings.card_bundles = TRUE;
6661     }
6662 }
6663
6664 BOOL gc_heap::card_bundles_enabled ()
6665 {
6666     return settings.card_bundles;
6667 }
6668
6669 #endif // CARD_BUNDLE
6670
6671 #if defined (_TARGET_AMD64_)
6672 #define brick_size ((size_t)4096)
6673 #else
6674 #define brick_size ((size_t)2048)
6675 #endif //_TARGET_AMD64_
6676
6677 inline
6678 size_t gc_heap::brick_of (uint8_t* add)
6679 {
6680     return (size_t)(add - lowest_address) / brick_size;
6681 }
6682
6683 inline
6684 uint8_t* gc_heap::brick_address (size_t brick)
6685 {
6686     return lowest_address + (brick_size * brick);
6687 }
6688
6689
6690 void gc_heap::clear_brick_table (uint8_t* from, uint8_t* end)
6691 {
6692     for (size_t i = brick_of (from);i < brick_of (end); i++)
6693         brick_table[i] = 0;
6694 }
6695
6696 //codes for the brick entries:
6697 //entry == 0 -> not assigned
6698 //entry >0 offset is entry-1
6699 //entry <0 jump back entry bricks
6700
6701
6702 inline
6703 void gc_heap::set_brick (size_t index, ptrdiff_t val)
6704 {
6705     if (val < -32767)
6706     {
6707         val = -32767;
6708     }
6709     assert (val < 32767);
6710     if (val >= 0)
6711         brick_table [index] = (short)val+1;
6712     else
6713         brick_table [index] = (short)val;
6714 }
6715
6716 inline
6717 int gc_heap::get_brick_entry (size_t index)
6718 {
6719 #ifdef MULTIPLE_HEAPS
6720     return VolatileLoadWithoutBarrier(&brick_table [index]);
6721 #else
6722     return brick_table[index];
6723 #endif
6724 }
6725
6726
6727 inline
6728 uint8_t* align_on_brick (uint8_t* add)
6729 {
6730     return (uint8_t*)((size_t)(add + brick_size - 1) & ~(brick_size - 1));
6731 }
6732
6733 inline
6734 uint8_t* align_lower_brick (uint8_t* add)
6735 {
6736     return (uint8_t*)(((size_t)add) & ~(brick_size - 1));
6737 }
6738
6739 size_t size_brick_of (uint8_t* from, uint8_t* end)
6740 {
6741     assert (((size_t)from & (brick_size-1)) == 0);
6742     assert (((size_t)end  & (brick_size-1)) == 0);
6743
6744     return ((end - from) / brick_size) * sizeof (short);
6745 }
6746
6747 inline
6748 uint8_t* gc_heap::card_address (size_t card)
6749 {
6750     return  (uint8_t*) (card_size * card);
6751 }
6752
6753 inline
6754 size_t gc_heap::card_of ( uint8_t* object)
6755 {
6756     return (size_t)(object) / card_size;
6757 }
6758
6759 inline
6760 size_t gc_heap::card_to_brick (size_t card)
6761 {
6762     return brick_of (card_address (card));
6763 }
6764
6765 inline
6766 uint8_t* align_on_card (uint8_t* add)
6767 {
6768     return (uint8_t*)((size_t)(add + card_size - 1) & ~(card_size - 1 ));
6769 }
6770 inline
6771 uint8_t* align_on_card_word (uint8_t* add)
6772 {
6773     return (uint8_t*) ((size_t)(add + (card_size*card_word_width)-1) & ~(card_size*card_word_width - 1));
6774 }
6775
6776 inline
6777 uint8_t* align_lower_card (uint8_t* add)
6778 {
6779     return (uint8_t*)((size_t)add & ~(card_size-1));
6780 }
6781
6782 inline
6783 void gc_heap::clear_card (size_t card)
6784 {
6785     card_table [card_word (card)] =
6786         (card_table [card_word (card)] & ~(1 << card_bit (card)));
6787     dprintf (3,("Cleared card %Ix [%Ix, %Ix[", card, (size_t)card_address (card),
6788               (size_t)card_address (card+1)));
6789 }
6790
6791 inline
6792 void gc_heap::set_card (size_t card)
6793 {
6794     size_t word = card_word (card);
6795     card_table[word] = (card_table [word] | (1 << card_bit (card)));
6796
6797 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
6798     // Also set the card bundle that corresponds to the card
6799     size_t bundle_to_set = cardw_card_bundle(word);
6800
6801     card_bundle_set(bundle_to_set);
6802
6803     dprintf (3,("Set card %Ix [%Ix, %Ix[ and bundle %Ix", card, (size_t)card_address (card), (size_t)card_address (card+1), bundle_to_set));
6804     assert(card_bundle_set_p(bundle_to_set) != 0);
6805 #endif
6806 }
6807
6808 inline
6809 BOOL  gc_heap::card_set_p (size_t card)
6810 {
6811     return ( card_table [ card_word (card) ] & (1 << card_bit (card)));
6812 }
6813
6814 // Returns the number of DWORDs in the card table that cover the
6815 // range of addresses [from, end[.
6816 size_t count_card_of (uint8_t* from, uint8_t* end)
6817 {
6818     return card_word (gcard_of (end - 1)) - card_word (gcard_of (from)) + 1;
6819 }
6820
6821 // Returns the number of bytes to allocate for a card table
6822 // that covers the range of addresses [from, end[.
6823 size_t size_card_of (uint8_t* from, uint8_t* end)
6824 {
6825     return count_card_of (from, end) * sizeof(uint32_t);
6826 }
6827
6828 // We don't store seg_mapping_table in card_table_info because there's only always one view.
6829 class card_table_info
6830 {
6831 public:
6832     unsigned    recount;
6833     uint8_t*    lowest_address;
6834     uint8_t*    highest_address;
6835     short*      brick_table;
6836
6837 #ifdef CARD_BUNDLE
6838     uint32_t*   card_bundle_table;
6839 #endif //CARD_BUNDLE
6840
6841     // mark_array is always at the end of the data structure because we
6842     // want to be able to make one commit call for everything before it.
6843 #ifdef MARK_ARRAY
6844     uint32_t*   mark_array;
6845 #endif //MARK_ARRAY
6846
6847     size_t      size;
6848     uint32_t*   next_card_table;
6849 };
6850
6851 //These are accessors on untranslated cardtable
6852 inline
6853 unsigned& card_table_refcount (uint32_t* c_table)
6854 {
6855     return *(unsigned*)((char*)c_table - sizeof (card_table_info));
6856 }
6857
6858 inline
6859 uint8_t*& card_table_lowest_address (uint32_t* c_table)
6860 {
6861     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->lowest_address;
6862 }
6863
6864 uint32_t* translate_card_table (uint32_t* ct)
6865 {
6866     return (uint32_t*)((uint8_t*)ct - card_word (gcard_of (card_table_lowest_address (ct))) * sizeof(uint32_t));
6867 }
6868
6869 inline
6870 uint8_t*& card_table_highest_address (uint32_t* c_table)
6871 {
6872     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->highest_address;
6873 }
6874
6875 inline
6876 short*& card_table_brick_table (uint32_t* c_table)
6877 {
6878     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->brick_table;
6879 }
6880
6881 #ifdef CARD_BUNDLE
6882 inline
6883 uint32_t*& card_table_card_bundle_table (uint32_t* c_table)
6884 {
6885     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->card_bundle_table;
6886 }
6887 #endif //CARD_BUNDLE
6888
6889 #ifdef MARK_ARRAY
6890 /* Support for mark_array */
6891
6892 inline
6893 uint32_t*& card_table_mark_array (uint32_t* c_table)
6894 {
6895     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->mark_array;
6896 }
6897
6898 #ifdef BIT64
6899 #define mark_bit_pitch ((size_t)16)
6900 #else
6901 #define mark_bit_pitch ((size_t)8)
6902 #endif // BIT64
6903 #define mark_word_width ((size_t)32)
6904 #define mark_word_size (mark_word_width * mark_bit_pitch)
6905
6906 inline
6907 uint8_t* align_on_mark_bit (uint8_t* add)
6908 {
6909     return (uint8_t*)((size_t)(add + (mark_bit_pitch - 1)) & ~(mark_bit_pitch - 1));
6910 }
6911
6912 inline
6913 uint8_t* align_lower_mark_bit (uint8_t* add)
6914 {
6915     return (uint8_t*)((size_t)(add) & ~(mark_bit_pitch - 1));
6916 }
6917
6918 inline
6919 BOOL is_aligned_on_mark_word (uint8_t* add)
6920 {
6921     return ((size_t)add == ((size_t)(add) & ~(mark_word_size - 1)));
6922 }
6923
6924 inline
6925 uint8_t* align_on_mark_word (uint8_t* add)
6926 {
6927     return (uint8_t*)((size_t)(add + mark_word_size - 1) & ~(mark_word_size - 1));
6928 }
6929
6930 inline
6931 uint8_t* align_lower_mark_word (uint8_t* add)
6932 {
6933     return (uint8_t*)((size_t)(add) & ~(mark_word_size - 1));
6934 }
6935
6936 inline
6937 size_t mark_bit_of (uint8_t* add)
6938 {
6939     return ((size_t)add / mark_bit_pitch);
6940 }
6941
6942 inline
6943 unsigned int mark_bit_bit (size_t mark_bit)
6944 {
6945     return (unsigned int)(mark_bit % mark_word_width);
6946 }
6947
6948 inline
6949 size_t mark_bit_word (size_t mark_bit)
6950 {
6951     return (mark_bit / mark_word_width);
6952 }
6953
6954 inline
6955 size_t mark_word_of (uint8_t* add)
6956 {
6957     return ((size_t)add) / mark_word_size;
6958 }
6959
6960 uint8_t* mark_word_address (size_t wd)
6961 {
6962     return (uint8_t*)(wd*mark_word_size);
6963 }
6964
6965 uint8_t* mark_bit_address (size_t mark_bit)
6966 {
6967     return (uint8_t*)(mark_bit*mark_bit_pitch);
6968 }
6969
6970 inline
6971 size_t mark_bit_bit_of (uint8_t* add)
6972 {
6973     return  (((size_t)add / mark_bit_pitch) % mark_word_width);
6974 }
6975
6976 inline
6977 unsigned int gc_heap::mark_array_marked(uint8_t* add)
6978 {
6979     return mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add));
6980 }
6981
6982 inline
6983 BOOL gc_heap::is_mark_bit_set (uint8_t* add)
6984 {
6985     return (mark_array [mark_word_of (add)] & (1 << mark_bit_bit_of (add)));
6986 }
6987
6988 inline
6989 void gc_heap::mark_array_set_marked (uint8_t* add)
6990 {
6991     size_t index = mark_word_of (add);
6992     uint32_t val = (1 << mark_bit_bit_of (add));
6993 #ifdef MULTIPLE_HEAPS
6994     Interlocked::Or (&(mark_array [index]), val);
6995 #else
6996     mark_array [index] |= val;
6997 #endif 
6998 }
6999
7000 inline
7001 void gc_heap::mark_array_clear_marked (uint8_t* add)
7002 {
7003     mark_array [mark_word_of (add)] &= ~(1 << mark_bit_bit_of (add));
7004 }
7005
7006 size_t size_mark_array_of (uint8_t* from, uint8_t* end)
7007 {
7008     assert (((size_t)from & ((mark_word_size)-1)) == 0);
7009     assert (((size_t)end  & ((mark_word_size)-1)) == 0);
7010     return sizeof (uint32_t)*(((end - from) / mark_word_size));
7011 }
7012
7013 //In order to eliminate the lowest_address in the mark array
7014 //computations (mark_word_of, etc) mark_array is offset
7015 // according to the lowest_address.
7016 uint32_t* translate_mark_array (uint32_t* ma)
7017 {
7018     return (uint32_t*)((uint8_t*)ma - size_mark_array_of (0, g_gc_lowest_address));
7019 }
7020
7021 // from and end must be page aligned addresses. 
7022 void gc_heap::clear_mark_array (uint8_t* from, uint8_t* end, BOOL check_only/*=TRUE*/
7023 #ifdef FEATURE_BASICFREEZE
7024                                 , BOOL read_only/*=FALSE*/
7025 #endif // FEATURE_BASICFREEZE
7026                                 )
7027 {
7028     if(!gc_can_use_concurrent)
7029         return;
7030
7031 #ifdef FEATURE_BASICFREEZE
7032     if (!read_only)
7033 #endif // FEATURE_BASICFREEZE
7034     {
7035         assert (from == align_on_mark_word (from));
7036     }
7037     assert (end == align_on_mark_word (end));
7038
7039 #ifdef BACKGROUND_GC
7040     uint8_t* current_lowest_address = background_saved_lowest_address;
7041     uint8_t* current_highest_address = background_saved_highest_address;
7042 #else
7043     uint8_t* current_lowest_address = lowest_address;
7044     uint8_t* current_highest_address = highest_address;
7045 #endif //BACKGROUND_GC
7046
7047     //there is a possibility of the addresses to be
7048     //outside of the covered range because of a newly allocated
7049     //large object segment
7050     if ((end <= current_highest_address) && (from >= current_lowest_address))
7051     {
7052         size_t beg_word = mark_word_of (align_on_mark_word (from));
7053         MAYBE_UNUSED_VAR(beg_word);
7054         //align end word to make sure to cover the address
7055         size_t end_word = mark_word_of (align_on_mark_word (end));
7056         MAYBE_UNUSED_VAR(end_word);
7057         dprintf (3, ("Calling clearing mark array [%Ix, %Ix[ for addresses [%Ix, %Ix[(%s)",
7058                      (size_t)mark_word_address (beg_word),
7059                      (size_t)mark_word_address (end_word),
7060                      (size_t)from, (size_t)end,
7061                      (check_only ? "check_only" : "clear")));
7062         if (!check_only)
7063         {
7064             uint8_t* op = from;
7065             while (op < mark_word_address (beg_word))
7066             {
7067                 mark_array_clear_marked (op);
7068                 op += mark_bit_pitch;
7069             }
7070
7071             memset (&mark_array[beg_word], 0, (end_word - beg_word)*sizeof (uint32_t));
7072         }
7073 #ifdef _DEBUG
7074         else
7075         {
7076             //Beware, it is assumed that the mark array word straddling
7077             //start has been cleared before
7078             //verify that the array is empty.
7079             size_t  markw = mark_word_of (align_on_mark_word (from));
7080             size_t  markw_end = mark_word_of (align_on_mark_word (end));
7081             while (markw < markw_end)
7082             {
7083                 assert (!(mark_array [markw]));
7084                 markw++;
7085             }
7086             uint8_t* p = mark_word_address (markw_end);
7087             while (p < end)
7088             {
7089                 assert (!(mark_array_marked (p)));
7090                 p++;
7091             }
7092         }
7093 #endif //_DEBUG
7094     }
7095 }
7096 #endif //MARK_ARRAY
7097
7098 //These work on untranslated card tables
7099 inline
7100 uint32_t*& card_table_next (uint32_t* c_table)
7101 {
7102     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->next_card_table;
7103 }
7104
7105 inline
7106 size_t& card_table_size (uint32_t* c_table)
7107 {
7108     return ((card_table_info*)((uint8_t*)c_table - sizeof (card_table_info)))->size;
7109 }
7110
7111 void own_card_table (uint32_t* c_table)
7112 {
7113     card_table_refcount (c_table) += 1;
7114 }
7115
7116 void destroy_card_table (uint32_t* c_table);
7117
7118 void delete_next_card_table (uint32_t* c_table)
7119 {
7120     uint32_t* n_table = card_table_next (c_table);
7121     if (n_table)
7122     {
7123         if (card_table_next (n_table))
7124         {
7125             delete_next_card_table (n_table);
7126         }
7127         if (card_table_refcount (n_table) == 0)
7128         {
7129             destroy_card_table (n_table);
7130             card_table_next (c_table) = 0;
7131         }
7132     }
7133 }
7134
7135 void release_card_table (uint32_t* c_table)
7136 {
7137     assert (card_table_refcount (c_table) >0);
7138     card_table_refcount (c_table) -= 1;
7139     if (card_table_refcount (c_table) == 0)
7140     {
7141         delete_next_card_table (c_table);
7142         if (card_table_next (c_table) == 0)
7143         {
7144             destroy_card_table (c_table);
7145             // sever the link from the parent
7146             if (&g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))] == c_table)
7147             {
7148                 g_gc_card_table = 0;
7149
7150 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7151                 g_gc_card_bundle_table = 0;
7152 #endif
7153 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7154                 SoftwareWriteWatch::StaticClose();
7155 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7156             }
7157             else
7158             {
7159                 uint32_t* p_table = &g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))];
7160                 if (p_table)
7161                 {
7162                     while (p_table && (card_table_next (p_table) != c_table))
7163                         p_table = card_table_next (p_table);
7164                     card_table_next (p_table) = 0;
7165                 }
7166             }
7167         }
7168     }
7169 }
7170
7171 void destroy_card_table (uint32_t* c_table)
7172 {
7173 //  delete (uint32_t*)&card_table_refcount(c_table);
7174
7175     GCToOSInterface::VirtualRelease (&card_table_refcount(c_table), card_table_size(c_table));
7176     dprintf (2, ("Table Virtual Free : %Ix", (size_t)&card_table_refcount(c_table)));
7177 }
7178
7179 uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end)
7180 {
7181     assert (g_gc_lowest_address == start);
7182     assert (g_gc_highest_address == end);
7183
7184     uint32_t virtual_reserve_flags = VirtualReserveFlags::None;
7185
7186     size_t bs = size_brick_of (start, end);
7187     size_t cs = size_card_of (start, end);
7188 #ifdef MARK_ARRAY
7189     size_t ms = (gc_can_use_concurrent ? 
7190                  size_mark_array_of (start, end) :
7191                  0);
7192 #else
7193     size_t ms = 0;
7194 #endif //MARK_ARRAY
7195
7196     size_t cb = 0;
7197
7198 #ifdef CARD_BUNDLE
7199     if (can_use_write_watch_for_card_table())
7200     {
7201         cb = size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address);
7202 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7203         // If we're not manually managing the card bundles, we will need to use OS write
7204         // watch APIs over this region to track changes.
7205         virtual_reserve_flags |= VirtualReserveFlags::WriteWatch;
7206 #endif
7207     }
7208 #endif //CARD_BUNDLE
7209
7210     size_t wws = 0;
7211 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7212     size_t sw_ww_table_offset = 0;
7213     if (gc_can_use_concurrent)
7214     {
7215         size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb;
7216         sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table);
7217         wws = sw_ww_table_offset - sw_ww_size_before_table + SoftwareWriteWatch::GetTableByteSize(start, end);
7218     }
7219 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7220
7221 #ifdef GROWABLE_SEG_MAPPING_TABLE
7222     size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address);
7223     size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws;
7224     size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset);
7225
7226     st += (st_table_offset_aligned - st_table_offset);
7227 #else //GROWABLE_SEG_MAPPING_TABLE
7228     size_t st = 0;
7229 #endif //GROWABLE_SEG_MAPPING_TABLE
7230
7231     // it is impossible for alloc_size to overflow due bounds on each of 
7232     // its components.
7233     size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms);
7234     uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags);
7235
7236     if (!mem)
7237         return 0;
7238
7239     dprintf (2, ("Init - Card table alloc for %Id bytes: [%Ix, %Ix[",
7240                  alloc_size, (size_t)mem, (size_t)(mem+alloc_size)));
7241
7242     // mark array will be committed separately (per segment).
7243     size_t commit_size = alloc_size - ms;
7244
7245     if (!virtual_commit (mem, commit_size))
7246     {
7247         dprintf (1, ("Card table commit failed"));
7248         GCToOSInterface::VirtualRelease (mem, alloc_size);
7249         return 0;
7250     }
7251     
7252     // initialize the ref count
7253     uint32_t* ct = (uint32_t*)(mem+sizeof (card_table_info));
7254     card_table_refcount (ct) = 0;
7255     card_table_lowest_address (ct) = start;
7256     card_table_highest_address (ct) = end;
7257     card_table_brick_table (ct) = (short*)((uint8_t*)ct + cs);
7258     card_table_size (ct) = alloc_size;
7259     card_table_next (ct) = 0;
7260
7261 #ifdef CARD_BUNDLE
7262     card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs);
7263
7264 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7265     g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), g_gc_lowest_address);
7266 #endif
7267
7268 #endif //CARD_BUNDLE
7269
7270 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7271     if (gc_can_use_concurrent)
7272     {
7273         SoftwareWriteWatch::InitializeUntranslatedTable(mem + sw_ww_table_offset, start);
7274     }
7275 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7276
7277 #ifdef GROWABLE_SEG_MAPPING_TABLE
7278     seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
7279     seg_mapping_table = (seg_mapping*)((uint8_t*)seg_mapping_table - 
7280                                         size_seg_mapping_table_of (0, (align_lower_segment (g_gc_lowest_address))));
7281 #endif //GROWABLE_SEG_MAPPING_TABLE
7282
7283 #ifdef MARK_ARRAY
7284     if (gc_can_use_concurrent)
7285         card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st);
7286     else
7287         card_table_mark_array (ct) = NULL;
7288 #endif //MARK_ARRAY
7289
7290     return translate_card_table(ct);
7291 }
7292
7293 void gc_heap::set_fgm_result (failure_get_memory f, size_t s, BOOL loh_p)
7294 {
7295 #ifdef MULTIPLE_HEAPS
7296     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
7297     {
7298         gc_heap* hp = gc_heap::g_heaps [hn];
7299         hp->fgm_result.set_fgm (f, s, loh_p);
7300     }
7301 #else //MULTIPLE_HEAPS
7302     fgm_result.set_fgm (f, s, loh_p);
7303 #endif //MULTIPLE_HEAPS
7304 }
7305
7306 //returns 0 for success, -1 otherwise
7307 // We are doing all the decommitting here because we want to make sure we have
7308 // enough memory to do so - if we do this during copy_brick_card_table and 
7309 // and fail to decommit it would make the failure case very complicated to 
7310 // handle. This way we can waste some decommit if we call this multiple 
7311 // times before the next FGC but it's easier to handle the failure case.
7312 int gc_heap::grow_brick_card_tables (uint8_t* start,
7313                                      uint8_t* end,
7314                                      size_t size,
7315                                      heap_segment* new_seg, 
7316                                      gc_heap* hp, 
7317                                      BOOL loh_p)
7318 {
7319     uint8_t* la = g_gc_lowest_address;
7320     uint8_t* ha = g_gc_highest_address;
7321     uint8_t* saved_g_lowest_address = min (start, g_gc_lowest_address);
7322     uint8_t* saved_g_highest_address = max (end, g_gc_highest_address);
7323     seg_mapping* new_seg_mapping_table = nullptr;
7324 #ifdef BACKGROUND_GC
7325     // This value is only for logging purpose - it's not necessarily exactly what we 
7326     // would commit for mark array but close enough for diagnostics purpose.
7327     size_t logging_ma_commit_size = size_mark_array_of (0, (uint8_t*)size);
7328 #endif //BACKGROUND_GC
7329
7330     // See if the address is already covered
7331     if ((la != saved_g_lowest_address ) || (ha != saved_g_highest_address))
7332     {
7333         {
7334             //modify the higest address so the span covered
7335             //is twice the previous one.
7336             uint8_t* top = (uint8_t*)0 + Align (GCToOSInterface::GetVirtualMemoryLimit());
7337             // On non-Windows systems, we get only an approximate value that can possibly be
7338             // slightly lower than the saved_g_highest_address.
7339             // In such case, we set the top to the saved_g_highest_address so that the
7340             // card and brick tables always cover the whole new range.
7341             if (top < saved_g_highest_address)
7342             {
7343                 top = saved_g_highest_address;
7344             }
7345             size_t ps = ha-la;
7346 #ifdef BIT64
7347             if (ps > (uint64_t)200*1024*1024*1024)
7348                 ps += (uint64_t)100*1024*1024*1024;
7349             else
7350 #endif // BIT64
7351                 ps *= 2;
7352
7353             if (saved_g_lowest_address < g_gc_lowest_address)
7354             {
7355                 if (ps > (size_t)g_gc_lowest_address)
7356                     saved_g_lowest_address = (uint8_t*)(size_t)OS_PAGE_SIZE;
7357                 else
7358                 {
7359                     assert (((size_t)g_gc_lowest_address - ps) >= OS_PAGE_SIZE);
7360                     saved_g_lowest_address = min (saved_g_lowest_address, (g_gc_lowest_address - ps));
7361                 }
7362             }
7363
7364             if (saved_g_highest_address > g_gc_highest_address)
7365             {
7366                 saved_g_highest_address = max ((saved_g_lowest_address + ps), saved_g_highest_address);
7367                 if (saved_g_highest_address > top)
7368                     saved_g_highest_address = top;
7369             }
7370         }
7371         dprintf (GC_TABLE_LOG, ("Growing card table [%Ix, %Ix[",
7372                                 (size_t)saved_g_lowest_address,
7373                                 (size_t)saved_g_highest_address));
7374
7375         bool write_barrier_updated = false;
7376         uint32_t virtual_reserve_flags = VirtualReserveFlags::None;
7377         uint32_t* saved_g_card_table = g_gc_card_table;
7378
7379 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7380         uint32_t* saved_g_card_bundle_table = g_gc_card_bundle_table;
7381 #endif
7382
7383         uint32_t* ct = 0;
7384         uint32_t* translated_ct = 0;
7385         short* bt = 0;
7386
7387         size_t cs = size_card_of (saved_g_lowest_address, saved_g_highest_address);
7388         size_t bs = size_brick_of (saved_g_lowest_address, saved_g_highest_address);
7389
7390 #ifdef MARK_ARRAY
7391         size_t ms = (gc_heap::gc_can_use_concurrent ? 
7392                     size_mark_array_of (saved_g_lowest_address, saved_g_highest_address) :
7393                     0);
7394 #else
7395         size_t ms = 0;
7396 #endif //MARK_ARRAY
7397
7398         size_t cb = 0;
7399
7400 #ifdef CARD_BUNDLE
7401         if (can_use_write_watch_for_card_table())
7402         {
7403             cb = size_card_bundle_of (saved_g_lowest_address, saved_g_highest_address);
7404
7405 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7406             // If we're not manually managing the card bundles, we will need to use OS write
7407             // watch APIs over this region to track changes.
7408             virtual_reserve_flags |= VirtualReserveFlags::WriteWatch;
7409 #endif
7410         }
7411 #endif //CARD_BUNDLE
7412
7413         size_t wws = 0;
7414 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7415         size_t sw_ww_table_offset = 0;
7416         if (gc_can_use_concurrent)
7417         {
7418             size_t sw_ww_size_before_table = sizeof(card_table_info) + cs + bs + cb;
7419             sw_ww_table_offset = SoftwareWriteWatch::GetTableStartByteOffset(sw_ww_size_before_table);
7420             wws =
7421                 sw_ww_table_offset -
7422                 sw_ww_size_before_table +
7423                 SoftwareWriteWatch::GetTableByteSize(saved_g_lowest_address, saved_g_highest_address);
7424         }
7425 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7426
7427 #ifdef GROWABLE_SEG_MAPPING_TABLE
7428         size_t st = size_seg_mapping_table_of (saved_g_lowest_address, saved_g_highest_address);
7429         size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws;
7430         size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset);
7431         st += (st_table_offset_aligned - st_table_offset);
7432 #else //GROWABLE_SEG_MAPPING_TABLE
7433         size_t st = 0;
7434 #endif //GROWABLE_SEG_MAPPING_TABLE
7435
7436         // it is impossible for alloc_size to overflow due bounds on each of 
7437         // its components.
7438         size_t alloc_size = sizeof (uint8_t)*(sizeof(card_table_info) + cs + bs + cb + wws + st + ms);
7439         dprintf (GC_TABLE_LOG, ("card table: %Id; brick table: %Id; card bundle: %Id; sw ww table: %Id; seg table: %Id; mark array: %Id",
7440                                   cs, bs, cb, wws, st, ms));
7441
7442         uint8_t* mem = (uint8_t*)GCToOSInterface::VirtualReserve (alloc_size, 0, virtual_reserve_flags);
7443
7444         if (!mem)
7445         {
7446             set_fgm_result (fgm_grow_table, alloc_size, loh_p);
7447             goto fail;
7448         }
7449
7450         dprintf (GC_TABLE_LOG, ("Table alloc for %Id bytes: [%Ix, %Ix[",
7451                                  alloc_size, (size_t)mem, (size_t)((uint8_t*)mem+alloc_size)));
7452
7453         {   
7454             // mark array will be committed separately (per segment).
7455             size_t commit_size = alloc_size - ms;
7456
7457             if (!virtual_commit (mem, commit_size))
7458             {
7459                 dprintf (GC_TABLE_LOG, ("Table commit failed"));
7460                 set_fgm_result (fgm_commit_table, commit_size, loh_p);
7461                 goto fail;
7462             }
7463         }
7464
7465         ct = (uint32_t*)(mem + sizeof (card_table_info));
7466         card_table_refcount (ct) = 0;
7467         card_table_lowest_address (ct) = saved_g_lowest_address;
7468         card_table_highest_address (ct) = saved_g_highest_address;
7469         card_table_next (ct) = &g_gc_card_table[card_word (gcard_of (la))];
7470
7471         //clear the card table
7472 /*
7473         memclr ((uint8_t*)ct,
7474                 (((saved_g_highest_address - saved_g_lowest_address)*sizeof (uint32_t) /
7475                   (card_size * card_word_width))
7476                  + sizeof (uint32_t)));
7477 */
7478
7479         bt = (short*)((uint8_t*)ct + cs);
7480
7481         // No initialization needed, will be done in copy_brick_card
7482
7483         card_table_brick_table (ct) = bt;
7484
7485 #ifdef CARD_BUNDLE
7486         card_table_card_bundle_table (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs);
7487         //set all bundle to look at all of the cards
7488         memset(card_table_card_bundle_table (ct), 0xFF, cb);
7489 #endif //CARD_BUNDLE
7490
7491 #ifdef GROWABLE_SEG_MAPPING_TABLE
7492         {
7493             new_seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned);
7494             new_seg_mapping_table = (seg_mapping*)((uint8_t*)new_seg_mapping_table -
7495                                               size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address))));
7496             memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
7497                 &seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)],
7498                 size_seg_mapping_table_of(g_gc_lowest_address, g_gc_highest_address));
7499
7500             // new_seg_mapping_table gets assigned to seg_mapping_table at the bottom of this function,
7501             // not here. The reason for this is that, if we fail at mark array committing (OOM) and we've
7502             // already switched seg_mapping_table to point to the new mapping table, we'll decommit it and
7503             // run into trouble. By not assigning here, we're making sure that we will not change seg_mapping_table
7504             // if an OOM occurs.
7505         }
7506 #endif //GROWABLE_SEG_MAPPING_TABLE
7507
7508 #ifdef MARK_ARRAY
7509         if(gc_can_use_concurrent)
7510             card_table_mark_array (ct) = (uint32_t*)((uint8_t*)card_table_brick_table (ct) + bs + cb + wws + st);
7511         else
7512             card_table_mark_array (ct) = NULL;
7513 #endif //MARK_ARRAY
7514
7515         translated_ct = translate_card_table (ct);
7516
7517         dprintf (GC_TABLE_LOG, ("card table: %Ix(translated: %Ix), seg map: %Ix, mark array: %Ix", 
7518             (size_t)ct, (size_t)translated_ct, (size_t)new_seg_mapping_table, (size_t)card_table_mark_array (ct)));
7519
7520 #ifdef BACKGROUND_GC
7521         if (hp->should_commit_mark_array())
7522         {
7523             dprintf (GC_TABLE_LOG, ("new low: %Ix, new high: %Ix, latest mark array is %Ix(translate: %Ix)", 
7524                                     saved_g_lowest_address, saved_g_highest_address,
7525                                     card_table_mark_array (ct),
7526                                     translate_mark_array (card_table_mark_array (ct))));
7527             uint32_t* new_mark_array = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, saved_g_lowest_address));
7528             if (!commit_new_mark_array_global (new_mark_array))
7529             {
7530                 dprintf (GC_TABLE_LOG, ("failed to commit portions in the mark array for existing segments"));
7531                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7532                 goto fail;
7533             }
7534
7535             if (!commit_mark_array_new_seg (hp, new_seg, translated_ct, saved_g_lowest_address))
7536             {
7537                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg"));
7538                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7539                 goto fail;
7540             }
7541         }
7542         else
7543         {
7544             clear_commit_flag_global();
7545         }
7546 #endif //BACKGROUND_GC
7547
7548 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7549         if (gc_can_use_concurrent)
7550         {
7551             // The current design of software write watch requires that the runtime is suspended during resize. Suspending
7552             // on resize is preferred because it is a far less frequent operation than GetWriteWatch() / ResetWriteWatch().
7553             // Suspending here allows copying dirty state from the old table into the new table, and not have to merge old
7554             // table info lazily as done for card tables.
7555
7556             // Either this thread was the thread that did the suspension which means we are suspended; or this is called
7557             // from a GC thread which means we are in a blocking GC and also suspended.
7558             bool is_runtime_suspended = GCToEEInterface::IsGCThread();
7559             if (!is_runtime_suspended)
7560             {
7561                 // Note on points where the runtime is suspended anywhere in this function. Upon an attempt to suspend the
7562                 // runtime, a different thread may suspend first, causing this thread to block at the point of the suspend call.
7563                 // So, at any suspend point, externally visible state needs to be consistent, as code that depends on that state
7564                 // may run while this thread is blocked. This includes updates to g_gc_card_table, g_gc_lowest_address, and
7565                 // g_gc_highest_address.
7566                 suspend_EE();
7567             }
7568
7569             g_gc_card_table = translated_ct;
7570
7571 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7572             g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), saved_g_lowest_address);
7573 #endif
7574
7575             SoftwareWriteWatch::SetResizedUntranslatedTable(
7576                 mem + sw_ww_table_offset,
7577                 saved_g_lowest_address,
7578                 saved_g_highest_address);
7579
7580             seg_mapping_table = new_seg_mapping_table;
7581
7582             // Since the runtime is already suspended, update the write barrier here as well.
7583             // This passes a bool telling whether we need to switch to the post
7584             // grow version of the write barrier.  This test tells us if the new
7585             // segment was allocated at a lower address than the old, requiring
7586             // that we start doing an upper bounds check in the write barrier.
7587             g_gc_lowest_address = saved_g_lowest_address;
7588             g_gc_highest_address = saved_g_highest_address;
7589             stomp_write_barrier_resize(true, la != saved_g_lowest_address);
7590             write_barrier_updated = true;
7591
7592             if (!is_runtime_suspended)
7593             {
7594                 restart_EE();
7595             }
7596         }
7597         else
7598 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
7599         {
7600             g_gc_card_table = translated_ct;
7601
7602 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7603             g_gc_card_bundle_table = translate_card_bundle_table(card_table_card_bundle_table(ct), saved_g_lowest_address);
7604 #endif
7605         }
7606
7607         if (!write_barrier_updated)
7608         {
7609             seg_mapping_table = new_seg_mapping_table;
7610             GCToOSInterface::FlushProcessWriteBuffers();
7611             g_gc_lowest_address = saved_g_lowest_address;
7612             g_gc_highest_address = saved_g_highest_address;
7613
7614             // This passes a bool telling whether we need to switch to the post
7615             // grow version of the write barrier.  This test tells us if the new
7616             // segment was allocated at a lower address than the old, requiring
7617             // that we start doing an upper bounds check in the write barrier.
7618             // This will also suspend the runtime if the write barrier type needs
7619             // to be changed, so we are doing this after all global state has
7620             // been updated. See the comment above suspend_EE() above for more
7621             // info.
7622             stomp_write_barrier_resize(GCToEEInterface::IsGCThread(), la != saved_g_lowest_address);
7623         }
7624
7625         return 0;
7626         
7627 fail:
7628         //cleanup mess and return -1;
7629
7630         if (mem)
7631         {
7632             assert(g_gc_card_table == saved_g_card_table);
7633
7634 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7635             assert(g_gc_card_bundle_table  == saved_g_card_bundle_table);
7636 #endif
7637
7638             //delete (uint32_t*)((uint8_t*)ct - sizeof(card_table_info));
7639             if (!GCToOSInterface::VirtualRelease (mem, alloc_size))
7640             {
7641                 dprintf (GC_TABLE_LOG, ("GCToOSInterface::VirtualRelease failed"));
7642                 assert (!"release failed");
7643             }
7644         }
7645
7646         return -1;
7647     }
7648     else
7649     {
7650 #ifdef BACKGROUND_GC
7651         if (hp->should_commit_mark_array())
7652         {
7653             dprintf (GC_TABLE_LOG, ("in range new seg %Ix, mark_array is %Ix", new_seg, hp->mark_array));
7654             if (!commit_mark_array_new_seg (hp, new_seg))
7655             {
7656                 dprintf (GC_TABLE_LOG, ("failed to commit mark array for the new seg in range"));
7657                 set_fgm_result (fgm_commit_table, logging_ma_commit_size, loh_p);
7658                 return -1;
7659             }
7660         }
7661 #endif //BACKGROUND_GC
7662     }
7663
7664     return 0;
7665 }
7666
7667 //copy all of the arrays managed by the card table for a page aligned range
7668 void gc_heap::copy_brick_card_range (uint8_t* la, uint32_t* old_card_table,
7669                                      short* old_brick_table,
7670                                      heap_segment* seg,
7671                                      uint8_t* start, uint8_t* end)
7672 {
7673     ptrdiff_t brick_offset = brick_of (start) - brick_of (la);
7674
7675
7676     dprintf (2, ("copying tables for range [%Ix %Ix[", (size_t)start, (size_t)end));
7677
7678     // copy brick table
7679     short* brick_start = &brick_table [brick_of (start)];
7680     if (old_brick_table)
7681     {
7682         // segments are always on page boundaries
7683         memcpy (brick_start, &old_brick_table[brick_offset],
7684                 size_brick_of (start, end));
7685
7686     }
7687     else
7688     {
7689         // This is a large heap, just clear the brick table
7690     }
7691
7692     uint32_t* old_ct = &old_card_table[card_word (card_of (la))];
7693 #ifdef MARK_ARRAY
7694 #ifdef BACKGROUND_GC
7695     UNREFERENCED_PARAMETER(seg);
7696     if (recursive_gc_sync::background_running_p())
7697     {
7698         uint32_t* old_mark_array = card_table_mark_array (old_ct);
7699
7700         // We don't need to go through all the card tables here because 
7701         // we only need to copy from the GC version of the mark array - when we
7702         // mark (even in allocate_large_object) we always use that mark array.
7703         if ((card_table_highest_address (old_ct) >= start) &&
7704             (card_table_lowest_address (old_ct) <= end))
7705         {
7706             if ((background_saved_highest_address >= start) &&
7707                 (background_saved_lowest_address <= end))
7708             {
7709                 //copy the mark bits
7710                 // segments are always on page boundaries
7711                 uint8_t* m_start = max (background_saved_lowest_address, start);
7712                 uint8_t* m_end = min (background_saved_highest_address, end);
7713                 memcpy (&mark_array[mark_word_of (m_start)],
7714                         &old_mark_array[mark_word_of (m_start) - mark_word_of (la)],
7715                         size_mark_array_of (m_start, m_end));
7716             }
7717         }
7718         else
7719         {
7720             //only large segments can be out of range
7721             assert (old_brick_table == 0);
7722         }
7723     }
7724 #else //BACKGROUND_GC
7725     assert (seg != 0);
7726     clear_mark_array (start, heap_segment_committed(seg));
7727 #endif //BACKGROUND_GC
7728 #endif //MARK_ARRAY
7729
7730     // n way merge with all of the card table ever used in between
7731     uint32_t* ct = card_table_next (&card_table[card_word (card_of(lowest_address))]);
7732
7733     assert (ct);
7734     while (card_table_next (old_ct) != ct)
7735     {
7736         //copy if old card table contained [start, end[
7737         if ((card_table_highest_address (ct) >= end) &&
7738             (card_table_lowest_address (ct) <= start))
7739         {
7740             // or the card_tables
7741
7742             size_t start_word = card_word (card_of (start));
7743
7744             uint32_t* dest = &card_table[start_word];
7745             uint32_t* src = &((translate_card_table (ct))[start_word]);
7746             ptrdiff_t count = count_card_of (start, end);
7747             for (int x = 0; x < count; x++)
7748             {
7749                 *dest |= *src;
7750
7751 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
7752                 if (*src != 0)
7753                 {
7754                     card_bundle_set(cardw_card_bundle(start_word+x));
7755                 }
7756 #endif
7757
7758                 dest++;
7759                 src++;
7760             }
7761         }
7762         ct = card_table_next (ct);
7763     }
7764 }
7765
7766 //initialize all of the arrays managed by the card table for a page aligned range when an existing ro segment becomes in range
7767 void gc_heap::init_brick_card_range (heap_segment* seg)
7768 {
7769     dprintf (2, ("initialising tables for range [%Ix %Ix[",
7770                  (size_t)heap_segment_mem (seg),
7771                  (size_t)heap_segment_allocated (seg)));
7772
7773     // initialize the brick table
7774     for (size_t b = brick_of (heap_segment_mem (seg));
7775          b < brick_of (align_on_brick (heap_segment_allocated (seg)));
7776          b++)
7777     {
7778         set_brick (b, -1);
7779     }
7780
7781 #ifdef MARK_ARRAY
7782     if (recursive_gc_sync::background_running_p() && (seg->flags & heap_segment_flags_ma_committed))
7783     {
7784         assert (seg != 0);
7785         clear_mark_array (heap_segment_mem (seg), heap_segment_committed(seg));
7786     }
7787 #endif //MARK_ARRAY
7788
7789     clear_card_for_addresses (heap_segment_mem (seg),
7790                               heap_segment_allocated (seg));
7791 }
7792
7793 void gc_heap::copy_brick_card_table()
7794 {
7795     uint8_t* la = lowest_address;
7796     uint8_t* ha = highest_address;
7797     MAYBE_UNUSED_VAR(ha);
7798     uint32_t* old_card_table = card_table;
7799     short* old_brick_table = brick_table;
7800
7801     assert (la == card_table_lowest_address (&old_card_table[card_word (card_of (la))]));
7802     assert (ha == card_table_highest_address (&old_card_table[card_word (card_of (la))]));
7803
7804     /* todo: Need a global lock for this */
7805     uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
7806     own_card_table (ct);
7807     card_table = translate_card_table (ct);
7808     /* End of global lock */
7809     highest_address = card_table_highest_address (ct);
7810     lowest_address = card_table_lowest_address (ct);
7811
7812     brick_table = card_table_brick_table (ct);
7813
7814 #ifdef MARK_ARRAY
7815     if (gc_can_use_concurrent)
7816     {
7817         mark_array = translate_mark_array (card_table_mark_array (ct));
7818         assert (mark_word_of (g_gc_highest_address) ==
7819             mark_word_of (align_on_mark_word (g_gc_highest_address)));
7820     }
7821     else
7822         mark_array = NULL;
7823 #endif //MARK_ARRAY
7824
7825 #ifdef CARD_BUNDLE
7826 #if defined(MARK_ARRAY) && defined(_DEBUG)
7827     size_t cb_end = (size_t)((uint8_t*)card_table_card_bundle_table (ct) + size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address));
7828 #ifdef GROWABLE_SEG_MAPPING_TABLE
7829     size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address);
7830     size_t cb_end_aligned = align_for_seg_mapping_table (cb_end);
7831     st += (cb_end_aligned - cb_end);
7832 #else  //GROWABLE_SEG_MAPPING_TABLE
7833     size_t st = 0;
7834 #endif //GROWABLE_SEG_MAPPING_TABLE
7835 #endif //MARK_ARRAY && _DEBUG
7836     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address);
7837
7838     // Ensure that the word that represents g_gc_lowest_address in the translated table is located at the
7839     // start of the untranslated table.
7840     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
7841             card_table_card_bundle_table (ct));
7842
7843     //set the card table if we are in a heap growth scenario
7844     if (card_bundles_enabled())
7845     {
7846         card_bundles_set (cardw_card_bundle (card_word (card_of (lowest_address))),
7847                           cardw_card_bundle (align_cardw_on_bundle (card_word (card_of (highest_address)))));
7848     }
7849     //check if we need to turn on card_bundles.
7850 #ifdef MULTIPLE_HEAPS
7851     // use INT64 arithmetic here because of possible overflow on 32p
7852     uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*gc_heap::n_heaps;
7853 #else
7854     // use INT64 arithmetic here because of possible overflow on 32p
7855     uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
7856 #endif //MULTIPLE_HEAPS
7857     if (reserved_memory >= th)
7858     {
7859         enable_card_bundles();
7860     }
7861
7862 #endif //CARD_BUNDLE
7863
7864     // for each of the segments and heaps, copy the brick table and
7865     // or the card table
7866     heap_segment* seg = generation_start_segment (generation_of (max_generation));
7867     while (seg)
7868     {
7869         if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7870         {
7871             //check if it became in range
7872             if ((heap_segment_reserved (seg) > lowest_address) &&
7873                 (heap_segment_mem (seg) < highest_address))
7874             {
7875                 set_ro_segment_in_range (seg);
7876             }
7877         }
7878         else
7879         {
7880
7881             uint8_t* end = align_on_page (heap_segment_allocated (seg));
7882             copy_brick_card_range (la, old_card_table,
7883                                    old_brick_table,
7884                                    seg,
7885                                    align_lower_page (heap_segment_mem (seg)),
7886                                    end);
7887         }
7888         seg = heap_segment_next (seg);
7889     }
7890
7891     seg = generation_start_segment (large_object_generation);
7892     while (seg)
7893     {
7894         if (heap_segment_read_only_p (seg) && !heap_segment_in_range_p (seg))
7895         {
7896             //check if it became in range
7897             if ((heap_segment_reserved (seg) > lowest_address) &&
7898                 (heap_segment_mem (seg) < highest_address))
7899             {
7900                 set_ro_segment_in_range (seg);
7901             }
7902         }
7903         else
7904         {
7905             uint8_t* end = align_on_page (heap_segment_allocated (seg));
7906             copy_brick_card_range (la, old_card_table,
7907                                    0,
7908                                    seg,
7909                                    align_lower_page (heap_segment_mem (seg)),
7910                                    end);
7911         }
7912         seg = heap_segment_next (seg);
7913     }
7914
7915     release_card_table (&old_card_table[card_word (card_of(la))]);
7916 }
7917
7918 #ifdef FEATURE_BASICFREEZE
7919 BOOL gc_heap::insert_ro_segment (heap_segment* seg)
7920 {
7921     enter_spin_lock (&gc_heap::gc_lock);
7922
7923     if (!gc_heap::seg_table->ensure_space_for_insert ()
7924         || (should_commit_mark_array() && !commit_mark_array_new_seg(__this, seg)))
7925     {
7926         leave_spin_lock(&gc_heap::gc_lock);
7927         return FALSE;
7928     }
7929
7930     //insert at the head of the segment list
7931     generation* gen2 = generation_of (max_generation);
7932     heap_segment* oldhead = generation_start_segment (gen2);
7933     heap_segment_next (seg) = oldhead;
7934     generation_start_segment (gen2) = seg;
7935
7936     seg_table->insert (heap_segment_mem(seg), (size_t)seg);
7937
7938 #ifdef SEG_MAPPING_TABLE
7939     seg_mapping_table_add_ro_segment (seg);
7940 #endif //SEG_MAPPING_TABLE
7941
7942     //test if in range
7943     if ((heap_segment_reserved (seg) > lowest_address) &&
7944         (heap_segment_mem (seg) < highest_address))
7945     {
7946         set_ro_segment_in_range (seg);
7947     }
7948
7949     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg), (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)), gc_etw_segment_read_only_heap);
7950
7951     leave_spin_lock (&gc_heap::gc_lock);
7952     return TRUE;
7953 }
7954
7955 // No one is calling this function right now. If this is getting called we need
7956 // to take care of decommitting the mark array for it - we will need to remember
7957 // which portion of the mark array was committed and only decommit that.
7958 void gc_heap::remove_ro_segment (heap_segment* seg)
7959 {
7960 //clear the mark bits so a new segment allocated in its place will have a clear mark bits
7961 #ifdef MARK_ARRAY
7962     if (gc_can_use_concurrent)
7963     {
7964         clear_mark_array (align_lower_mark_word (max (heap_segment_mem (seg), lowest_address)),
7965                       align_on_card_word (min (heap_segment_allocated (seg), highest_address)),
7966                       false); // read_only segments need the mark clear
7967     }
7968 #endif //MARK_ARRAY
7969
7970     enter_spin_lock (&gc_heap::gc_lock);
7971
7972     seg_table->remove ((uint8_t*)seg);
7973
7974 #ifdef SEG_MAPPING_TABLE
7975     seg_mapping_table_remove_ro_segment (seg);
7976 #endif //SEG_MAPPING_TABLE
7977
7978     // Locate segment (and previous segment) in the list.
7979     generation* gen2 = generation_of (max_generation);
7980     heap_segment* curr_seg = generation_start_segment (gen2);
7981     heap_segment* prev_seg = NULL;
7982
7983     while (curr_seg && curr_seg != seg)
7984     {
7985         prev_seg = curr_seg;
7986         curr_seg = heap_segment_next (curr_seg);
7987     }
7988     assert (curr_seg == seg);
7989
7990     // Patch previous segment (or list head if there is none) to skip the removed segment.
7991     if (prev_seg)
7992         heap_segment_next (prev_seg) = heap_segment_next (curr_seg);
7993     else
7994         generation_start_segment (gen2) = heap_segment_next (curr_seg);
7995
7996     leave_spin_lock (&gc_heap::gc_lock);
7997 }
7998 #endif //FEATURE_BASICFREEZE
7999
8000 BOOL gc_heap::set_ro_segment_in_range (heap_segment* seg)
8001 {
8002     //set it in range
8003     seg->flags |= heap_segment_flags_inrange;
8004 //    init_brick_card_range (seg);
8005     ro_segments_in_range = TRUE;
8006     //right now, segments aren't protected
8007     //unprotect_segment (seg);
8008     return TRUE;
8009 }
8010
8011 #ifdef MARK_LIST
8012
8013 uint8_t** make_mark_list (size_t size)
8014 {
8015     uint8_t** mark_list = new (nothrow) uint8_t* [size];
8016     return mark_list;
8017 }
8018
8019 #define swap(a,b){uint8_t* t; t = a; a = b; b = t;}
8020
8021 void verify_qsort_array (uint8_t* *low, uint8_t* *high)
8022 {
8023     uint8_t **i = 0;
8024
8025     for (i = low+1; i <= high; i++)
8026     {
8027         if (*i < *(i-1))
8028         {
8029             FATAL_GC_ERROR();
8030         }
8031     }
8032 }
8033
8034 #ifndef USE_INTROSORT
8035 void qsort1( uint8_t* *low, uint8_t* *high, unsigned int depth)
8036 {
8037     if (((low + 16) >= high) || (depth > 100))
8038     {
8039         //insertion sort
8040         uint8_t **i, **j;
8041         for (i = low+1; i <= high; i++)
8042         {
8043             uint8_t* val = *i;
8044             for (j=i;j >low && val<*(j-1);j--)
8045             {
8046                 *j=*(j-1);
8047             }
8048             *j=val;
8049         }
8050     }
8051     else
8052     {
8053         uint8_t *pivot, **left, **right;
8054
8055         //sort low middle and high
8056         if (*(low+((high-low)/2)) < *low)
8057             swap (*(low+((high-low)/2)), *low);
8058         if (*high < *low)
8059             swap (*low, *high);
8060         if (*high < *(low+((high-low)/2)))
8061             swap (*(low+((high-low)/2)), *high);
8062
8063         swap (*(low+((high-low)/2)), *(high-1));
8064         pivot =  *(high-1);
8065         left = low; right = high-1;
8066         while (1) {
8067             while (*(--right) > pivot);
8068             while (*(++left)  < pivot);
8069             if (left < right)
8070             {
8071                 swap(*left, *right);
8072             }
8073             else
8074                 break;
8075         }
8076         swap (*left, *(high-1));
8077         qsort1(low, left-1, depth+1);
8078         qsort1(left+1, high, depth+1);
8079     }
8080 }
8081 #endif //USE_INTROSORT
8082 void rqsort1( uint8_t* *low, uint8_t* *high)
8083 {
8084     if ((low + 16) >= high)
8085     {
8086         //insertion sort
8087         uint8_t **i, **j;
8088         for (i = low+1; i <= high; i++)
8089         {
8090             uint8_t* val = *i;
8091             for (j=i;j >low && val>*(j-1);j--)
8092             {
8093                 *j=*(j-1);
8094             }
8095             *j=val;
8096         }
8097     }
8098     else
8099     {
8100         uint8_t *pivot, **left, **right;
8101
8102         //sort low middle and high
8103         if (*(low+((high-low)/2)) > *low)
8104             swap (*(low+((high-low)/2)), *low);
8105         if (*high > *low)
8106             swap (*low, *high);
8107         if (*high > *(low+((high-low)/2)))
8108             swap (*(low+((high-low)/2)), *high);
8109
8110         swap (*(low+((high-low)/2)), *(high-1));
8111         pivot =  *(high-1);
8112         left = low; right = high-1;
8113         while (1) {
8114             while (*(--right) < pivot);
8115             while (*(++left)  > pivot);
8116             if (left < right)
8117             {
8118                 swap(*left, *right);
8119             }
8120             else
8121                 break;
8122         }
8123         swap (*left, *(high-1));
8124         rqsort1(low, left-1);
8125         rqsort1(left+1, high);
8126     }
8127 }
8128
8129 #ifdef USE_INTROSORT
8130 class introsort 
8131 {
8132
8133 private: 
8134     static const int size_threshold = 64;
8135     static const int max_depth = 100;
8136
8137
8138 inline static void swap_elements(uint8_t** i,uint8_t** j)
8139     {
8140         uint8_t* t=*i;
8141         *i=*j; 
8142         *j=t;
8143     }
8144
8145 public:
8146     static void sort (uint8_t** begin, uint8_t** end, int ignored)
8147     {
8148         ignored = 0;
8149         introsort_loop (begin, end, max_depth);
8150         insertionsort (begin, end);
8151     }
8152
8153 private: 
8154
8155     static void introsort_loop (uint8_t** lo, uint8_t** hi, int depth_limit)
8156     {
8157         while (hi-lo >= size_threshold)
8158         {
8159             if (depth_limit == 0)
8160             {
8161                 heapsort (lo, hi);
8162                 return;
8163             }
8164             uint8_t** p=median_partition (lo, hi);
8165             depth_limit=depth_limit-1;
8166             introsort_loop (p, hi, depth_limit);
8167             hi=p-1;
8168         }        
8169     }
8170
8171     static uint8_t** median_partition (uint8_t** low, uint8_t** high)
8172     {
8173         uint8_t *pivot, **left, **right;
8174
8175         //sort low middle and high
8176         if (*(low+((high-low)/2)) < *low)
8177             swap_elements ((low+((high-low)/2)), low);
8178         if (*high < *low)
8179             swap_elements (low, high);
8180         if (*high < *(low+((high-low)/2)))
8181             swap_elements ((low+((high-low)/2)), high);
8182
8183         swap_elements ((low+((high-low)/2)), (high-1));
8184         pivot =  *(high-1);
8185         left = low; right = high-1;
8186         while (1) {
8187             while (*(--right) > pivot);
8188             while (*(++left)  < pivot);
8189             if (left < right)
8190             {
8191                 swap_elements(left, right);
8192             }
8193             else
8194                 break;
8195         }
8196         swap_elements (left, (high-1));
8197         return left;
8198     }
8199
8200
8201     static void insertionsort (uint8_t** lo, uint8_t** hi)
8202     {
8203         for (uint8_t** i=lo+1; i <= hi; i++)
8204         {
8205             uint8_t** j = i;
8206             uint8_t* t = *i;
8207             while((j > lo) && (t <*(j-1)))
8208             {
8209                 *j = *(j-1);
8210                 j--;
8211             }
8212             *j = t;
8213         }
8214     }
8215
8216     static void heapsort (uint8_t** lo, uint8_t** hi)
8217     { 
8218         size_t n = hi - lo + 1;
8219         for (size_t i=n / 2; i >= 1; i--)
8220         {
8221             downheap (i,n,lo);
8222         }
8223         for (size_t i = n; i > 1; i--)
8224         {
8225             swap_elements (lo, lo + i - 1);
8226             downheap(1, i - 1,  lo);
8227         }
8228     }
8229
8230     static void downheap (size_t i, size_t n, uint8_t** lo)
8231     {
8232         uint8_t* d = *(lo + i - 1);
8233         size_t child;
8234         while (i <= n / 2)
8235         {
8236             child = 2*i;
8237             if (child < n && *(lo + child - 1)<(*(lo + child)))
8238             {
8239                 child++;
8240             }
8241             if (!(d<*(lo + child - 1))) 
8242             {
8243                 break;
8244             }
8245             *(lo + i - 1) = *(lo + child - 1);
8246             i = child;
8247         }
8248         *(lo + i - 1) = d;
8249     }
8250
8251 };
8252
8253 #endif //USE_INTROSORT    
8254
8255 #ifdef MULTIPLE_HEAPS
8256 #ifdef PARALLEL_MARK_LIST_SORT
8257 void gc_heap::sort_mark_list()
8258 {
8259     // if this heap had a mark list overflow, we don't do anything
8260     if (mark_list_index > mark_list_end)
8261     {
8262 //        printf("sort_mark_list: overflow on heap %d\n", heap_number);
8263         return;
8264     }
8265
8266     // if any other heap had a mark list overflow, we fake one too,
8267     // so we don't use an incomplete mark list by mistake
8268     for (int i = 0; i < n_heaps; i++)
8269     {
8270         if (g_heaps[i]->mark_list_index > g_heaps[i]->mark_list_end)
8271         {
8272             mark_list_index = mark_list_end + 1;
8273 //            printf("sort_mark_list: overflow on heap %d\n", i);
8274             return;
8275         }
8276     }
8277
8278 //    unsigned long start = GetCycleCount32();
8279
8280     dprintf (3, ("Sorting mark lists"));
8281     if (mark_list_index > mark_list)
8282         _sort (mark_list, mark_list_index - 1, 0);
8283
8284 //    printf("first phase of sort_mark_list for heap %d took %u cycles to sort %u entries\n", this->heap_number, GetCycleCount32() - start, mark_list_index - mark_list);
8285 //    start = GetCycleCount32();
8286
8287     // first set the pieces for all heaps to empty
8288     int heap_num;
8289     for (heap_num = 0; heap_num < n_heaps; heap_num++)
8290     {
8291         mark_list_piece_start[heap_num] = NULL;
8292         mark_list_piece_end[heap_num] = NULL;
8293     }
8294
8295     uint8_t** x = mark_list;
8296
8297 // predicate means: x is still within the mark list, and within the bounds of this heap
8298 #define predicate(x) (((x) < mark_list_index) && (*(x) < heap->ephemeral_high))
8299
8300     heap_num = -1;
8301     while (x < mark_list_index)
8302     {
8303         gc_heap* heap;
8304         // find the heap x points into - searching cyclically from the last heap,
8305         // because in many cases the right heap is the next one or comes soon after
8306         int last_heap_num = heap_num;
8307         MAYBE_UNUSED_VAR(last_heap_num);
8308         do
8309         {
8310             heap_num++;
8311             if (heap_num >= n_heaps)
8312                 heap_num = 0;
8313             assert(heap_num != last_heap_num); // we should always find the heap - infinite loop if not!
8314             heap = g_heaps[heap_num];
8315         }
8316         while (!(*x >= heap->ephemeral_low && *x < heap->ephemeral_high));
8317
8318         // x is the start of the mark list piece for this heap
8319         mark_list_piece_start[heap_num] = x;
8320
8321         // to find the end of the mark list piece for this heap, find the first x
8322         // that has !predicate(x), i.e. that is either not in this heap, or beyond the end of the list
8323         if (predicate(x))
8324         {
8325             // let's see if we get lucky and the whole rest belongs to this piece
8326             if (predicate(mark_list_index-1))
8327             {
8328                 x = mark_list_index;
8329                 mark_list_piece_end[heap_num] = x;
8330                 break;
8331             }
8332
8333             // we play a variant of binary search to find the point sooner.
8334             // the first loop advances by increasing steps until the predicate turns false.
8335             // then we retreat the last step, and the second loop advances by decreasing steps, keeping the predicate true.
8336             unsigned inc = 1;
8337             do
8338             {
8339                 inc *= 2;
8340                 uint8_t** temp_x = x;
8341                 x += inc;
8342                 if (temp_x > x)
8343                 {
8344                     break;
8345                 }
8346             }
8347             while (predicate(x));
8348             // we know that only the last step was wrong, so we undo it
8349             x -= inc;
8350             do
8351             {
8352                 // loop invariant - predicate holds at x, but not x + inc
8353                 assert (predicate(x) && !(((x + inc) > x) && predicate(x + inc)));
8354                 inc /= 2;
8355                 if (((x + inc) > x) && predicate(x + inc))
8356                 {
8357                     x += inc;
8358                 }
8359             }
8360             while (inc > 1);
8361             // the termination condition and the loop invariant together imply this:
8362             assert(predicate(x) && !predicate(x + inc) && (inc == 1));
8363             // so the spot we're looking for is one further
8364             x += 1;
8365         }
8366         mark_list_piece_end[heap_num] = x;
8367     }
8368
8369 #undef predicate
8370
8371 //    printf("second phase of sort_mark_list for heap %d took %u cycles\n", this->heap_number, GetCycleCount32() - start);
8372 }
8373
8374 void gc_heap::append_to_mark_list(uint8_t **start, uint8_t **end)
8375 {
8376     size_t slots_needed = end - start;
8377     size_t slots_available = mark_list_end + 1 - mark_list_index;
8378     size_t slots_to_copy = min(slots_needed, slots_available);
8379     memcpy(mark_list_index, start, slots_to_copy*sizeof(*start));
8380     mark_list_index += slots_to_copy;
8381 //    printf("heap %d: appended %Id slots to mark_list\n", heap_number, slots_to_copy);
8382 }
8383
8384 void gc_heap::merge_mark_lists()
8385 {
8386     uint8_t** source[MAX_SUPPORTED_CPUS];
8387     uint8_t** source_end[MAX_SUPPORTED_CPUS];
8388     int source_heap[MAX_SUPPORTED_CPUS];
8389     int source_count = 0;
8390
8391     // in case of mark list overflow, don't bother
8392     if (mark_list_index >  mark_list_end)
8393     {
8394 //        printf("merge_mark_lists: overflow\n");
8395         return;
8396     }
8397
8398     dprintf(3, ("merge_mark_lists: heap_number = %d  starts out with %Id entries", heap_number, mark_list_index - mark_list));
8399 //    unsigned long start = GetCycleCount32();
8400     for (int i = 0; i < n_heaps; i++)
8401     {
8402         gc_heap* heap = g_heaps[i];
8403         if (heap->mark_list_piece_start[heap_number] < heap->mark_list_piece_end[heap_number])
8404         {
8405             source[source_count] = heap->mark_list_piece_start[heap_number];
8406             source_end[source_count] = heap->mark_list_piece_end[heap_number];
8407             source_heap[source_count] = i;
8408             if (source_count < MAX_SUPPORTED_CPUS)
8409                 source_count++;
8410         }
8411     }
8412 //    printf("first phase of merge_mark_lists for heap %d took %u cycles\n", heap_number, GetCycleCount32() - start);
8413
8414     dprintf(3, ("heap_number = %d  has %d sources\n", heap_number, source_count));
8415 #if defined(_DEBUG) || defined(TRACE_GC)
8416     for (int j = 0; j < source_count; j++)
8417     {
8418         dprintf(3, ("heap_number = %d  ", heap_number));
8419         dprintf(3, (" source from heap %d = %Ix .. %Ix (%Id entries)",
8420             (size_t)(source_heap[j]), (size_t)(source[j][0]), (size_t)(source_end[j][-1]), (size_t)(source_end[j] - source[j])));
8421        // the sources should all be sorted
8422         for (uint8_t **x = source[j]; x < source_end[j] - 1; x++)
8423         {
8424             if (x[0] > x[1])
8425             {
8426                 dprintf(3, ("oops, mark_list from source %d for heap %d isn't sorted\n", j, heap_number));
8427                 assert (0);
8428             }
8429         }
8430     }
8431 #endif //_DEBUG || TRACE_GC
8432
8433 //    start = GetCycleCount32();
8434
8435     mark_list = &g_mark_list_copy [heap_number*mark_list_size];
8436     mark_list_index = mark_list;
8437     mark_list_end = &mark_list [mark_list_size-1];
8438     int piece_count = 0;
8439     if (source_count == 0)
8440     {
8441         ; // nothing to do
8442     }
8443     else if (source_count == 1)
8444     {
8445         mark_list = source[0];
8446         mark_list_index = source_end[0];
8447         mark_list_end = mark_list_index;
8448         piece_count++;
8449     }
8450     else
8451     {
8452         while (source_count > 1)
8453         {
8454             // find the lowest and second lowest value in the sources we're merging from
8455             int lowest_source = 0;
8456             uint8_t *lowest = *source[0];
8457             uint8_t *second_lowest = *source[1];
8458             for (int i = 1; i < source_count; i++)
8459             {
8460                 if (lowest > *source[i])
8461                 {
8462                     second_lowest = lowest;
8463                     lowest = *source[i];
8464                     lowest_source = i;
8465                 }
8466                 else if (second_lowest > *source[i])
8467                 {
8468                     second_lowest = *source[i];
8469                 }
8470             }
8471
8472             // find the point in the lowest source where it either runs out or is not <= second_lowest anymore
8473
8474             // let's first try to get lucky and see if the whole source is <= second_lowest -- this is actually quite common
8475             uint8_t **x;
8476             if (source_end[lowest_source][-1] <= second_lowest)
8477                 x = source_end[lowest_source];
8478             else
8479             {
8480                 // use linear search to find the end -- could also use binary search as in sort_mark_list,
8481                 // but saw no improvement doing that
8482                 for (x = source[lowest_source]; x < source_end[lowest_source] && *x <= second_lowest; x++)
8483                     ;
8484             }
8485
8486             // blast this piece to the mark list
8487             append_to_mark_list(source[lowest_source], x);
8488             piece_count++;
8489
8490             source[lowest_source] = x;
8491
8492             // check whether this source is now exhausted
8493             if (x >= source_end[lowest_source])
8494             {
8495                 // if it's not the source with the highest index, copy the source with the highest index
8496                 // over it so the non-empty sources are always at the beginning
8497                 if (lowest_source < source_count-1)
8498                 {
8499                     source[lowest_source] = source[source_count-1];
8500                     source_end[lowest_source] = source_end[source_count-1];
8501                 }
8502                 source_count--;
8503             }
8504         }
8505         // we're left with just one source that we copy
8506         append_to_mark_list(source[0], source_end[0]);
8507         piece_count++;
8508     }
8509
8510 //    printf("second phase of merge_mark_lists for heap %d took %u cycles to merge %d pieces\n", heap_number, GetCycleCount32() - start, piece_count);
8511
8512 #if defined(_DEBUG) || defined(TRACE_GC)
8513     // the final mark list must be sorted
8514     for (uint8_t **x = mark_list; x < mark_list_index - 1; x++)
8515     {
8516         if (x[0] > x[1])
8517         {
8518             dprintf(3, ("oops, mark_list for heap %d isn't sorted at the end of merge_mark_lists", heap_number));
8519             assert (0);
8520         }
8521     }
8522 #endif //defined(_DEBUG) || defined(TRACE_GC)
8523 }
8524 #else //PARALLEL_MARK_LIST_SORT
8525 void gc_heap::combine_mark_lists()
8526 {
8527     dprintf (3, ("Combining mark lists"));
8528     //verify if a heap has overflowed its mark list
8529     BOOL use_mark_list = TRUE;
8530     for (int i = 0; i < n_heaps; i++)
8531     {
8532         if (g_heaps [i]->mark_list_index >  g_heaps [i]->mark_list_end)
8533         {
8534             use_mark_list = FALSE;
8535             break;
8536         }
8537     }
8538
8539     if (use_mark_list)
8540     {
8541         dprintf (3, ("Using mark list"));
8542         //compact the gaps out of the mark list
8543         int gn = 0;
8544         uint8_t** current_gap = g_heaps [gn]->mark_list_index;
8545         uint8_t** current_gap_end = g_heaps[gn]->mark_list_end + 1;
8546         uint8_t** dst_last = current_gap-1;
8547
8548         int srcn = n_heaps-1;
8549         gc_heap* srch = g_heaps [srcn];
8550         uint8_t** src = srch->mark_list_index - 1;
8551         uint8_t** src_beg = srch->mark_list;
8552
8553         while (current_gap <= src)
8554         {
8555             while ((gn < n_heaps-1) && (current_gap >= current_gap_end))
8556             {
8557                 //go to the next gap
8558                 gn++;
8559                 dprintf (3, ("Going to the next gap %d", gn));
8560                 assert (gn < n_heaps);
8561                 current_gap = g_heaps [gn]->mark_list_index;
8562                 current_gap_end = g_heaps[gn]->mark_list_end + 1;
8563                 assert ((gn == (n_heaps-1)) || (current_gap_end == g_heaps[gn+1]->mark_list));
8564             }
8565             while ((srcn > 0) && (src < src_beg))
8566             {
8567                 //go to the previous source
8568                 srcn--;
8569                 dprintf (3, ("going to the previous source %d", srcn));
8570                 assert (srcn>=0);
8571                 gc_heap* srch = g_heaps [srcn];
8572                 src = srch->mark_list_index - 1;
8573                 src_beg = srch->mark_list;
8574             }
8575             if (current_gap < src)
8576             {
8577                 dst_last = current_gap;
8578                 *current_gap++ = *src--;
8579             }
8580         }
8581         dprintf (3, ("src: %Ix dst_last: %Ix", (size_t)src, (size_t)dst_last));
8582
8583         uint8_t** end_of_list = max (src, dst_last);
8584
8585         //sort the resulting compacted list
8586         assert (end_of_list < &g_mark_list [n_heaps*mark_list_size]);
8587         if (end_of_list > &g_mark_list[0])
8588             _sort (&g_mark_list[0], end_of_list, 0);
8589         //adjust the mark_list to the begining of the resulting mark list.
8590         for (int i = 0; i < n_heaps; i++)
8591         {
8592             g_heaps [i]->mark_list = g_mark_list;
8593             g_heaps [i]->mark_list_index = end_of_list + 1;
8594             g_heaps [i]->mark_list_end = end_of_list + 1;
8595         }
8596     }
8597     else
8598     {
8599         uint8_t** end_of_list = g_mark_list;
8600         //adjust the mark_list to the begining of the resulting mark list.
8601         //put the index beyond the end to turn off mark list processing
8602         for (int i = 0; i < n_heaps; i++)
8603         {
8604             g_heaps [i]->mark_list = g_mark_list;
8605             g_heaps [i]->mark_list_index = end_of_list + 1;
8606             g_heaps [i]->mark_list_end = end_of_list;
8607         }
8608     }
8609 }
8610 #endif // PARALLEL_MARK_LIST_SORT
8611 #endif //MULTIPLE_HEAPS
8612 #endif //MARK_LIST
8613
8614 class seg_free_spaces
8615 {
8616     struct seg_free_space
8617     {
8618         BOOL is_plug;
8619         void* start;
8620     };
8621
8622     struct free_space_bucket
8623     {
8624         seg_free_space* free_space;
8625         ptrdiff_t count_add; // Assigned when we first contruct the array.
8626         ptrdiff_t count_fit; // How many items left when we are fitting plugs.
8627     };
8628
8629     void move_bucket (int old_power2, int new_power2)
8630     {
8631         // PREFAST warning 22015: old_power2 could be negative
8632         assert (old_power2 >= 0);
8633         assert (old_power2 >= new_power2);
8634
8635         if (old_power2 == new_power2)
8636         {
8637             return;
8638         }
8639
8640         seg_free_space* src_index = free_space_buckets[old_power2].free_space;
8641         for (int i = old_power2; i > new_power2; i--)
8642         {
8643             seg_free_space** dest = &(free_space_buckets[i].free_space);
8644             (*dest)++;
8645
8646             seg_free_space* dest_index = free_space_buckets[i - 1].free_space;
8647             if (i > (new_power2 + 1))
8648             {
8649                 seg_free_space temp = *src_index;
8650                 *src_index = *dest_index;
8651                 *dest_index = temp;
8652             }
8653             src_index = dest_index;
8654         }
8655
8656         free_space_buckets[old_power2].count_fit--;
8657         free_space_buckets[new_power2].count_fit++;
8658     }
8659
8660 #ifdef _DEBUG
8661
8662     void dump_free_space (seg_free_space* item)
8663     {
8664         uint8_t* addr = 0;
8665         size_t len = 0;
8666
8667         if (item->is_plug)
8668         {
8669             mark* m = (mark*)(item->start);
8670             len = pinned_len (m);
8671             addr = pinned_plug (m) - len;
8672         }
8673         else
8674         {
8675             heap_segment* seg = (heap_segment*)(item->start);
8676             addr = heap_segment_plan_allocated (seg);
8677             len = heap_segment_committed (seg) - addr;
8678         }
8679
8680         dprintf (SEG_REUSE_LOG_1, ("[%d]0x%Ix %Id", heap_num, addr, len));
8681     }
8682
8683     void dump()
8684     {
8685         seg_free_space* item = NULL;
8686         int i = 0;
8687
8688         dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------\nnow the free spaces look like:", heap_num));
8689         for (i = 0; i < (free_space_bucket_count - 1); i++)
8690         {
8691             dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8692             dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8693             item = free_space_buckets[i].free_space;
8694             while (item < free_space_buckets[i + 1].free_space)
8695             {
8696                 dump_free_space (item);
8697                 item++;
8698             }
8699             dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8700         }
8701
8702         dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces for 2^%d bucket:", heap_num, (base_power2 + i)));
8703         dprintf (SEG_REUSE_LOG_1, ("[%d]%s %s", heap_num, "start", "len"));
8704         item = free_space_buckets[i].free_space;
8705
8706         while (item <= &seg_free_space_array[free_space_item_count - 1])
8707         {
8708             dump_free_space (item);
8709             item++;
8710         }
8711         dprintf (SEG_REUSE_LOG_1, ("[%d]----------------------------------", heap_num));
8712     }
8713
8714 #endif //_DEBUG
8715
8716     free_space_bucket* free_space_buckets;
8717     seg_free_space* seg_free_space_array;
8718     ptrdiff_t free_space_bucket_count;
8719     ptrdiff_t free_space_item_count;
8720     int base_power2;
8721     int heap_num;
8722 #ifdef _DEBUG
8723     BOOL has_end_of_seg;
8724 #endif //_DEBUG
8725
8726 public:
8727
8728     seg_free_spaces (int h_number)
8729     {
8730         heap_num = h_number;
8731     }
8732
8733     BOOL alloc ()
8734     {
8735         size_t total_prealloc_size = 
8736             MAX_NUM_BUCKETS * sizeof (free_space_bucket) +
8737             MAX_NUM_FREE_SPACES * sizeof (seg_free_space);
8738
8739         free_space_buckets = (free_space_bucket*) new (nothrow) uint8_t[total_prealloc_size];
8740
8741         return (!!free_space_buckets);
8742     }
8743
8744     // We take the ordered free space array we got from the 1st pass,
8745     // and feed the portion that we decided to use to this method, ie,
8746     // the largest item_count free spaces.
8747     void add_buckets (int base, size_t* ordered_free_spaces, int bucket_count, size_t item_count)
8748     {
8749         assert (free_space_buckets);
8750         assert (item_count <= (size_t)MAX_PTR);
8751
8752         free_space_bucket_count = bucket_count;
8753         free_space_item_count = item_count;
8754         base_power2 = base;
8755 #ifdef _DEBUG
8756         has_end_of_seg = FALSE;
8757 #endif //_DEBUG
8758
8759         ptrdiff_t total_item_count = 0;
8760         ptrdiff_t i = 0;
8761
8762         seg_free_space_array = (seg_free_space*)(free_space_buckets + free_space_bucket_count);
8763
8764         for (i = 0; i < (ptrdiff_t)item_count; i++)
8765         {
8766             seg_free_space_array[i].start = 0;
8767             seg_free_space_array[i].is_plug = FALSE;
8768         }
8769
8770         for (i = 0; i < bucket_count; i++)
8771         {
8772             free_space_buckets[i].count_add = ordered_free_spaces[i];
8773             free_space_buckets[i].count_fit = ordered_free_spaces[i];
8774             free_space_buckets[i].free_space = &seg_free_space_array[total_item_count];
8775             total_item_count += free_space_buckets[i].count_add;
8776         }
8777
8778         assert (total_item_count == (ptrdiff_t)item_count);
8779     }
8780
8781     // If we are adding a free space before a plug we pass the
8782     // mark stack position so we can update the length; we could
8783     // also be adding the free space after the last plug in which
8784     // case start is the segment which we'll need to update the 
8785     // heap_segment_plan_allocated.
8786     void add (void* start, BOOL plug_p, BOOL first_p)
8787     {
8788         size_t size = (plug_p ? 
8789                        pinned_len ((mark*)start) : 
8790                        (heap_segment_committed ((heap_segment*)start) - 
8791                            heap_segment_plan_allocated ((heap_segment*)start)));
8792         
8793         if (plug_p)
8794         {
8795             dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space before plug: %Id", heap_num, size));
8796         }
8797         else
8798         {
8799             dprintf (SEG_REUSE_LOG_1, ("[%d]Adding a free space at end of seg: %Id", heap_num, size));
8800 #ifdef _DEBUG
8801             has_end_of_seg = TRUE;
8802 #endif //_DEBUG
8803         }
8804                   
8805         if (first_p)
8806         {
8807             size_t eph_gen_starts = gc_heap::eph_gen_starts_size;
8808             size -= eph_gen_starts;
8809             if (plug_p)
8810             {
8811                 mark* m = (mark*)(start);
8812                 pinned_len (m) -= eph_gen_starts;
8813             }
8814             else
8815             {
8816                 heap_segment* seg = (heap_segment*)start;
8817                 heap_segment_plan_allocated (seg) += eph_gen_starts;
8818             }
8819         }
8820
8821         int bucket_power2 = index_of_highest_set_bit (size);
8822         if (bucket_power2 < base_power2)
8823         {
8824             return;
8825         }
8826
8827         free_space_bucket* bucket = &free_space_buckets[bucket_power2 - base_power2];
8828
8829         seg_free_space* bucket_free_space = bucket->free_space;
8830         assert (plug_p || (!plug_p && bucket->count_add));
8831
8832         if (bucket->count_add == 0)
8833         {
8834             dprintf (SEG_REUSE_LOG_1, ("[%d]Already have enough of 2^%d", heap_num, bucket_power2));
8835             return;
8836         }
8837
8838         ptrdiff_t index = bucket->count_add - 1;
8839
8840         dprintf (SEG_REUSE_LOG_1, ("[%d]Building free spaces: adding %Ix; len: %Id (2^%d)", 
8841                     heap_num, 
8842                     (plug_p ? 
8843                         (pinned_plug ((mark*)start) - pinned_len ((mark*)start)) : 
8844                         heap_segment_plan_allocated ((heap_segment*)start)),
8845                     size,
8846                     bucket_power2));
8847
8848         if (plug_p)
8849         {
8850             bucket_free_space[index].is_plug = TRUE;
8851         }
8852
8853         bucket_free_space[index].start = start;
8854         bucket->count_add--;
8855     }
8856
8857 #ifdef _DEBUG
8858
8859     // Do a consistency check after all free spaces are added.
8860     void check()
8861     {
8862         ptrdiff_t i = 0;
8863         int end_of_seg_count = 0;
8864
8865         for (i = 0; i < free_space_item_count; i++)
8866         {
8867             assert (seg_free_space_array[i].start);
8868             if (!(seg_free_space_array[i].is_plug))
8869             {
8870                 end_of_seg_count++;
8871             }
8872         }
8873         
8874         if (has_end_of_seg)
8875         {
8876             assert (end_of_seg_count == 1);
8877         }
8878         else
8879         {
8880             assert (end_of_seg_count == 0);
8881         }
8882
8883         for (i = 0; i < free_space_bucket_count; i++)
8884         {
8885             assert (free_space_buckets[i].count_add == 0);
8886         }
8887     }
8888
8889 #endif //_DEBUG
8890
8891     uint8_t* fit (uint8_t* old_loc,
8892 #ifdef SHORT_PLUGS
8893                BOOL set_padding_on_saved_p,
8894                mark* pinned_plug_entry,
8895 #endif //SHORT_PLUGS
8896                size_t plug_size
8897                REQD_ALIGN_AND_OFFSET_DCL)
8898     {
8899         if (old_loc)
8900         {
8901 #ifdef SHORT_PLUGS
8902             assert (!is_plug_padded (old_loc));
8903 #endif //SHORT_PLUGS
8904             assert (!node_realigned (old_loc));
8905         }
8906
8907         size_t saved_plug_size = plug_size;
8908
8909 #ifdef FEATURE_STRUCTALIGN
8910         // BARTOKTODO (4841): this code path is disabled (see can_fit_all_blocks_p) until we take alignment requirements into account
8911         _ASSERTE(requiredAlignment == DATA_ALIGNMENT && false);
8912 #endif // FEATURE_STRUCTALIGN
8913         // TODO: this is also not large alignment ready. We would need to consider alignment when chosing the 
8914         // the bucket.
8915
8916         size_t plug_size_to_fit = plug_size;
8917
8918         // best fit is only done for gen1 to gen2 and we do not pad in gen2.
8919         int pad_in_front = 0;
8920
8921 #ifdef SHORT_PLUGS
8922         plug_size_to_fit += (pad_in_front ? Align(min_obj_size) : 0);
8923 #endif //SHORT_PLUGS
8924
8925         int plug_power2 = index_of_highest_set_bit (round_up_power2 (plug_size_to_fit + Align(min_obj_size)));
8926         ptrdiff_t i;
8927         uint8_t* new_address = 0;
8928
8929         if (plug_power2 < base_power2)
8930         {
8931             plug_power2 = base_power2;
8932         }
8933
8934         int chosen_power2 = plug_power2 - base_power2;
8935 retry:
8936         for (i = chosen_power2; i < free_space_bucket_count; i++)
8937         {
8938             if (free_space_buckets[i].count_fit != 0)
8939             {
8940                 break;
8941             }
8942             chosen_power2++;
8943         }
8944
8945         dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting plug len %Id (2^%d) using 2^%d free space", 
8946             heap_num, 
8947             plug_size, 
8948             plug_power2, 
8949             (chosen_power2 + base_power2)));
8950
8951         assert (i < free_space_bucket_count);
8952         
8953         seg_free_space* bucket_free_space = free_space_buckets[chosen_power2].free_space;
8954         ptrdiff_t free_space_count = free_space_buckets[chosen_power2].count_fit;
8955         size_t new_free_space_size = 0;
8956         BOOL can_fit = FALSE;
8957         size_t pad = 0;
8958
8959         for (i = 0; i < free_space_count; i++)
8960         {
8961             size_t free_space_size = 0;
8962             pad = 0;
8963 #ifdef SHORT_PLUGS
8964             BOOL short_plugs_padding_p = FALSE;
8965 #endif //SHORT_PLUGS
8966             BOOL realign_padding_p = FALSE;
8967
8968             if (bucket_free_space[i].is_plug)
8969             {
8970                 mark* m = (mark*)(bucket_free_space[i].start);
8971                 uint8_t* plug_free_space_start = pinned_plug (m) - pinned_len (m);
8972                 
8973 #ifdef SHORT_PLUGS
8974                 if ((pad_in_front & USE_PADDING_FRONT) &&
8975                     (((plug_free_space_start - pin_allocation_context_start_region (m))==0) ||
8976                     ((plug_free_space_start - pin_allocation_context_start_region (m))>=DESIRED_PLUG_LENGTH)))
8977                 {
8978                     pad = Align (min_obj_size);
8979                     short_plugs_padding_p = TRUE;
8980                 }
8981 #endif //SHORT_PLUGS
8982
8983                 if (!((old_loc == 0) || same_large_alignment_p (old_loc, plug_free_space_start+pad)))
8984                 {
8985                     pad += switch_alignment_size (pad != 0);
8986                     realign_padding_p = TRUE;
8987                 }
8988
8989                 plug_size = saved_plug_size + pad;
8990
8991                 free_space_size = pinned_len (m);
8992                 new_address = pinned_plug (m) - pinned_len (m);
8993
8994                 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
8995                     free_space_size == plug_size)
8996                 {
8997                     new_free_space_size = free_space_size - plug_size;
8998                     pinned_len (m) = new_free_space_size;
8999 #ifdef SIMPLE_DPRINTF
9000                     dprintf (SEG_REUSE_LOG_0, ("[%d]FP: 0x%Ix->0x%Ix(%Ix)(%Ix), [0x%Ix (2^%d) -> [0x%Ix (2^%d)",
9001                                 heap_num, 
9002                                 old_loc,
9003                                 new_address, 
9004                                 (plug_size - pad),
9005                                 pad,
9006                                 pinned_plug (m), 
9007                                 index_of_highest_set_bit (free_space_size),
9008                                 (pinned_plug (m) - pinned_len (m)), 
9009                                 index_of_highest_set_bit (new_free_space_size)));
9010 #endif //SIMPLE_DPRINTF
9011
9012 #ifdef SHORT_PLUGS
9013                     if (short_plugs_padding_p)
9014                     {
9015                         pin_allocation_context_start_region (m) = plug_free_space_start;
9016                         set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
9017                     }
9018 #endif //SHORT_PLUGS
9019
9020                     if (realign_padding_p)
9021                     {
9022                         set_node_realigned (old_loc);
9023                     }
9024
9025                     can_fit = TRUE;
9026                 }
9027             }
9028             else
9029             {
9030                 heap_segment* seg = (heap_segment*)(bucket_free_space[i].start);
9031                 free_space_size = heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
9032
9033                 if (!((old_loc == 0) || same_large_alignment_p (old_loc, heap_segment_plan_allocated (seg))))
9034                 {
9035                     pad = switch_alignment_size (FALSE);
9036                     realign_padding_p = TRUE;
9037                 }
9038
9039                 plug_size = saved_plug_size + pad;
9040
9041                 if (free_space_size >= (plug_size + Align (min_obj_size)) ||
9042                     free_space_size == plug_size)
9043                 {
9044                     new_address = heap_segment_plan_allocated (seg);
9045                     new_free_space_size = free_space_size - plug_size;
9046                     heap_segment_plan_allocated (seg) = new_address + plug_size;
9047 #ifdef SIMPLE_DPRINTF
9048                     dprintf (SEG_REUSE_LOG_0, ("[%d]FS: 0x%Ix-> 0x%Ix(%Ix) (2^%d) -> 0x%Ix (2^%d)",
9049                                 heap_num, 
9050                                 old_loc,
9051                                 new_address, 
9052                                 (plug_size - pad),
9053                                 index_of_highest_set_bit (free_space_size),
9054                                 heap_segment_plan_allocated (seg), 
9055                                 index_of_highest_set_bit (new_free_space_size)));
9056 #endif //SIMPLE_DPRINTF
9057
9058                     if (realign_padding_p)
9059                         set_node_realigned (old_loc);
9060
9061                     can_fit = TRUE;
9062                 }
9063             }
9064
9065             if (can_fit)
9066             {
9067                 break;
9068             }
9069         }
9070
9071         if (!can_fit)
9072         {
9073             assert (chosen_power2 == 0);
9074             chosen_power2 = 1;
9075             goto retry;
9076         }
9077         else
9078         {
9079             if (pad)
9080             {
9081                 new_address += pad;
9082             }
9083             assert ((chosen_power2 && (i == 0)) ||
9084                     ((!chosen_power2) && (i < free_space_count)));
9085         }
9086
9087         int new_bucket_power2 = index_of_highest_set_bit (new_free_space_size);
9088
9089         if (new_bucket_power2 < base_power2)
9090         {
9091             new_bucket_power2 = base_power2;
9092         }
9093
9094         move_bucket (chosen_power2, new_bucket_power2 - base_power2);
9095
9096         //dump();
9097
9098         return new_address;
9099     }
9100
9101     void cleanup ()
9102     {
9103         if (free_space_buckets)
9104         {
9105             delete [] free_space_buckets;
9106         }
9107         if (seg_free_space_array)
9108         {
9109             delete [] seg_free_space_array;
9110         }
9111     }
9112 };
9113
9114
9115 #define marked(i) header(i)->IsMarked()
9116 #define set_marked(i) header(i)->SetMarked()
9117 #define clear_marked(i) header(i)->ClearMarked()
9118 #define pinned(i) header(i)->IsPinned()
9119 #define set_pinned(i) header(i)->SetPinned()
9120 #define clear_pinned(i) header(i)->GetHeader()->ClrGCBit();
9121
9122 inline size_t my_get_size (Object* ob)
9123 {
9124     MethodTable* mT = header(ob)->GetMethodTable();
9125     return (mT->GetBaseSize() +
9126             (mT->HasComponentSize() ?
9127              ((size_t)((CObjectHeader*)ob)->GetNumComponents() * mT->RawGetComponentSize()) : 0));
9128 }
9129
9130 //#define size(i) header(i)->GetSize()
9131 #define size(i) my_get_size (header(i))
9132
9133 #define contain_pointers(i) header(i)->ContainsPointers()
9134 #ifdef COLLECTIBLE_CLASS
9135 #define contain_pointers_or_collectible(i) header(i)->ContainsPointersOrCollectible()
9136
9137 #define get_class_object(i) GCToEEInterface::GetLoaderAllocatorObjectForGC((Object *)i)
9138 #define is_collectible(i) method_table(i)->Collectible()
9139 #else //COLLECTIBLE_CLASS
9140 #define contain_pointers_or_collectible(i) header(i)->ContainsPointers()
9141 #endif //COLLECTIBLE_CLASS
9142
9143 #if defined (MARK_ARRAY) && defined (BACKGROUND_GC)
9144 inline
9145 void gc_heap::seg_clear_mark_array_bits_soh (heap_segment* seg)
9146 {
9147     uint8_t* range_beg = 0;
9148     uint8_t* range_end = 0;
9149     if (bgc_mark_array_range (seg, FALSE, &range_beg, &range_end))
9150     {
9151         clear_mark_array (range_beg, align_on_mark_word (range_end), FALSE
9152 #ifdef FEATURE_BASICFREEZE
9153             , TRUE
9154 #endif // FEATURE_BASICFREEZE
9155             );
9156     }
9157 }
9158
9159 void gc_heap::clear_batch_mark_array_bits (uint8_t* start, uint8_t* end)
9160 {
9161     if ((start < background_saved_highest_address) &&
9162         (end > background_saved_lowest_address))
9163     {
9164         start = max (start, background_saved_lowest_address);
9165         end = min (end, background_saved_highest_address);
9166
9167         size_t start_mark_bit = mark_bit_of (start);
9168         size_t end_mark_bit = mark_bit_of (end);
9169         unsigned int startbit = mark_bit_bit (start_mark_bit);
9170         unsigned int endbit = mark_bit_bit (end_mark_bit);
9171         size_t startwrd = mark_bit_word (start_mark_bit);
9172         size_t endwrd = mark_bit_word (end_mark_bit);
9173
9174         dprintf (3, ("Clearing all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
9175             (size_t)start, (size_t)start_mark_bit, 
9176             (size_t)end, (size_t)end_mark_bit));
9177
9178         unsigned int firstwrd = lowbits (~0, startbit);
9179         unsigned int lastwrd = highbits (~0, endbit);
9180
9181         if (startwrd == endwrd)
9182         {
9183             unsigned int wrd = firstwrd | lastwrd;
9184             mark_array[startwrd] &= wrd;
9185             return;
9186         }
9187
9188         // clear the first mark word.
9189         if (startbit)
9190         {
9191             mark_array[startwrd] &= firstwrd;
9192             startwrd++;
9193         }
9194
9195         for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
9196         {
9197             mark_array[wrdtmp] = 0;
9198         }
9199
9200         // clear the last mark word.
9201         if (endbit)
9202         {
9203             mark_array[endwrd] &= lastwrd;
9204         }
9205     }
9206 }
9207
9208 void gc_heap::bgc_clear_batch_mark_array_bits (uint8_t* start, uint8_t* end)
9209 {
9210     if ((start < background_saved_highest_address) &&
9211         (end > background_saved_lowest_address))
9212     {
9213         start = max (start, background_saved_lowest_address);
9214         end = min (end, background_saved_highest_address);
9215
9216         clear_batch_mark_array_bits (start, end);
9217     }
9218 }
9219
9220 void gc_heap::clear_mark_array_by_objects (uint8_t* from, uint8_t* end, BOOL loh_p)
9221 {
9222     dprintf (3, ("clearing mark array bits by objects for addr [%Ix,[%Ix", 
9223                   from, end));
9224     int align_const = get_alignment_constant (!loh_p);
9225
9226     uint8_t* o = from;
9227
9228     while (o < end)
9229     {
9230         uint8_t*  next_o = o + Align (size (o), align_const);
9231
9232         if (background_object_marked (o, TRUE))
9233         {
9234             dprintf (3, ("%Ix was marked by bgc, is now cleared", o));
9235         }
9236
9237         o = next_o;
9238     }
9239 }
9240 #endif //MARK_ARRAY && BACKGROUND_GC
9241
9242 inline
9243 BOOL gc_heap::is_mark_set (uint8_t* o)
9244 {
9245     return marked (o);
9246 }
9247
9248 #if defined (_MSC_VER) && defined (_TARGET_X86_)
9249 #pragma optimize("y", on)        // Small critical routines, don't put in EBP frame 
9250 #endif //_MSC_VER && _TARGET_X86_
9251
9252 // return the generation number of an object.
9253 // It is assumed that the object is valid.
9254 //Note that this will return max_generation for a LOH object
9255 int gc_heap::object_gennum (uint8_t* o)
9256 {
9257     if (in_range_for_segment (o, ephemeral_heap_segment) &&
9258         (o >= generation_allocation_start (generation_of (max_generation-1))))
9259     {
9260         // in an ephemeral generation.
9261         for ( int i = 0; i < max_generation-1; i++)
9262         {
9263             if ((o >= generation_allocation_start (generation_of (i))))
9264                 return i;
9265         }
9266         return max_generation-1;
9267     }
9268     else
9269     {
9270         return max_generation;
9271     }
9272 }
9273
9274 int gc_heap::object_gennum_plan (uint8_t* o)
9275 {
9276     if (in_range_for_segment (o, ephemeral_heap_segment))
9277     {
9278         for (int i = 0; i <= max_generation-1; i++)
9279         {
9280             uint8_t* plan_start = generation_plan_allocation_start (generation_of (i));
9281             if (plan_start && (o >= plan_start))
9282             {
9283                 return i;
9284             }
9285         }
9286     }
9287     return max_generation;
9288 }
9289
9290 #if defined(_MSC_VER) && defined(_TARGET_X86_)
9291 #pragma optimize("", on)        // Go back to command line default optimizations
9292 #endif //_MSC_VER && _TARGET_X86_
9293
9294 heap_segment* gc_heap::make_heap_segment (uint8_t* new_pages, size_t size, int h_number)
9295 {
9296     size_t initial_commit = SEGMENT_INITIAL_COMMIT;
9297
9298     //Commit the first page
9299     if (!virtual_commit (new_pages, initial_commit, h_number))
9300     {
9301         return 0;
9302     }
9303
9304     //overlay the heap_segment
9305     heap_segment* new_segment = (heap_segment*)new_pages;
9306
9307     uint8_t* start = new_pages + segment_info_size;
9308     heap_segment_mem (new_segment) = start;
9309     heap_segment_used (new_segment) = start;
9310     heap_segment_reserved (new_segment) = new_pages + size;
9311     heap_segment_committed (new_segment) = new_pages + initial_commit;
9312     init_heap_segment (new_segment);
9313     dprintf (2, ("Creating heap segment %Ix", (size_t)new_segment));
9314     return new_segment;
9315 }
9316
9317 void gc_heap::init_heap_segment (heap_segment* seg)
9318 {
9319     seg->flags = 0;
9320     heap_segment_next (seg) = 0;
9321     heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
9322     heap_segment_allocated (seg) = heap_segment_mem (seg);
9323 #ifdef BACKGROUND_GC
9324     heap_segment_background_allocated (seg) = 0;
9325     heap_segment_saved_bg_allocated (seg) = 0;
9326 #endif //BACKGROUND_GC
9327 }
9328
9329 //Releases the segment to the OS.
9330 // this is always called on one thread only so calling seg_table->remove is fine.
9331 void gc_heap::delete_heap_segment (heap_segment* seg, BOOL consider_hoarding)
9332 {
9333     if (!heap_segment_loh_p (seg))
9334     {
9335         //cleanup the brick table back to the empty value
9336         clear_brick_table (heap_segment_mem (seg), heap_segment_reserved (seg));
9337     }
9338
9339     if (consider_hoarding)
9340     {
9341         assert ((heap_segment_mem (seg) - (uint8_t*)seg) <= ptrdiff_t(2*OS_PAGE_SIZE));
9342         size_t ss = (size_t) (heap_segment_reserved (seg) - (uint8_t*)seg);
9343         //Don't keep the big ones.
9344         if (ss <= INITIAL_ALLOC)
9345         {
9346             dprintf (2, ("Hoarding segment %Ix", (size_t)seg));
9347 #ifdef BACKGROUND_GC
9348             // We don't need to clear the decommitted flag because when this segment is used
9349             // for a new segment the flags will be cleared.
9350             if (!heap_segment_decommitted_p (seg))
9351 #endif //BACKGROUND_GC
9352             {
9353                 decommit_heap_segment (seg);
9354             }
9355
9356 #ifdef SEG_MAPPING_TABLE
9357             seg_mapping_table_remove_segment (seg);
9358 #endif //SEG_MAPPING_TABLE
9359
9360             heap_segment_next (seg) = segment_standby_list;
9361             segment_standby_list = seg;
9362             seg = 0;
9363         }
9364     }
9365
9366     if (seg != 0)
9367     {
9368         dprintf (2, ("h%d: del seg: [%Ix, %Ix[", 
9369                      heap_number, (size_t)seg,
9370                      (size_t)(heap_segment_reserved (seg))));
9371
9372 #ifdef BACKGROUND_GC
9373         ::record_changed_seg ((uint8_t*)seg, heap_segment_reserved (seg), 
9374                             settings.gc_index, current_bgc_state,
9375                             seg_deleted);
9376         decommit_mark_array_by_seg (seg);
9377 #endif //BACKGROUND_GC
9378
9379 #ifdef SEG_MAPPING_TABLE
9380         seg_mapping_table_remove_segment (seg);
9381 #else //SEG_MAPPING_TABLE
9382         seg_table->remove ((uint8_t*)seg);
9383 #endif //SEG_MAPPING_TABLE
9384
9385         release_segment (seg);
9386     }
9387 }
9388
9389 //resets the pages beyond allocates size so they won't be swapped out and back in
9390
9391 void gc_heap::reset_heap_segment_pages (heap_segment* seg)
9392 {
9393     size_t page_start = align_on_page ((size_t)heap_segment_allocated (seg));
9394     size_t size = (size_t)heap_segment_committed (seg) - page_start;
9395     if (size != 0)
9396         GCToOSInterface::VirtualReset((void*)page_start, size, false /* unlock */);
9397 }
9398
9399 void gc_heap::decommit_heap_segment_pages (heap_segment* seg,
9400                                            size_t extra_space)
9401 {
9402     uint8_t*  page_start = align_on_page (heap_segment_allocated(seg));
9403     size_t size = heap_segment_committed (seg) - page_start;
9404     extra_space = align_on_page (extra_space);
9405     if (size >= max ((extra_space + 2*OS_PAGE_SIZE), 100*OS_PAGE_SIZE))
9406     {
9407         page_start += max(extra_space, 32*OS_PAGE_SIZE);
9408         size -= max (extra_space, 32*OS_PAGE_SIZE);
9409
9410         virtual_decommit (page_start, size, heap_number);
9411         dprintf (3, ("Decommitting heap segment [%Ix, %Ix[(%d)", 
9412             (size_t)page_start, 
9413             (size_t)(page_start + size),
9414             size));
9415         heap_segment_committed (seg) = page_start;
9416         if (heap_segment_used (seg) > heap_segment_committed (seg))
9417         {
9418             heap_segment_used (seg) = heap_segment_committed (seg);
9419         }
9420     }
9421 }
9422
9423 //decommit all pages except one or 2
9424 void gc_heap::decommit_heap_segment (heap_segment* seg)
9425 {
9426     uint8_t*  page_start = align_on_page (heap_segment_mem (seg));
9427
9428     dprintf (3, ("Decommitting heap segment %Ix", (size_t)seg));
9429
9430 #ifdef BACKGROUND_GC
9431     page_start += OS_PAGE_SIZE;
9432 #endif //BACKGROUND_GC
9433
9434     size_t size = heap_segment_committed (seg) - page_start;
9435     virtual_decommit (page_start, size, heap_number);
9436
9437     //re-init the segment object
9438     heap_segment_committed (seg) = page_start;
9439     if (heap_segment_used (seg) > heap_segment_committed (seg))
9440     {
9441         heap_segment_used (seg) = heap_segment_committed (seg);
9442     }
9443 }
9444
9445 void gc_heap::clear_gen0_bricks()
9446 {
9447     if (!gen0_bricks_cleared)
9448     {
9449         gen0_bricks_cleared = TRUE;
9450         //initialize brick table for gen 0
9451         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
9452                 b < brick_of (align_on_brick
9453                             (heap_segment_allocated (ephemeral_heap_segment)));
9454                 b++)
9455         {
9456             set_brick (b, -1);
9457         }
9458     }
9459 }
9460
9461 #ifdef BACKGROUND_GC
9462 void gc_heap::rearrange_small_heap_segments()
9463 {
9464     heap_segment* seg = freeable_small_heap_segment;
9465     while (seg)
9466     {
9467         heap_segment* next_seg = heap_segment_next (seg);
9468         // TODO: we need to consider hoarding here.
9469         delete_heap_segment (seg, FALSE);
9470         seg = next_seg;
9471     }
9472     freeable_small_heap_segment = 0;
9473 }
9474 #endif //BACKGROUND_GC
9475
9476 void gc_heap::rearrange_large_heap_segments()
9477 {
9478     dprintf (2, ("deleting empty large segments"));
9479     heap_segment* seg = freeable_large_heap_segment;
9480     while (seg)
9481     {
9482         heap_segment* next_seg = heap_segment_next (seg);
9483         delete_heap_segment (seg, GCConfig::GetRetainVM());
9484         seg = next_seg;
9485     }
9486     freeable_large_heap_segment = 0;
9487 }
9488
9489 void gc_heap::rearrange_heap_segments(BOOL compacting)
9490 {
9491     heap_segment* seg =
9492         generation_start_segment (generation_of (max_generation));
9493
9494     heap_segment* prev_seg = 0;
9495     heap_segment* next_seg = 0;
9496     while (seg)
9497     {
9498         next_seg = heap_segment_next (seg);
9499
9500         //link ephemeral segment when expanding
9501         if ((next_seg == 0) && (seg != ephemeral_heap_segment))
9502         {
9503             seg->next = ephemeral_heap_segment;
9504             next_seg = heap_segment_next (seg);
9505         }
9506
9507         //re-used expanded heap segment
9508         if ((seg == ephemeral_heap_segment) && next_seg)
9509         {
9510             heap_segment_next (prev_seg) = next_seg;
9511             heap_segment_next (seg) = 0;
9512         }
9513         else
9514         {
9515             uint8_t* end_segment = (compacting ?
9516                                  heap_segment_plan_allocated (seg) : 
9517                                  heap_segment_allocated (seg));
9518             // check if the segment was reached by allocation
9519             if ((end_segment == heap_segment_mem (seg))&&
9520                 !heap_segment_read_only_p (seg))
9521             {
9522                 //if not, unthread and delete
9523                 assert (prev_seg);
9524                 assert (seg != ephemeral_heap_segment);
9525                 heap_segment_next (prev_seg) = next_seg;
9526                 delete_heap_segment (seg, GCConfig::GetRetainVM());
9527
9528                 dprintf (2, ("Deleting heap segment %Ix", (size_t)seg));
9529             }
9530             else
9531             {
9532                 if (!heap_segment_read_only_p (seg))
9533                 {
9534                     if (compacting)
9535                     {
9536                         heap_segment_allocated (seg) =
9537                             heap_segment_plan_allocated (seg);
9538                     }
9539
9540                     // reset the pages between allocated and committed.
9541                     if (seg != ephemeral_heap_segment)
9542                     {
9543                         decommit_heap_segment_pages (seg, 0);
9544                     }
9545                 }
9546                 prev_seg = seg;
9547             }
9548         }
9549
9550         seg = next_seg;
9551     }
9552 }
9553
9554
9555 #ifdef WRITE_WATCH
9556
9557 uint8_t* g_addresses [array_size+2]; // to get around the bug in GetWriteWatch
9558
9559 #ifdef TIME_WRITE_WATCH
9560 static unsigned int tot_cycles = 0;
9561 #endif //TIME_WRITE_WATCH
9562
9563 #ifdef CARD_BUNDLE
9564
9565 inline void gc_heap::verify_card_bundle_bits_set(size_t first_card_word, size_t last_card_word)
9566 {
9567 #ifdef _DEBUG
9568     for (size_t x = cardw_card_bundle (first_card_word); x < cardw_card_bundle (last_card_word); x++)
9569     {
9570         if (!card_bundle_set_p (x))
9571         {
9572             assert (!"Card bundle not set");
9573             dprintf (3, ("Card bundle %Ix not set", x));
9574         }
9575     }
9576 #endif
9577 }
9578
9579 // Verifies that any bundles that are not set represent only cards that are not set.
9580 inline void gc_heap::verify_card_bundles()
9581 {
9582 #ifdef _DEBUG
9583     size_t lowest_card = card_word (card_of (lowest_address));
9584     size_t highest_card = card_word (card_of (highest_address));
9585     size_t cardb = cardw_card_bundle (lowest_card);
9586     size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (highest_card));
9587
9588     while (cardb < end_cardb)
9589     {
9590         uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb), lowest_card)];
9591         uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1), highest_card)];
9592
9593         if (card_bundle_set_p (cardb) == 0)
9594         {
9595             // Verify that no card is set
9596             while (card_word < card_word_end)
9597             {
9598                 if (*card_word != 0)
9599                 {
9600                     dprintf  (3, ("gc: %d, Card word %Ix for address %Ix set, card_bundle %Ix clear",
9601                             dd_collection_count (dynamic_data_of (0)), 
9602                             (size_t)(card_word-&card_table[0]),
9603                             (size_t)(card_address ((size_t)(card_word-&card_table[0]) * card_word_width)), cardb));
9604                 }
9605
9606                 assert((*card_word)==0);
9607                 card_word++;
9608             }
9609         }
9610
9611         cardb++;
9612     }
9613 #endif
9614 }
9615
9616 // If card bundles are enabled, use write watch to find pages in the card table that have 
9617 // been dirtied, and set the corresponding card bundle bits.
9618 void gc_heap::update_card_table_bundle()
9619 {
9620     if (card_bundles_enabled())
9621     {
9622         // The address of the card word containing the card representing the lowest heap address
9623         uint8_t* base_address = (uint8_t*)(&card_table[card_word (card_of (lowest_address))]);
9624
9625         // The address of the card word containing the card representing the highest heap address
9626         uint8_t* high_address = (uint8_t*)(&card_table[card_word (card_of (highest_address))]);
9627         
9628         uint8_t* saved_base_address = base_address;
9629         uintptr_t bcount = array_size;
9630         size_t saved_region_size = align_on_page (high_address) - saved_base_address;
9631
9632         do
9633         {
9634             size_t region_size = align_on_page (high_address) - base_address;
9635
9636             dprintf (3,("Probing card table pages [%Ix, %Ix[", (size_t)base_address, (size_t)base_address+region_size));
9637             bool success = GCToOSInterface::GetWriteWatch(false /* resetState */,
9638                                                           base_address,
9639                                                           region_size,
9640                                                           (void**)g_addresses,
9641                                                           &bcount);
9642             assert (success && "GetWriteWatch failed!");
9643
9644             dprintf (3,("Found %d pages written", bcount));
9645             for (unsigned i = 0; i < bcount; i++)
9646             {
9647                 // Offset of the dirty page from the start of the card table (clamped to base_address)
9648                 size_t bcardw = (uint32_t*)(max(g_addresses[i],base_address)) - &card_table[0];
9649
9650                 // Offset of the end of the page from the start of the card table (clamped to high addr)
9651                 size_t ecardw = (uint32_t*)(min(g_addresses[i]+OS_PAGE_SIZE, high_address)) - &card_table[0];
9652                 assert (bcardw >= card_word (card_of (g_gc_lowest_address)));
9653
9654                 // Set the card bundle bits representing the dirty card table page
9655                 card_bundles_set (cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw)));
9656                 dprintf (3,("Set Card bundle [%Ix, %Ix[", cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw))));
9657
9658                 verify_card_bundle_bits_set(bcardw, ecardw);
9659             }
9660
9661             if (bcount >= array_size)
9662             {
9663                 base_address = g_addresses [array_size-1] + OS_PAGE_SIZE;
9664                 bcount = array_size;
9665             }
9666
9667         } while ((bcount >= array_size) && (base_address < high_address));
9668
9669         // Now that we've updated the card bundle bits, reset the write-tracking state. 
9670         GCToOSInterface::ResetWriteWatch (saved_base_address, saved_region_size);
9671     }
9672 }
9673 #endif //CARD_BUNDLE
9674
9675 // static
9676 void gc_heap::reset_write_watch_for_gc_heap(void* base_address, size_t region_size)
9677 {
9678 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9679     SoftwareWriteWatch::ClearDirty(base_address, region_size);
9680 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9681     GCToOSInterface::ResetWriteWatch(base_address, region_size);
9682 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9683 }
9684
9685 // static
9686 void gc_heap::get_write_watch_for_gc_heap(bool reset, void *base_address, size_t region_size, void** dirty_pages, uintptr_t* dirty_page_count_ref, bool is_runtime_suspended)
9687 {
9688 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9689     SoftwareWriteWatch::GetDirty(base_address, region_size, dirty_pages, dirty_page_count_ref, reset, is_runtime_suspended);
9690 #else // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9691     UNREFERENCED_PARAMETER(is_runtime_suspended);
9692     bool success = GCToOSInterface::GetWriteWatch(reset, base_address, region_size, dirty_pages, dirty_page_count_ref);
9693     assert(success);
9694 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9695 }
9696
9697 const size_t ww_reset_quantum = 128*1024*1024;
9698
9699 inline
9700 void gc_heap::switch_one_quantum()
9701 {
9702     enable_preemptive ();
9703     GCToOSInterface::Sleep (1);
9704     disable_preemptive (true);
9705 }
9706
9707 void gc_heap::reset_ww_by_chunk (uint8_t* start_address, size_t total_reset_size)
9708 {
9709     size_t reset_size = 0;
9710     size_t remaining_reset_size = 0;
9711     size_t next_reset_size = 0;
9712
9713     while (reset_size != total_reset_size)
9714     {
9715         remaining_reset_size = total_reset_size - reset_size;
9716         next_reset_size = ((remaining_reset_size >= ww_reset_quantum) ? ww_reset_quantum : remaining_reset_size);
9717         if (next_reset_size)
9718         {
9719             reset_write_watch_for_gc_heap(start_address, next_reset_size);
9720             reset_size += next_reset_size;
9721
9722             switch_one_quantum();
9723         }
9724     }
9725
9726     assert (reset_size == total_reset_size);
9727 }
9728
9729 // This does a Sleep(1) for every reset ww_reset_quantum bytes of reset 
9730 // we do concurrently.
9731 void gc_heap::switch_on_reset (BOOL concurrent_p, size_t* current_total_reset_size, size_t last_reset_size)
9732 {
9733     if (concurrent_p)
9734     {
9735         *current_total_reset_size += last_reset_size;
9736
9737         dprintf (2, ("reset %Id bytes so far", *current_total_reset_size));
9738
9739         if (*current_total_reset_size > ww_reset_quantum)
9740         {
9741             switch_one_quantum();
9742
9743             *current_total_reset_size = 0;
9744         }
9745     }
9746 }
9747
9748 void gc_heap::reset_write_watch (BOOL concurrent_p)
9749 {
9750 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9751     // Software write watch currently requires the runtime to be suspended during reset. See SoftwareWriteWatch::ClearDirty().
9752     assert(!concurrent_p);
9753 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
9754
9755     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
9756
9757     PREFIX_ASSUME(seg != NULL);
9758
9759     size_t reset_size = 0;
9760     size_t region_size = 0;
9761
9762     dprintf (2, ("bgc lowest: %Ix, bgc highest: %Ix", background_saved_lowest_address, background_saved_highest_address));
9763
9764     while (seg)
9765     {
9766         uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
9767         base_address = max (base_address, background_saved_lowest_address);
9768
9769         uint8_t* high_address = 0;
9770         high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
9771         high_address = min (high_address, background_saved_highest_address);
9772         
9773         if (base_address < high_address)
9774         {
9775             region_size = high_address - base_address;
9776
9777 #ifdef TIME_WRITE_WATCH
9778             unsigned int time_start = GetCycleCount32();
9779 #endif //TIME_WRITE_WATCH
9780             dprintf (3, ("h%d: soh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9781             //reset_ww_by_chunk (base_address, region_size);
9782             reset_write_watch_for_gc_heap(base_address, region_size);
9783
9784 #ifdef TIME_WRITE_WATCH
9785             unsigned int time_stop = GetCycleCount32();
9786             tot_cycles += time_stop - time_start;
9787             printf ("ResetWriteWatch Duration: %d, total: %d\n",
9788                     time_stop - time_start, tot_cycles);
9789 #endif //TIME_WRITE_WATCH
9790
9791             switch_on_reset (concurrent_p, &reset_size, region_size);
9792         }
9793
9794         seg = heap_segment_next_rw (seg);
9795
9796         concurrent_print_time_delta ("CRWW soh");
9797     }
9798
9799     //concurrent_print_time_delta ("CRW soh");
9800
9801     seg = heap_segment_rw (generation_start_segment (large_object_generation));
9802
9803     PREFIX_ASSUME(seg != NULL);
9804
9805     while (seg)
9806     {
9807         uint8_t* base_address = align_lower_page (heap_segment_mem (seg));
9808         uint8_t* high_address =  heap_segment_allocated (seg);
9809
9810         base_address = max (base_address, background_saved_lowest_address);
9811         high_address = min (high_address, background_saved_highest_address);
9812
9813         if (base_address < high_address)
9814         {
9815             region_size = high_address - base_address;
9816             
9817 #ifdef TIME_WRITE_WATCH
9818             unsigned int time_start = GetCycleCount32();
9819 #endif //TIME_WRITE_WATCH
9820             dprintf (3, ("h%d: loh ww: [%Ix(%Id)", heap_number, (size_t)base_address, region_size));
9821             //reset_ww_by_chunk (base_address, region_size);
9822             reset_write_watch_for_gc_heap(base_address, region_size);
9823
9824 #ifdef TIME_WRITE_WATCH
9825             unsigned int time_stop = GetCycleCount32();
9826             tot_cycles += time_stop - time_start;
9827             printf ("ResetWriteWatch Duration: %d, total: %d\n",
9828                     time_stop - time_start, tot_cycles);
9829 #endif //TIME_WRITE_WATCH
9830     
9831             switch_on_reset (concurrent_p, &reset_size, region_size);
9832         }
9833
9834         seg = heap_segment_next_rw (seg);
9835
9836         concurrent_print_time_delta ("CRWW loh");
9837     }
9838
9839 #ifdef DEBUG_WRITE_WATCH
9840     debug_write_watch = (uint8_t**)~0;
9841 #endif //DEBUG_WRITE_WATCH
9842 }
9843
9844 #endif //WRITE_WATCH
9845
9846 #ifdef BACKGROUND_GC
9847 void gc_heap::restart_vm()
9848 {
9849     //assert (generation_allocation_pointer (youngest_generation) == 0);
9850     dprintf (3, ("Restarting EE"));
9851     STRESS_LOG0(LF_GC, LL_INFO10000, "Concurrent GC: Retarting EE\n");
9852     ee_proceed_event.Set();
9853 }
9854
9855 inline
9856 void fire_alloc_wait_event (alloc_wait_reason awr, BOOL begin_p)
9857 {
9858     if (awr != awr_ignored)
9859     {
9860         if (begin_p)
9861         {
9862             FIRE_EVENT(BGCAllocWaitBegin, awr);
9863         }
9864         else
9865         {
9866             FIRE_EVENT(BGCAllocWaitEnd, awr);
9867         }
9868     }
9869 }
9870
9871
9872 void gc_heap::fire_alloc_wait_event_begin (alloc_wait_reason awr)
9873 {
9874     fire_alloc_wait_event (awr, TRUE);
9875 }
9876
9877
9878 void gc_heap::fire_alloc_wait_event_end (alloc_wait_reason awr)
9879 {
9880     fire_alloc_wait_event (awr, FALSE);
9881 }
9882 #endif //BACKGROUND_GC
9883 void gc_heap::make_generation (generation& gen, heap_segment* seg, uint8_t* start, uint8_t* pointer)
9884 {
9885     gen.allocation_start = start;
9886     gen.allocation_context.alloc_ptr = pointer;
9887     gen.allocation_context.alloc_limit = pointer;
9888     gen.allocation_context.alloc_bytes = 0;
9889     gen.allocation_context.alloc_bytes_loh = 0;
9890     gen.allocation_context_start_region = pointer;
9891     gen.start_segment = seg;
9892     gen.allocation_segment = seg;
9893     gen.plan_allocation_start = 0;
9894     gen.free_list_space = 0;
9895     gen.pinned_allocated = 0; 
9896     gen.free_list_allocated = 0; 
9897     gen.end_seg_allocated = 0;
9898     gen.condemned_allocated = 0; 
9899     gen.free_obj_space = 0;
9900     gen.allocation_size = 0;
9901     gen.pinned_allocation_sweep_size = 0;
9902     gen.pinned_allocation_compact_size = 0;
9903     gen.allocate_end_seg_p = FALSE;
9904     gen.free_list_allocator.clear();
9905
9906 #ifdef FREE_USAGE_STATS
9907     memset (gen.gen_free_spaces, 0, sizeof (gen.gen_free_spaces));
9908     memset (gen.gen_current_pinned_free_spaces, 0, sizeof (gen.gen_current_pinned_free_spaces));
9909     memset (gen.gen_plugs, 0, sizeof (gen.gen_plugs));
9910 #endif //FREE_USAGE_STATS
9911 }
9912
9913 void gc_heap::adjust_ephemeral_limits ()
9914 {
9915     ephemeral_low = generation_allocation_start (generation_of (max_generation - 1));
9916     ephemeral_high = heap_segment_reserved (ephemeral_heap_segment);
9917
9918     dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix",
9919                  (size_t)ephemeral_low, (size_t)ephemeral_high))
9920
9921 #ifndef MULTIPLE_HEAPS
9922     // This updates the write barrier helpers with the new info.
9923     stomp_write_barrier_ephemeral(ephemeral_low, ephemeral_high);
9924 #endif // MULTIPLE_HEAPS
9925 }
9926
9927 #if defined(TRACE_GC) || defined(GC_CONFIG_DRIVEN)
9928 FILE* CreateLogFile(const GCConfigStringHolder& temp_logfile_name, bool is_config)
9929 {
9930     FILE* logFile;
9931
9932     if (!temp_logfile_name.Get())
9933     {
9934         return nullptr;
9935     }
9936
9937     char logfile_name[MAX_LONGPATH+1];
9938     uint32_t pid = GCToOSInterface::GetCurrentProcessId();
9939     const char* suffix = is_config ? ".config.log" : ".log";
9940     _snprintf_s(logfile_name, MAX_LONGPATH+1, _TRUNCATE, "%s.%d%s", temp_logfile_name.Get(), pid, suffix);
9941     logFile = fopen(logfile_name, "wb");
9942     return logFile;
9943 }
9944 #endif //TRACE_GC || GC_CONFIG_DRIVEN
9945
9946 size_t gc_heap::get_segment_size_hard_limit (uint32_t* num_heaps, bool should_adjust_num_heaps)
9947 {
9948     assert (heap_hard_limit);
9949     size_t aligned_hard_limit = ((heap_hard_limit + min_segment_size_hard_limit - 1) & ~(min_segment_size_hard_limit - 1));
9950     if (should_adjust_num_heaps)
9951     {
9952         uint32_t max_num_heaps = (uint32_t)(aligned_hard_limit / min_segment_size_hard_limit);
9953         if (*num_heaps > max_num_heaps)
9954         {
9955             *num_heaps = max_num_heaps;
9956         }
9957     }
9958
9959     size_t seg_size = aligned_hard_limit / *num_heaps;
9960     size_t aligned_seg_size = round_up_power2 (seg_size);
9961
9962     assert (g_theGCHeap->IsValidSegmentSize (aligned_seg_size));
9963
9964     size_t seg_size_from_config = (size_t)GCConfig::GetSegmentSize();
9965     if (seg_size_from_config)
9966     {
9967         size_t aligned_seg_size_config = round_up_power2 (seg_size_from_config);
9968
9969         aligned_seg_size = max (aligned_seg_size, aligned_seg_size_config);
9970     }
9971
9972     //printf ("limit: %Idmb, aligned: %Idmb, %d heaps, seg size from config: %Idmb, seg size %Idmb", 
9973     //    (heap_hard_limit / 1024 / 1024),
9974     //    (aligned_hard_limit / 1024 / 1024),
9975     //    *num_heaps, 
9976     //    (seg_size_from_config / 1024 / 1024),
9977     //    (aligned_seg_size / 1024 / 1024));
9978     return aligned_seg_size;
9979 }
9980
9981 HRESULT gc_heap::initialize_gc (size_t segment_size,
9982                                 size_t heap_size
9983 #ifdef MULTIPLE_HEAPS
9984                                 ,unsigned number_of_heaps
9985 #endif //MULTIPLE_HEAPS
9986 )
9987 {
9988 #ifdef TRACE_GC
9989     if (GCConfig::GetLogEnabled())
9990     {
9991         gc_log = CreateLogFile(GCConfig::GetLogFile(), false);
9992
9993         if (gc_log == NULL)
9994             return E_FAIL;
9995
9996         // GCLogFileSize in MBs.
9997         gc_log_file_size = static_cast<size_t>(GCConfig::GetLogFileSize());
9998
9999         if (gc_log_file_size <= 0 || gc_log_file_size > 500)
10000         {
10001             fclose (gc_log);
10002             return E_FAIL;
10003         }
10004
10005         gc_log_lock.Initialize();
10006         gc_log_buffer = new (nothrow) uint8_t [gc_log_buffer_size];
10007         if (!gc_log_buffer)
10008         {
10009             fclose(gc_log);
10010             return E_FAIL;
10011         }
10012
10013         memset (gc_log_buffer, '*', gc_log_buffer_size);
10014
10015         max_gc_buffers = gc_log_file_size * 1024 * 1024 / gc_log_buffer_size;
10016     }
10017 #endif // TRACE_GC
10018
10019 #ifdef GC_CONFIG_DRIVEN
10020     if (GCConfig::GetConfigLogEnabled())
10021     {
10022         gc_config_log = CreateLogFile(GCConfig::GetConfigLogFile(), true);
10023
10024         if (gc_config_log == NULL)
10025             return E_FAIL;
10026
10027         gc_config_log_buffer = new (nothrow) uint8_t [gc_config_log_buffer_size];
10028         if (!gc_config_log_buffer)
10029         {
10030             fclose(gc_config_log);
10031             return E_FAIL;
10032         }
10033
10034         compact_ratio = static_cast<int>(GCConfig::GetCompactRatio());
10035
10036         //         h#  | GC  | gen | C   | EX   | NF  | BF  | ML  | DM  || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP | 
10037         cprintf (("%2s | %6s | %1s | %1s | %2s | %2s | %2s | %2s | %2s || %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s | %5s |",
10038                 "h#", // heap index
10039                 "GC", // GC index
10040                 "g", // generation
10041                 "C",  // compaction (empty means sweeping), 'M' means it was mandatory, 'W' means it was not
10042                 "EX", // heap expansion
10043                 "NF", // normal fit
10044                 "BF", // best fit (if it indicates neither NF nor BF it means it had to acquire a new seg.
10045                 "ML", // mark list
10046                 "DM", // demotion
10047                 "PreS", // short object before pinned plug
10048                 "PostS", // short object after pinned plug
10049                 "Merge", // merged pinned plugs
10050                 "Conv", // converted to pinned plug
10051                 "Pre", // plug before pinned plug but not after
10052                 "Post", // plug after pinned plug but not before
10053                 "PrPo", // plug both before and after pinned plug
10054                 "PreP", // pre short object padded
10055                 "PostP" // post short object padded
10056                 ));
10057     }
10058 #endif //GC_CONFIG_DRIVEN
10059
10060 #ifdef GC_STATS
10061     GCConfigStringHolder logFileName = GCConfig::GetMixLogFile();
10062     if (logFileName.Get() != nullptr)
10063     {
10064         GCStatistics::logFileName = _strdup(logFileName.Get());
10065         GCStatistics::logFile = fopen(GCStatistics::logFileName, "a");
10066         if (!GCStatistics::logFile)
10067         {
10068             return E_FAIL;
10069         }
10070     }
10071 #endif // GC_STATS
10072
10073     HRESULT hres = S_OK;
10074
10075 #ifdef WRITE_WATCH
10076     hardware_write_watch_api_supported();
10077 #ifdef BACKGROUND_GC
10078     if (can_use_write_watch_for_gc_heap() && GCConfig::GetConcurrentGC())
10079     {
10080         gc_can_use_concurrent = true;
10081 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
10082         virtual_alloc_hardware_write_watch = true;
10083 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
10084     }
10085     else
10086     {
10087         gc_can_use_concurrent = false;
10088     }
10089 #endif //BACKGROUND_GC
10090 #endif //WRITE_WATCH
10091
10092 #ifdef BACKGROUND_GC
10093     // leave the first page to contain only segment info
10094     // because otherwise we could need to revisit the first page frequently in 
10095     // background GC.
10096     segment_info_size = OS_PAGE_SIZE;
10097 #else
10098     segment_info_size = Align (sizeof (heap_segment), get_alignment_constant (FALSE));
10099 #endif //BACKGROUND_GC
10100
10101     reserved_memory = 0;
10102     unsigned block_count;
10103 #ifdef MULTIPLE_HEAPS
10104     reserved_memory_limit = (segment_size + heap_size) * number_of_heaps;
10105     block_count = number_of_heaps;
10106 #else //MULTIPLE_HEAPS
10107     reserved_memory_limit = segment_size + heap_size;
10108     block_count = 1;
10109 #endif //MULTIPLE_HEAPS
10110
10111     if (heap_hard_limit)
10112     {
10113         check_commit_cs.Initialize();
10114     }
10115
10116     if (!reserve_initial_memory(segment_size,heap_size,block_count))
10117         return E_OUTOFMEMORY;
10118
10119 #ifdef CARD_BUNDLE
10120     //check if we need to turn on card_bundles.
10121 #ifdef MULTIPLE_HEAPS
10122     // use INT64 arithmetic here because of possible overflow on 32p
10123     uint64_t th = (uint64_t)MH_TH_CARD_BUNDLE*number_of_heaps;
10124 #else
10125     // use INT64 arithmetic here because of possible overflow on 32p
10126     uint64_t th = (uint64_t)SH_TH_CARD_BUNDLE;
10127 #endif //MULTIPLE_HEAPS
10128
10129     if (can_use_write_watch_for_card_table() && reserved_memory >= th)
10130     {
10131         settings.card_bundles = TRUE;
10132     }
10133     else
10134     {
10135         settings.card_bundles = FALSE;
10136     }
10137 #endif //CARD_BUNDLE
10138
10139     settings.first_init();
10140
10141     int latency_level_from_config = static_cast<int>(GCConfig::GetLatencyLevel());
10142     if (latency_level_from_config >= latency_level_first && latency_level_from_config <= latency_level_last)
10143     {
10144         gc_heap::latency_level = static_cast<gc_latency_level>(latency_level_from_config);
10145     }
10146
10147     init_static_data();
10148
10149     g_gc_card_table = make_card_table (g_gc_lowest_address, g_gc_highest_address);
10150
10151     if (!g_gc_card_table)
10152         return E_OUTOFMEMORY;
10153
10154     gc_started = FALSE;
10155
10156 #ifdef MULTIPLE_HEAPS
10157     g_heaps = new (nothrow) gc_heap* [number_of_heaps];
10158     if (!g_heaps)
10159         return E_OUTOFMEMORY;
10160
10161 #ifdef _PREFAST_ 
10162 #pragma warning(push)
10163 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
10164 #endif // _PREFAST_
10165     g_promoted = new (nothrow) size_t [number_of_heaps*16];
10166     g_bpromoted = new (nothrow) size_t [number_of_heaps*16];
10167 #ifdef MH_SC_MARK
10168     g_mark_stack_busy = new (nothrow) int[(number_of_heaps+2)*HS_CACHE_LINE_SIZE/sizeof(int)];
10169 #endif //MH_SC_MARK
10170 #ifdef _PREFAST_ 
10171 #pragma warning(pop)
10172 #endif // _PREFAST_
10173     if (!g_promoted || !g_bpromoted)
10174         return E_OUTOFMEMORY;
10175
10176 #ifdef MH_SC_MARK
10177     if (!g_mark_stack_busy)
10178         return E_OUTOFMEMORY;
10179 #endif //MH_SC_MARK
10180
10181     if (!create_thread_support (number_of_heaps))
10182         return E_OUTOFMEMORY;
10183
10184     if (!heap_select::init (number_of_heaps))
10185         return E_OUTOFMEMORY;
10186
10187 #endif //MULTIPLE_HEAPS
10188
10189 #ifdef MULTIPLE_HEAPS
10190     yp_spin_count_unit = 32 * number_of_heaps;
10191 #else
10192     yp_spin_count_unit = 32 * g_num_processors;
10193 #endif //MULTIPLE_HEAPS
10194
10195     if (!init_semi_shared())
10196     {
10197         hres = E_FAIL;
10198     }
10199
10200     return hres;
10201 }
10202
10203 //Initializes PER_HEAP_ISOLATED data members.
10204 int
10205 gc_heap::init_semi_shared()
10206 {
10207     int ret = 0;
10208
10209     // This is used for heap expansion - it's to fix exactly the start for gen 0
10210     // through (max_generation-1). When we expand the heap we allocate all these
10211     // gen starts at the beginning of the new ephemeral seg. 
10212     eph_gen_starts_size = (Align (min_obj_size)) * max_generation;
10213
10214 #ifdef MARK_LIST
10215 #ifdef MULTIPLE_HEAPS
10216     mark_list_size = min (150*1024, max (8192, soh_segment_size/(2*10*32)));
10217     g_mark_list = make_mark_list (mark_list_size*n_heaps);
10218
10219     min_balance_threshold = alloc_quantum_balance_units * CLR_SIZE * 2;
10220 #ifdef PARALLEL_MARK_LIST_SORT
10221     g_mark_list_copy = make_mark_list (mark_list_size*n_heaps);
10222     if (!g_mark_list_copy)
10223     {
10224         goto cleanup;
10225     }
10226 #endif //PARALLEL_MARK_LIST_SORT
10227
10228 #else //MULTIPLE_HEAPS
10229
10230     mark_list_size = max (8192, soh_segment_size/(64*32));
10231     g_mark_list = make_mark_list (mark_list_size);
10232
10233 #endif //MULTIPLE_HEAPS
10234
10235     dprintf (3, ("mark_list_size: %d", mark_list_size));
10236
10237     if (!g_mark_list)
10238     {
10239         goto cleanup;
10240     }
10241 #endif //MARK_LIST
10242
10243 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10244     if (!seg_mapping_table_init())
10245         goto cleanup;
10246 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10247
10248 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10249     seg_table = sorted_table::make_sorted_table();
10250
10251     if (!seg_table)
10252         goto cleanup;
10253 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10254
10255     segment_standby_list = 0;
10256
10257     if (!full_gc_approach_event.CreateManualEventNoThrow(FALSE))
10258     {
10259         goto cleanup;
10260     }
10261     if (!full_gc_end_event.CreateManualEventNoThrow(FALSE))
10262     {
10263         goto cleanup;
10264     }
10265
10266     fgn_maxgen_percent = 0;
10267     fgn_loh_percent = 0;
10268     full_gc_approach_event_set = false;
10269
10270     memset (full_gc_counts, 0, sizeof (full_gc_counts));
10271
10272     last_gc_index = 0;
10273     should_expand_in_full_gc = FALSE;
10274
10275 #ifdef FEATURE_LOH_COMPACTION
10276     loh_compaction_always_p = GCConfig::GetLOHCompactionMode() != 0;
10277     loh_compaction_mode = loh_compaction_default;
10278 #endif //FEATURE_LOH_COMPACTION
10279
10280     loh_size_threshold = (size_t)GCConfig::GetLOHThreshold();
10281     assert (loh_size_threshold >= LARGE_OBJECT_SIZE);
10282
10283 #ifdef BACKGROUND_GC
10284     memset (ephemeral_fgc_counts, 0, sizeof (ephemeral_fgc_counts));
10285     bgc_alloc_spin_count = static_cast<uint32_t>(GCConfig::GetBGCSpinCount());
10286     bgc_alloc_spin = static_cast<uint32_t>(GCConfig::GetBGCSpin());
10287
10288     {   
10289         int number_bgc_threads = 1;
10290 #ifdef MULTIPLE_HEAPS
10291         number_bgc_threads = n_heaps;
10292 #endif //MULTIPLE_HEAPS
10293         if (!create_bgc_threads_support (number_bgc_threads))
10294         {
10295             goto cleanup;
10296         }
10297     }
10298 #endif //BACKGROUND_GC
10299
10300     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
10301
10302 #ifdef GC_CONFIG_DRIVEN
10303     compact_or_sweep_gcs[0] = 0;
10304     compact_or_sweep_gcs[1] = 0;
10305 #endif //GC_CONFIG_DRIVEN
10306
10307 #ifdef SHORT_PLUGS
10308     short_plugs_pad_ratio = (double)DESIRED_PLUG_LENGTH / (double)(DESIRED_PLUG_LENGTH - Align (min_obj_size));
10309 #endif //SHORT_PLUGS
10310
10311     ret = 1;
10312
10313 cleanup:
10314
10315     if (!ret)
10316     {
10317         if (full_gc_approach_event.IsValid())
10318         {
10319             full_gc_approach_event.CloseEvent();
10320         }
10321         if (full_gc_end_event.IsValid())
10322         {
10323             full_gc_end_event.CloseEvent();
10324         }
10325     }
10326
10327     return ret;
10328 }
10329
10330 gc_heap* gc_heap::make_gc_heap (
10331 #ifdef MULTIPLE_HEAPS
10332                                 GCHeap* vm_hp,
10333                                 int heap_number
10334 #endif //MULTIPLE_HEAPS
10335                                 )
10336 {
10337     gc_heap* res = 0;
10338
10339 #ifdef MULTIPLE_HEAPS
10340     res = new (nothrow) gc_heap;
10341     if (!res)
10342         return 0;
10343
10344     res->vm_heap = vm_hp;
10345     res->alloc_context_count = 0;
10346
10347 #ifdef MARK_LIST
10348 #ifdef PARALLEL_MARK_LIST_SORT
10349     res->mark_list_piece_start = new (nothrow) uint8_t**[n_heaps];
10350     if (!res->mark_list_piece_start)
10351         return 0;
10352
10353 #ifdef _PREFAST_ 
10354 #pragma warning(push)
10355 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
10356 #endif // _PREFAST_
10357     res->mark_list_piece_end = new (nothrow) uint8_t**[n_heaps + 32]; // +32 is padding to reduce false sharing
10358 #ifdef _PREFAST_ 
10359 #pragma warning(pop)
10360 #endif // _PREFAST_
10361
10362     if (!res->mark_list_piece_end)
10363         return 0;
10364 #endif //PARALLEL_MARK_LIST_SORT
10365 #endif //MARK_LIST
10366
10367
10368 #endif //MULTIPLE_HEAPS
10369
10370     if (res->init_gc_heap (
10371 #ifdef MULTIPLE_HEAPS
10372         heap_number
10373 #else  //MULTIPLE_HEAPS
10374         0
10375 #endif //MULTIPLE_HEAPS
10376         )==0)
10377     {
10378         return 0;
10379     }
10380
10381 #ifdef MULTIPLE_HEAPS
10382     return res;
10383 #else
10384     return (gc_heap*)1;
10385 #endif //MULTIPLE_HEAPS
10386 }
10387
10388 uint32_t
10389 gc_heap::wait_for_gc_done(int32_t timeOut)
10390 {
10391     bool cooperative_mode = enable_preemptive ();
10392
10393     uint32_t dwWaitResult = NOERROR;
10394
10395     gc_heap* wait_heap = NULL;
10396     while (gc_heap::gc_started)
10397     {       
10398 #ifdef MULTIPLE_HEAPS
10399         wait_heap = GCHeap::GetHeap(heap_select::select_heap(NULL, 0))->pGenGCHeap;
10400         dprintf(2, ("waiting for the gc_done_event on heap %d", wait_heap->heap_number));
10401 #endif // MULTIPLE_HEAPS
10402
10403 #ifdef _PREFAST_
10404         PREFIX_ASSUME(wait_heap != NULL);
10405 #endif // _PREFAST_
10406
10407         dwWaitResult = wait_heap->gc_done_event.Wait(timeOut, FALSE); 
10408     }
10409     disable_preemptive (cooperative_mode);
10410
10411     return dwWaitResult;
10412 }
10413
10414 void 
10415 gc_heap::set_gc_done()
10416 {
10417     enter_gc_done_event_lock();
10418     if (!gc_done_event_set)
10419     {
10420         gc_done_event_set = true;
10421         dprintf (2, ("heap %d: setting gc_done_event", heap_number));
10422         gc_done_event.Set();
10423     }
10424     exit_gc_done_event_lock();
10425 }
10426
10427 void 
10428 gc_heap::reset_gc_done()
10429 {
10430     enter_gc_done_event_lock();
10431     if (gc_done_event_set)
10432     {
10433         gc_done_event_set = false;
10434         dprintf (2, ("heap %d: resetting gc_done_event", heap_number));
10435         gc_done_event.Reset();
10436     }
10437     exit_gc_done_event_lock();
10438 }
10439
10440 void 
10441 gc_heap::enter_gc_done_event_lock()
10442 {
10443     uint32_t dwSwitchCount = 0;
10444 retry:
10445
10446     if (Interlocked::CompareExchange(&gc_done_event_lock, 0, -1) >= 0)
10447     {
10448         while (gc_done_event_lock >= 0)
10449         {
10450             if  (g_num_processors > 1)
10451             {
10452                 int spin_count = yp_spin_count_unit;
10453                 for (int j = 0; j < spin_count; j++)
10454                 {
10455                     if  (gc_done_event_lock < 0)
10456                         break;
10457                     YieldProcessor();           // indicate to the processor that we are spinning
10458                 }
10459                 if  (gc_done_event_lock >= 0)
10460                     GCToOSInterface::YieldThread(++dwSwitchCount);
10461             }
10462             else
10463                 GCToOSInterface::YieldThread(++dwSwitchCount);
10464         }
10465         goto retry;
10466     }
10467 }
10468
10469 void 
10470 gc_heap::exit_gc_done_event_lock()
10471 {
10472     gc_done_event_lock = -1;
10473 }
10474
10475 #ifndef MULTIPLE_HEAPS
10476
10477 #ifdef RECORD_LOH_STATE
10478 int gc_heap::loh_state_index = 0;
10479 gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states];
10480 #endif //RECORD_LOH_STATE
10481
10482 VOLATILE(int32_t) gc_heap::gc_done_event_lock;
10483 VOLATILE(bool) gc_heap::gc_done_event_set;
10484 GCEvent gc_heap::gc_done_event;
10485 #endif //!MULTIPLE_HEAPS
10486 VOLATILE(bool) gc_heap::internal_gc_done;
10487
10488 void gc_heap::add_saved_spinlock_info (
10489             bool loh_p, 
10490             msl_enter_state enter_state, 
10491             msl_take_state take_state)
10492
10493 {
10494 #ifdef SPINLOCK_HISTORY
10495     spinlock_info* current = &last_spinlock_info[spinlock_info_index];
10496
10497     current->enter_state = enter_state;
10498     current->take_state = take_state;
10499     current->thread_id.SetToCurrentThread();
10500     current->loh_p = loh_p;
10501     dprintf (SPINLOCK_LOG, ("[%d]%s %s %s", 
10502         heap_number, 
10503         (loh_p ? "loh" : "soh"),
10504         ((enter_state == me_acquire) ? "E" : "L"),
10505         msl_take_state_str[take_state]));
10506
10507     spinlock_info_index++;
10508
10509     assert (spinlock_info_index <= max_saved_spinlock_info);
10510
10511     if (spinlock_info_index >= max_saved_spinlock_info)
10512     {
10513         spinlock_info_index = 0;
10514     }
10515 #else
10516     MAYBE_UNUSED_VAR(enter_state);
10517     MAYBE_UNUSED_VAR(take_state);
10518 #endif //SPINLOCK_HISTORY
10519 }
10520
10521 int
10522 gc_heap::init_gc_heap (int  h_number)
10523 {
10524 #ifdef MULTIPLE_HEAPS
10525
10526     time_bgc_last = 0;
10527
10528     allocated_since_last_gc = 0;
10529
10530 #ifdef SPINLOCK_HISTORY
10531     spinlock_info_index = 0;
10532     memset (last_spinlock_info, 0, sizeof(last_spinlock_info));
10533 #endif //SPINLOCK_HISTORY
10534
10535     // initialize per heap members.
10536     ephemeral_low = (uint8_t*)1;
10537
10538     ephemeral_high = MAX_PTR;
10539
10540     ephemeral_heap_segment = 0;
10541
10542     freeable_large_heap_segment = 0;
10543
10544     condemned_generation_num = 0;
10545
10546     blocking_collection = FALSE;
10547
10548     generation_skip_ratio = 100;
10549
10550     mark_stack_tos = 0;
10551
10552     mark_stack_bos = 0;
10553
10554     mark_stack_array_length = 0;
10555
10556     mark_stack_array = 0;
10557
10558 #if defined (_DEBUG) && defined (VERIFY_HEAP)
10559     verify_pinned_queue_p = FALSE;
10560 #endif // _DEBUG && VERIFY_HEAP
10561
10562     loh_pinned_queue_tos = 0;
10563
10564     loh_pinned_queue_bos = 0;
10565
10566     loh_pinned_queue_length = 0;
10567
10568     loh_pinned_queue_decay = LOH_PIN_DECAY;
10569
10570     loh_pinned_queue = 0;
10571
10572     min_overflow_address = MAX_PTR;
10573
10574     max_overflow_address = 0;
10575
10576     gen0_bricks_cleared = FALSE;
10577
10578     gen0_must_clear_bricks = 0;
10579
10580     allocation_quantum = CLR_SIZE;
10581
10582     more_space_lock_soh = gc_lock;
10583
10584     more_space_lock_loh = gc_lock;
10585
10586     ro_segments_in_range = FALSE;
10587
10588     loh_alloc_since_cg = 0;
10589
10590     new_heap_segment = NULL;
10591
10592     gen0_allocated_after_gc_p = false;
10593
10594 #ifdef RECORD_LOH_STATE
10595     loh_state_index = 0;
10596 #endif //RECORD_LOH_STATE
10597 #endif //MULTIPLE_HEAPS
10598
10599 #ifdef MULTIPLE_HEAPS
10600     if (h_number > n_heaps)
10601     {
10602         assert (!"Number of heaps exceeded");
10603         return 0;
10604     }
10605
10606     heap_number = h_number;
10607 #endif //MULTIPLE_HEAPS
10608
10609     memset (&oom_info, 0, sizeof (oom_info));
10610     memset (&fgm_result, 0, sizeof (fgm_result));
10611     if (!gc_done_event.CreateManualEventNoThrow(FALSE))
10612     {
10613         return 0;
10614     }
10615     gc_done_event_lock = -1;
10616     gc_done_event_set = false;
10617
10618 #ifndef SEG_MAPPING_TABLE
10619     if (!gc_heap::seg_table->ensure_space_for_insert ())
10620     {
10621         return 0;
10622     }
10623 #endif //!SEG_MAPPING_TABLE
10624
10625     heap_segment* seg = get_initial_segment (soh_segment_size, h_number);
10626     if (!seg)
10627         return 0;
10628
10629     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg),
10630                               (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)),
10631                               gc_etw_segment_small_object_heap);
10632     
10633 #ifdef SEG_MAPPING_TABLE
10634     seg_mapping_table_add_segment (seg, __this);
10635 #else //SEG_MAPPING_TABLE
10636     seg_table->insert ((uint8_t*)seg, sdelta);
10637 #endif //SEG_MAPPING_TABLE
10638
10639 #ifdef MULTIPLE_HEAPS
10640     heap_segment_heap (seg) = this;
10641 #endif //MULTIPLE_HEAPS
10642
10643     /* todo: Need a global lock for this */
10644     uint32_t* ct = &g_gc_card_table [card_word (card_of (g_gc_lowest_address))];
10645     own_card_table (ct);
10646     card_table = translate_card_table (ct);
10647     /* End of global lock */
10648
10649     brick_table = card_table_brick_table (ct);
10650     highest_address = card_table_highest_address (ct);
10651     lowest_address = card_table_lowest_address (ct);
10652
10653 #ifdef CARD_BUNDLE
10654     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address);
10655     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
10656             card_table_card_bundle_table (ct));
10657 #endif //CARD_BUNDLE
10658
10659 #ifdef MARK_ARRAY
10660     if (gc_can_use_concurrent)
10661         mark_array = translate_mark_array (card_table_mark_array (&g_gc_card_table[card_word (card_of (g_gc_lowest_address))]));
10662     else
10663         mark_array = NULL;
10664 #endif //MARK_ARRAY
10665
10666     uint8_t*  start = heap_segment_mem (seg);
10667
10668     for (int i = 0; i < 1 + max_generation; i++)
10669     {
10670         make_generation (generation_table [ (max_generation - i) ],
10671                          seg, start, 0);
10672         generation_table [(max_generation - i)].gen_num = max_generation - i;
10673         start += Align (min_obj_size);
10674     }
10675
10676     heap_segment_allocated (seg) = start;
10677     alloc_allocated = start;
10678     heap_segment_used (seg) = start - plug_skew;
10679
10680     ephemeral_heap_segment = seg;
10681
10682 #ifndef SEG_MAPPING_TABLE
10683     if (!gc_heap::seg_table->ensure_space_for_insert ())
10684     {
10685         return 0;
10686     }
10687 #endif //!SEG_MAPPING_TABLE
10688     //Create the large segment generation
10689     heap_segment* lseg = get_initial_segment(min_loh_segment_size, h_number);
10690     if (!lseg)
10691         return 0;
10692     lseg->flags |= heap_segment_flags_loh;
10693
10694     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(lseg),
10695                               (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)),
10696                               gc_etw_segment_large_object_heap);
10697
10698 #ifdef SEG_MAPPING_TABLE
10699     seg_mapping_table_add_segment (lseg, __this);
10700 #else //SEG_MAPPING_TABLE
10701     seg_table->insert ((uint8_t*)lseg, sdelta);
10702 #endif //SEG_MAPPING_TABLE
10703
10704     generation_table [max_generation].free_list_allocator = allocator(NUM_GEN2_ALIST, BASE_GEN2_ALIST, gen2_alloc_list);
10705     //assign the alloc_list for the large generation 
10706     generation_table [max_generation+1].free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST, loh_alloc_list);
10707     generation_table [max_generation+1].gen_num = max_generation+1;
10708     make_generation (generation_table [max_generation+1],lseg, heap_segment_mem (lseg), 0);
10709     heap_segment_allocated (lseg) = heap_segment_mem (lseg) + Align (min_obj_size, get_alignment_constant (FALSE));
10710     heap_segment_used (lseg) = heap_segment_allocated (lseg) - plug_skew;
10711
10712     for (int gen_num = 0; gen_num <= 1 + max_generation; gen_num++)
10713     {
10714         generation*  gen = generation_of (gen_num);
10715         make_unused_array (generation_allocation_start (gen), Align (min_obj_size));
10716     }
10717
10718 #ifdef MULTIPLE_HEAPS
10719     heap_segment_heap (lseg) = this;
10720
10721     //initialize the alloc context heap
10722     generation_alloc_context (generation_of (0))->set_alloc_heap(vm_heap);
10723
10724     //initialize the alloc context heap
10725     generation_alloc_context (generation_of (max_generation+1))->set_alloc_heap(vm_heap);
10726
10727 #endif //MULTIPLE_HEAPS
10728
10729     //Do this only once
10730 #ifdef MULTIPLE_HEAPS
10731     if (h_number == 0)
10732 #endif //MULTIPLE_HEAPS
10733     {
10734 #ifndef INTERIOR_POINTERS
10735         //set the brick_table for large objects
10736         //but default value is clearded
10737         //clear_brick_table ((uint8_t*)heap_segment_mem (lseg),
10738         //                   (uint8_t*)heap_segment_reserved (lseg));
10739
10740 #else //INTERIOR_POINTERS
10741
10742         //Because of the interior pointer business, we have to clear
10743         //the whole brick table
10744         //but the default value is cleared
10745         // clear_brick_table (lowest_address, highest_address);
10746 #endif //INTERIOR_POINTERS
10747     }
10748
10749     if (!init_dynamic_data())
10750     {
10751         return 0;
10752     }
10753
10754     etw_allocation_running_amount[0] = 0;
10755     etw_allocation_running_amount[1] = 0;
10756
10757     //needs to be done after the dynamic data has been initialized
10758 #ifndef MULTIPLE_HEAPS
10759     allocation_running_amount = dd_min_size (dynamic_data_of (0));
10760 #endif //!MULTIPLE_HEAPS
10761
10762     fgn_last_alloc = dd_min_size (dynamic_data_of (0));
10763
10764     mark* arr = new (nothrow) (mark [MARK_STACK_INITIAL_LENGTH]);
10765     if (!arr)
10766         return 0;
10767
10768     make_mark_stack(arr);
10769
10770 #ifdef BACKGROUND_GC
10771     freeable_small_heap_segment = 0;
10772     gchist_index_per_heap = 0;
10773     uint8_t** b_arr = new (nothrow) (uint8_t* [MARK_STACK_INITIAL_LENGTH]);
10774     if (!b_arr)
10775         return 0;
10776
10777     make_background_mark_stack (b_arr);
10778 #endif //BACKGROUND_GC
10779
10780     ephemeral_low = generation_allocation_start(generation_of(max_generation - 1));
10781     ephemeral_high = heap_segment_reserved(ephemeral_heap_segment);
10782     if (heap_number == 0)
10783     {
10784         stomp_write_barrier_initialize(
10785 #ifdef MULTIPLE_HEAPS
10786             reinterpret_cast<uint8_t*>(1), reinterpret_cast<uint8_t*>(~0)
10787 #else
10788             ephemeral_low, ephemeral_high
10789 #endif //!MULTIPLE_HEAPS
10790         );
10791     }
10792
10793 #ifdef MARK_ARRAY
10794     // why would we clear the mark array for this page? it should be cleared..
10795     // clear the first committed page
10796     //if(gc_can_use_concurrent)
10797     //{
10798     //    clear_mark_array (align_lower_page (heap_segment_mem (seg)), heap_segment_committed (seg));
10799     //}
10800 #endif //MARK_ARRAY
10801
10802 #ifdef MULTIPLE_HEAPS
10803     //register the heap in the heaps array
10804
10805     if (!create_gc_thread ())
10806         return 0;
10807
10808     g_heaps [heap_number] = this;
10809
10810 #endif //MULTIPLE_HEAPS
10811
10812 #ifdef FEATURE_PREMORTEM_FINALIZATION
10813     HRESULT hr = AllocateCFinalize(&finalize_queue);
10814     if (FAILED(hr))
10815         return 0;
10816 #endif // FEATURE_PREMORTEM_FINALIZATION
10817
10818     max_free_space_items = MAX_NUM_FREE_SPACES;
10819
10820     bestfit_seg = new (nothrow) seg_free_spaces (heap_number);
10821
10822     if (!bestfit_seg)
10823     {
10824         return 0;
10825     }
10826
10827     if (!bestfit_seg->alloc())
10828     {
10829         return 0;
10830     }
10831
10832     last_gc_before_oom = FALSE;
10833
10834     sufficient_gen0_space_p = FALSE;
10835
10836 #ifdef MULTIPLE_HEAPS
10837
10838 #ifdef HEAP_ANALYZE
10839
10840     heap_analyze_success = TRUE;
10841
10842     internal_root_array  = 0;
10843
10844     internal_root_array_index = 0;
10845
10846     internal_root_array_length = initial_internal_roots;
10847
10848     current_obj          = 0;
10849
10850     current_obj_size     = 0;
10851
10852 #endif //HEAP_ANALYZE
10853
10854 #endif // MULTIPLE_HEAPS
10855
10856 #ifdef BACKGROUND_GC
10857     bgc_thread_id.Clear();
10858
10859     if (!create_bgc_thread_support())
10860     {
10861         return 0;
10862     }
10863
10864     bgc_alloc_lock = new (nothrow) exclusive_sync;
10865     if (!bgc_alloc_lock)
10866     {
10867         return 0;
10868     }
10869
10870     bgc_alloc_lock->init();
10871
10872     if (h_number == 0)
10873     {
10874         if (!recursive_gc_sync::init())
10875             return 0;
10876     }
10877
10878     bgc_thread_running = 0;
10879     bgc_thread = 0;
10880     bgc_threads_timeout_cs.Initialize();
10881     expanded_in_fgc = 0;
10882     current_bgc_state = bgc_not_in_process;
10883     background_soh_alloc_count = 0;
10884     background_loh_alloc_count = 0;
10885     bgc_overflow_count = 0;
10886     end_loh_size = dd_min_size (dynamic_data_of (max_generation + 1));
10887 #endif //BACKGROUND_GC
10888
10889 #ifdef GC_CONFIG_DRIVEN
10890     memset (interesting_data_per_heap, 0, sizeof (interesting_data_per_heap));
10891     memset(compact_reasons_per_heap, 0, sizeof (compact_reasons_per_heap));
10892     memset(expand_mechanisms_per_heap, 0, sizeof (expand_mechanisms_per_heap));
10893     memset(interesting_mechanism_bits_per_heap, 0, sizeof (interesting_mechanism_bits_per_heap));
10894 #endif //GC_CONFIG_DRIVEN
10895
10896     return 1;
10897 }
10898
10899 void
10900 gc_heap::destroy_semi_shared()
10901 {
10902 //TODO: will need to move this to per heap
10903 //#ifdef BACKGROUND_GC
10904 //    if (c_mark_list)
10905 //        delete c_mark_list;
10906 //#endif //BACKGROUND_GC
10907
10908 #ifdef MARK_LIST
10909     if (g_mark_list)
10910         delete g_mark_list;
10911 #endif //MARK_LIST
10912
10913 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10914     if (seg_mapping_table)
10915         delete seg_mapping_table;
10916 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10917
10918 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10919     //destroy the segment map
10920     seg_table->delete_sorted_table();
10921 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10922 }
10923
10924 void
10925 gc_heap::self_destroy()
10926 {
10927 #ifdef BACKGROUND_GC
10928     kill_gc_thread();
10929 #endif //BACKGROUND_GC
10930
10931     if (gc_done_event.IsValid())
10932     {
10933         gc_done_event.CloseEvent();
10934     }
10935
10936     // destroy every segment.
10937     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
10938
10939     PREFIX_ASSUME(seg != NULL);
10940
10941     heap_segment* next_seg;
10942     while (seg)
10943     {
10944         next_seg = heap_segment_next_rw (seg);
10945         delete_heap_segment (seg);
10946         seg = next_seg;
10947     }
10948
10949     seg = heap_segment_rw (generation_start_segment (generation_of (max_generation+1)));
10950
10951     PREFIX_ASSUME(seg != NULL);
10952
10953     while (seg)
10954     {
10955         next_seg = heap_segment_next_rw (seg);
10956         delete_heap_segment (seg);
10957         seg = next_seg;
10958     }
10959
10960     // get rid of the card table
10961     release_card_table (card_table);
10962
10963     // destroy the mark stack
10964     delete mark_stack_array;
10965
10966 #ifdef FEATURE_PREMORTEM_FINALIZATION
10967     if (finalize_queue)
10968         delete finalize_queue;
10969 #endif // FEATURE_PREMORTEM_FINALIZATION
10970 }
10971
10972 void
10973 gc_heap::destroy_gc_heap(gc_heap* heap)
10974 {
10975     heap->self_destroy();
10976     delete heap;
10977 }
10978
10979 // Destroys resources owned by gc. It is assumed that a last GC has been performed and that
10980 // the finalizer queue has been drained.
10981 void gc_heap::shutdown_gc()
10982 {
10983     destroy_semi_shared();
10984
10985 #ifdef MULTIPLE_HEAPS
10986     //delete the heaps array
10987     delete g_heaps;
10988     destroy_thread_support();
10989     n_heaps = 0;
10990 #endif //MULTIPLE_HEAPS
10991     //destroy seg_manager
10992
10993     destroy_initial_memory();
10994
10995     GCToOSInterface::Shutdown();
10996 }
10997
10998 inline
10999 BOOL gc_heap::size_fit_p (size_t size REQD_ALIGN_AND_OFFSET_DCL, uint8_t* alloc_pointer, uint8_t* alloc_limit,
11000                           uint8_t* old_loc, int use_padding)
11001 {
11002     BOOL already_padded = FALSE;
11003 #ifdef SHORT_PLUGS
11004     if ((old_loc != 0) && (use_padding & USE_PADDING_FRONT))
11005     {
11006         alloc_pointer = alloc_pointer + Align (min_obj_size);
11007         already_padded = TRUE;
11008     }
11009 #endif //SHORT_PLUGS
11010
11011     if (!((old_loc == 0) || same_large_alignment_p (old_loc, alloc_pointer)))
11012         size = size + switch_alignment_size (already_padded);
11013
11014 #ifdef FEATURE_STRUCTALIGN
11015     alloc_pointer = StructAlign(alloc_pointer, requiredAlignment, alignmentOffset);
11016 #endif // FEATURE_STRUCTALIGN
11017
11018     // in allocate_in_condemned_generation we can have this when we
11019     // set the alloc_limit to plan_allocated which could be less than 
11020     // alloc_ptr
11021     if (alloc_limit < alloc_pointer)
11022     {
11023         return FALSE;
11024     }
11025
11026     if (old_loc != 0)
11027     {
11028         return (((size_t)(alloc_limit - alloc_pointer) >= (size + ((use_padding & USE_PADDING_TAIL)? Align(min_obj_size) : 0))) 
11029 #ifdef SHORT_PLUGS
11030                 ||((!(use_padding & USE_PADDING_FRONT)) && ((alloc_pointer + size) == alloc_limit))
11031 #else //SHORT_PLUGS
11032                 ||((alloc_pointer + size) == alloc_limit)
11033 #endif //SHORT_PLUGS
11034             );
11035     }
11036     else
11037     {
11038         assert (size == Align (min_obj_size));
11039         return ((size_t)(alloc_limit - alloc_pointer) >= size);
11040     }
11041 }
11042
11043 inline
11044 BOOL gc_heap::a_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit,
11045                             int align_const)
11046 {
11047     // We could have run into cases where this is true when alloc_allocated is the 
11048     // the same as the seg committed.
11049     if (alloc_limit < alloc_pointer)
11050     {
11051         return FALSE;
11052     }
11053
11054     return ((size_t)(alloc_limit - alloc_pointer) >= (size + Align(min_obj_size, align_const)));
11055 }
11056
11057 // Grow by committing more pages
11058 BOOL gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* high_address, bool* hard_limit_exceeded_p)
11059 {
11060     assert (high_address <= heap_segment_reserved (seg));
11061
11062     if (hard_limit_exceeded_p)
11063         *hard_limit_exceeded_p = false;
11064
11065     //return 0 if we are at the end of the segment.
11066     if (align_on_page (high_address) > heap_segment_reserved (seg))
11067         return FALSE;
11068
11069     if (high_address <= heap_segment_committed (seg))
11070         return TRUE;
11071
11072     size_t c_size = align_on_page ((size_t)(high_address - heap_segment_committed (seg)));
11073     c_size = max (c_size, commit_min_th);
11074     c_size = min (c_size, (size_t)(heap_segment_reserved (seg) - heap_segment_committed (seg)));
11075
11076     if (c_size == 0)
11077         return FALSE;
11078
11079     STRESS_LOG2(LF_GC, LL_INFO10000,
11080                 "Growing heap_segment: %Ix high address: %Ix\n",
11081                 (size_t)seg, (size_t)high_address);
11082
11083     bool ret = virtual_commit (heap_segment_committed (seg), c_size, heap_number, hard_limit_exceeded_p);
11084     if (ret)
11085     {
11086 #ifdef MARK_ARRAY
11087 #ifndef BACKGROUND_GC
11088         clear_mark_array (heap_segment_committed (seg),
11089                         heap_segment_committed (seg)+c_size, TRUE);
11090 #endif //BACKGROUND_GC
11091 #endif //MARK_ARRAY
11092         heap_segment_committed (seg) += c_size;
11093
11094         STRESS_LOG1(LF_GC, LL_INFO10000, "New commit: %Ix",
11095                     (size_t)heap_segment_committed (seg));
11096
11097         assert (heap_segment_committed (seg) <= heap_segment_reserved (seg));
11098         assert (high_address <= heap_segment_committed (seg));
11099     }
11100
11101     return !!ret;
11102 }
11103
11104 inline
11105 int gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* allocated, uint8_t* old_loc, size_t size, BOOL pad_front_p  REQD_ALIGN_AND_OFFSET_DCL)
11106 {
11107 #ifdef SHORT_PLUGS
11108     if ((old_loc != 0) && pad_front_p)
11109     {
11110         allocated = allocated + Align (min_obj_size);
11111     }
11112 #endif //SHORT_PLUGS
11113
11114     if (!((old_loc == 0) || same_large_alignment_p (old_loc, allocated)))
11115         size = size + switch_alignment_size (FALSE);
11116 #ifdef FEATURE_STRUCTALIGN
11117     size_t pad = ComputeStructAlignPad(allocated, requiredAlignment, alignmentOffset);
11118     return grow_heap_segment (seg, allocated + pad + size);
11119 #else // FEATURE_STRUCTALIGN
11120     return grow_heap_segment (seg, allocated + size);
11121 #endif // FEATURE_STRUCTALIGN
11122 }
11123
11124 //used only in older generation allocation (i.e during gc).
11125 void gc_heap::adjust_limit (uint8_t* start, size_t limit_size, generation* gen,
11126                             int gennum)
11127 {
11128     UNREFERENCED_PARAMETER(gennum);
11129     dprintf (3, ("gc Expanding segment allocation"));
11130     heap_segment* seg = generation_allocation_segment (gen);
11131     if ((generation_allocation_limit (gen) != start) || (start != heap_segment_plan_allocated (seg)))
11132     {
11133         if (generation_allocation_limit (gen) == heap_segment_plan_allocated (seg))
11134         {
11135             assert (generation_allocation_pointer (gen) >= heap_segment_mem (seg));
11136             assert (generation_allocation_pointer (gen) <= heap_segment_committed (seg));
11137             heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
11138         }
11139         else
11140         {
11141             uint8_t*  hole = generation_allocation_pointer (gen);
11142             size_t  size = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
11143
11144             if (size != 0)
11145             {
11146                 dprintf (3, ("filling up hole: %Ix, size %Ix", hole, size));
11147                 size_t allocated_size = generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen);
11148                 if (size >= Align (min_free_list))
11149                 {
11150                     if (allocated_size < min_free_list)
11151                     {
11152                         if (size >= (Align (min_free_list) + Align (min_obj_size)))
11153                         {
11154                             //split hole into min obj + threadable free item
11155                             make_unused_array (hole, min_obj_size);
11156                             generation_free_obj_space (gen) += Align (min_obj_size);
11157                             make_unused_array (hole + Align (min_obj_size), size - Align (min_obj_size));
11158                             generation_free_list_space (gen) += size - Align (min_obj_size);
11159                             generation_allocator(gen)->thread_item_front (hole + Align (min_obj_size), 
11160                                                                           size - Align (min_obj_size));
11161                             add_gen_free (gen->gen_num, (size - Align (min_obj_size)));
11162                         }
11163                         else
11164                         {
11165                             dprintf (3, ("allocated size too small, can't put back rest on free list %Ix", allocated_size));
11166                             make_unused_array (hole, size);
11167                             generation_free_obj_space (gen) += size;
11168                         }
11169                     }
11170                     else 
11171                     {
11172                         dprintf (3, ("threading hole in front of free list"));
11173                         make_unused_array (hole, size);
11174                         generation_free_list_space (gen) += size;
11175                         generation_allocator(gen)->thread_item_front (hole, size);
11176                         add_gen_free (gen->gen_num, size);
11177                     }
11178                 }
11179                 else
11180                 {
11181                     make_unused_array (hole, size);
11182                     generation_free_obj_space (gen) += size;
11183                 }
11184             }
11185         }
11186         generation_allocation_pointer (gen) = start;
11187         generation_allocation_context_start_region (gen) = start;
11188     }
11189     generation_allocation_limit (gen) = (start + limit_size);
11190 }
11191
11192 void verify_mem_cleared (uint8_t* start, size_t size)
11193 {
11194     if (!Aligned (size))
11195     {
11196         FATAL_GC_ERROR();
11197     }
11198
11199     PTR_PTR curr_ptr = (PTR_PTR) start;
11200     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
11201     {
11202         if (*(curr_ptr++) != 0)
11203         {
11204             FATAL_GC_ERROR();
11205         }
11206     }
11207 }
11208
11209 #if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
11210 void gc_heap::set_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11211 {
11212     size_t start_mark_bit = mark_bit_of (start);
11213     size_t end_mark_bit = mark_bit_of (end);
11214     unsigned int startbit = mark_bit_bit (start_mark_bit);
11215     unsigned int endbit = mark_bit_bit (end_mark_bit);
11216     size_t startwrd = mark_bit_word (start_mark_bit);
11217     size_t endwrd = mark_bit_word (end_mark_bit);
11218
11219     dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
11220         (size_t)start, (size_t)start_mark_bit, 
11221         (size_t)end, (size_t)end_mark_bit));
11222
11223     unsigned int firstwrd = ~(lowbits (~0, startbit));
11224     unsigned int lastwrd = ~(highbits (~0, endbit));
11225
11226     if (startwrd == endwrd)
11227     {
11228         unsigned int wrd = firstwrd & lastwrd;
11229         mark_array[startwrd] |= wrd;
11230         return;
11231     }
11232
11233     // set the first mark word.
11234     if (startbit)
11235     {
11236         mark_array[startwrd] |= firstwrd;
11237         startwrd++;
11238     }
11239
11240     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11241     {
11242         mark_array[wrdtmp] = ~(unsigned int)0;
11243     }
11244
11245     // set the last mark word.
11246     if (endbit)
11247     {
11248         mark_array[endwrd] |= lastwrd;
11249     }
11250 }
11251
11252 // makes sure that the mark array bits between start and end are 0.
11253 void gc_heap::check_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11254 {
11255     size_t start_mark_bit = mark_bit_of (start);
11256     size_t end_mark_bit = mark_bit_of (end);
11257     unsigned int startbit = mark_bit_bit (start_mark_bit);
11258     unsigned int endbit = mark_bit_bit (end_mark_bit);
11259     size_t startwrd = mark_bit_word (start_mark_bit);
11260     size_t endwrd = mark_bit_word (end_mark_bit);
11261
11262     //dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
11263     //    (size_t)start, (size_t)start_mark_bit, 
11264     //    (size_t)end, (size_t)end_mark_bit));
11265
11266     unsigned int firstwrd = ~(lowbits (~0, startbit));
11267     unsigned int lastwrd = ~(highbits (~0, endbit));
11268
11269     if (startwrd == endwrd)
11270     {
11271         unsigned int wrd = firstwrd & lastwrd;
11272         if (mark_array[startwrd] & wrd)
11273         {
11274             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11275                             wrd, startwrd, 
11276                             mark_array [startwrd], mark_word_address (startwrd)));
11277             FATAL_GC_ERROR();
11278         }
11279         return;
11280     }
11281
11282     // set the first mark word.
11283     if (startbit)
11284     {
11285         if (mark_array[startwrd] & firstwrd)
11286         {
11287             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11288                             firstwrd, startwrd, 
11289                             mark_array [startwrd], mark_word_address (startwrd)));
11290             FATAL_GC_ERROR();
11291         }
11292
11293         startwrd++;
11294     }
11295
11296     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11297     {
11298         if (mark_array[wrdtmp])
11299         {
11300             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11301                             wrdtmp, 
11302                             mark_array [wrdtmp], mark_word_address (wrdtmp)));
11303             FATAL_GC_ERROR();
11304         }
11305     }
11306
11307     // set the last mark word.
11308     if (endbit)
11309     {
11310         if (mark_array[endwrd] & lastwrd)
11311         {
11312             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11313                             lastwrd, lastwrd, 
11314                             mark_array [lastwrd], mark_word_address (lastwrd)));
11315             FATAL_GC_ERROR();
11316         }
11317     }
11318 }
11319 #endif //VERIFY_HEAP && BACKGROUND_GC
11320
11321 allocator::allocator (unsigned int num_b, size_t fbs, alloc_list* b)
11322 {
11323     assert (num_b < MAX_BUCKET_COUNT);
11324     num_buckets = num_b;
11325     frst_bucket_size = fbs;
11326     buckets = b;
11327 }
11328
11329 alloc_list& allocator::alloc_list_of (unsigned int bn)
11330 {
11331     assert (bn < num_buckets);
11332     if (bn == 0)
11333         return first_bucket;
11334     else
11335         return buckets [bn-1];
11336 }
11337
11338 size_t& allocator::alloc_list_damage_count_of (unsigned int bn)
11339 {
11340     assert (bn < num_buckets);
11341     if (bn == 0)
11342         return first_bucket.alloc_list_damage_count();
11343     else
11344         return buckets [bn-1].alloc_list_damage_count();
11345 }
11346
11347 void allocator::unlink_item (unsigned int bn, uint8_t* item, uint8_t* prev_item, BOOL use_undo_p)
11348 {
11349     //unlink the free_item
11350     alloc_list* al = &alloc_list_of (bn);
11351     if (prev_item)
11352     {
11353         if (use_undo_p && (free_list_undo (prev_item) == UNDO_EMPTY))
11354         {
11355             assert (item == free_list_slot (prev_item));
11356             free_list_undo (prev_item) = item;
11357             alloc_list_damage_count_of (bn)++;
11358         }
11359         free_list_slot (prev_item) = free_list_slot(item);
11360     }
11361     else
11362     {
11363         al->alloc_list_head() = (uint8_t*)free_list_slot(item);
11364     }
11365     if (al->alloc_list_tail() == item)
11366     {
11367         al->alloc_list_tail() = prev_item;
11368     }
11369 }
11370
11371 void allocator::clear()
11372 {
11373     for (unsigned int i = 0; i < num_buckets; i++)
11374     {
11375         alloc_list_head_of (i) = 0;
11376         alloc_list_tail_of (i) = 0;
11377     }
11378 }
11379
11380 //always thread to the end.
11381 void allocator::thread_free_item (uint8_t* item, uint8_t*& head, uint8_t*& tail)
11382 {
11383     free_list_slot (item) = 0;
11384     free_list_undo (item) = UNDO_EMPTY;
11385     assert (item != head);
11386
11387     if (head == 0)
11388     {
11389        head = item;
11390     }
11391     //TODO: This shouldn't happen anymore - verify that's the case.
11392     //the following is necessary because the last free element
11393     //may have been truncated, and tail isn't updated.
11394     else if (free_list_slot (head) == 0)
11395     {
11396         free_list_slot (head) = item;
11397     }
11398     else
11399     {
11400         assert (item != tail);
11401         assert (free_list_slot(tail) == 0);
11402         free_list_slot (tail) = item;
11403     }
11404     tail = item;
11405 }
11406
11407 void allocator::thread_item (uint8_t* item, size_t size)
11408 {
11409     size_t sz = frst_bucket_size;
11410     unsigned int a_l_number = 0; 
11411
11412     for (; a_l_number < (num_buckets-1); a_l_number++)
11413     {
11414         if (size < sz)
11415         {
11416             break;
11417         }
11418         sz = sz * 2;
11419     }
11420     alloc_list* al = &alloc_list_of (a_l_number);
11421     thread_free_item (item, 
11422                       al->alloc_list_head(),
11423                       al->alloc_list_tail());
11424 }
11425
11426 void allocator::thread_item_front (uint8_t* item, size_t size)
11427 {
11428     //find right free list
11429     size_t sz = frst_bucket_size;
11430     unsigned int a_l_number = 0; 
11431     for (; a_l_number < (num_buckets-1); a_l_number++)
11432     {
11433         if (size < sz)
11434         {
11435             break;
11436         }
11437         sz = sz * 2;
11438     }
11439     alloc_list* al = &alloc_list_of (a_l_number);
11440     free_list_slot (item) = al->alloc_list_head();
11441     free_list_undo (item) = UNDO_EMPTY;
11442
11443     if (al->alloc_list_tail() == 0)
11444     {
11445         al->alloc_list_tail() = al->alloc_list_head();
11446     }
11447     al->alloc_list_head() = item;
11448     if (al->alloc_list_tail() == 0)
11449     {
11450         al->alloc_list_tail() = item;
11451     }
11452 }
11453
11454 void allocator::copy_to_alloc_list (alloc_list* toalist)
11455 {
11456     for (unsigned int i = 0; i < num_buckets; i++)
11457     {
11458         toalist [i] = alloc_list_of (i);
11459 #ifdef FL_VERIFICATION
11460         uint8_t* free_item = alloc_list_head_of (i);
11461         size_t count = 0;
11462         while (free_item)
11463         {
11464             count++;
11465             free_item = free_list_slot (free_item);
11466         }
11467
11468         toalist[i].item_count = count;
11469 #endif //FL_VERIFICATION
11470     }
11471 }
11472
11473 void allocator::copy_from_alloc_list (alloc_list* fromalist)
11474 {
11475     BOOL repair_list = !discard_if_no_fit_p ();
11476     for (unsigned int i = 0; i < num_buckets; i++)
11477     {
11478         size_t count = alloc_list_damage_count_of (i);
11479         alloc_list_of (i) = fromalist [i];
11480         assert (alloc_list_damage_count_of (i) == 0);
11481
11482         if (repair_list)
11483         {
11484             //repair the the list
11485             //new items may have been added during the plan phase 
11486             //items may have been unlinked. 
11487             uint8_t* free_item = alloc_list_head_of (i);
11488             while (free_item && count)
11489             {
11490                 assert (((CObjectHeader*)free_item)->IsFree());
11491                 if ((free_list_undo (free_item) != UNDO_EMPTY))
11492                 {
11493                     count--;
11494                     free_list_slot (free_item) = free_list_undo (free_item);
11495                     free_list_undo (free_item) = UNDO_EMPTY;
11496                 }
11497
11498                 free_item = free_list_slot (free_item);
11499             }
11500
11501 #ifdef FL_VERIFICATION
11502             free_item = alloc_list_head_of (i);
11503             size_t item_count = 0;
11504             while (free_item)
11505             {
11506                 item_count++;
11507                 free_item = free_list_slot (free_item);
11508             }
11509
11510             assert (item_count == alloc_list_of (i).item_count);
11511 #endif //FL_VERIFICATION
11512         }
11513 #ifdef DEBUG
11514         uint8_t* tail_item = alloc_list_tail_of (i);
11515         assert ((tail_item == 0) || (free_list_slot (tail_item) == 0));
11516 #endif
11517     }
11518 }
11519
11520 void allocator::commit_alloc_list_changes()
11521 {
11522     BOOL repair_list = !discard_if_no_fit_p ();
11523     if (repair_list)
11524     {
11525         for (unsigned int i = 0; i < num_buckets; i++)
11526         {
11527             //remove the undo info from list. 
11528             uint8_t* free_item = alloc_list_head_of (i);
11529             size_t count = alloc_list_damage_count_of (i);
11530             while (free_item && count)
11531             {
11532                 assert (((CObjectHeader*)free_item)->IsFree());
11533
11534                 if (free_list_undo (free_item) != UNDO_EMPTY)
11535                 {
11536                     free_list_undo (free_item) = UNDO_EMPTY;
11537                     count--;
11538                 }
11539
11540                 free_item = free_list_slot (free_item);
11541             }
11542
11543             alloc_list_damage_count_of (i) = 0; 
11544         }
11545     }
11546 }
11547
11548 void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size,
11549                                 alloc_context* acontext, heap_segment* seg,
11550                                 int align_const, int gen_number)
11551 {
11552     bool loh_p = (gen_number > 0);
11553     GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
11554
11555     size_t aligned_min_obj_size = Align(min_obj_size, align_const);
11556
11557     if (seg)
11558     {
11559         assert (heap_segment_used (seg) <= heap_segment_committed (seg));
11560     }
11561
11562 #ifdef MULTIPLE_HEAPS
11563     if (gen_number == 0)
11564     {
11565         if (!gen0_allocated_after_gc_p)
11566         {
11567             gen0_allocated_after_gc_p = true;
11568         }
11569     }
11570 #endif //MULTIPLE_HEAPS
11571
11572     dprintf (3, ("Expanding segment allocation [%Ix, %Ix[", (size_t)start,
11573                (size_t)start + limit_size - aligned_min_obj_size));
11574
11575     if ((acontext->alloc_limit != start) &&
11576         (acontext->alloc_limit + aligned_min_obj_size)!= start)
11577     {
11578         uint8_t*  hole = acontext->alloc_ptr;
11579         if (hole != 0)
11580         {
11581             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
11582             dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + size + Align (min_obj_size, align_const)));
11583             // when we are finishing an allocation from a free list
11584             // we know that the free area was Align(min_obj_size) larger
11585             acontext->alloc_bytes -= size;
11586             size_t free_obj_size = size + aligned_min_obj_size;
11587             make_unused_array (hole, free_obj_size);
11588             generation_free_obj_space (generation_of (gen_number)) += free_obj_size;
11589         }
11590         acontext->alloc_ptr = start;
11591     }
11592     else
11593     {
11594         if (gen_number == 0)
11595         {
11596             size_t pad_size = Align (min_obj_size, align_const);
11597             make_unused_array (acontext->alloc_ptr, pad_size);
11598             dprintf (3, ("contigous ac: making min obj gap %Ix->%Ix(%Id)", 
11599                 acontext->alloc_ptr, (acontext->alloc_ptr + pad_size), pad_size));
11600             acontext->alloc_ptr += pad_size;
11601         }
11602     }
11603     acontext->alloc_limit = (start + limit_size - aligned_min_obj_size);
11604     acontext->alloc_bytes += limit_size - ((gen_number < max_generation + 1) ? aligned_min_obj_size : 0);
11605
11606 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11607     if (g_fEnableAppDomainMonitoring)
11608     {
11609         GCToEEInterface::RecordAllocatedBytesForHeap(limit_size, heap_number);
11610     }
11611 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11612
11613     uint8_t* saved_used = 0;
11614
11615     if (seg)
11616     {
11617         saved_used = heap_segment_used (seg);
11618     }
11619
11620     if (seg == ephemeral_heap_segment)
11621     {
11622         //Sometimes the allocated size is advanced without clearing the
11623         //memory. Let's catch up here
11624         if (heap_segment_used (seg) < (alloc_allocated - plug_skew))
11625         {
11626 #ifdef MARK_ARRAY
11627 #ifndef BACKGROUND_GC
11628             clear_mark_array (heap_segment_used (seg) + plug_skew, alloc_allocated);
11629 #endif //BACKGROUND_GC
11630 #endif //MARK_ARRAY
11631             heap_segment_used (seg) = alloc_allocated - plug_skew;
11632         }
11633     }
11634 #ifdef BACKGROUND_GC
11635     else if (seg)
11636     {
11637         uint8_t* old_allocated = heap_segment_allocated (seg) - plug_skew - limit_size;
11638 #ifdef FEATURE_LOH_COMPACTION
11639         old_allocated -= Align (loh_padding_obj_size, align_const);
11640 #endif //FEATURE_LOH_COMPACTION
11641
11642         assert (heap_segment_used (seg) >= old_allocated);
11643     }
11644 #endif //BACKGROUND_GC
11645     if ((seg == 0) ||
11646         (start - plug_skew + limit_size) <= heap_segment_used (seg))
11647     {
11648         add_saved_spinlock_info (loh_p, me_release, mt_clr_mem);
11649         leave_spin_lock (msl);
11650         dprintf (3, ("clearing memory at %Ix for %d bytes", (start - plug_skew), limit_size));
11651         memclr (start - plug_skew, limit_size);
11652     }
11653     else
11654     {
11655         uint8_t* used = heap_segment_used (seg);
11656         heap_segment_used (seg) = start + limit_size - plug_skew;
11657
11658         add_saved_spinlock_info (loh_p, me_release, mt_clr_mem);
11659         leave_spin_lock (msl);
11660
11661         if ((start - plug_skew) < used)
11662         {
11663             if (used != saved_used)
11664             {
11665                 FATAL_GC_ERROR ();
11666             }
11667
11668             dprintf (2, ("clearing memory before used at %Ix for %Id bytes", 
11669                 (start - plug_skew), (plug_skew + used - start)));
11670             memclr (start - plug_skew, used - (start - plug_skew));
11671         }
11672     }
11673
11674     //this portion can be done after we release the lock
11675     if (seg == ephemeral_heap_segment)
11676     {
11677 #ifdef FFIND_OBJECT
11678         if (gen0_must_clear_bricks > 0)
11679         {
11680             //set the brick table to speed up find_object
11681             size_t b = brick_of (acontext->alloc_ptr);
11682             set_brick (b, acontext->alloc_ptr - brick_address (b));
11683             b++;
11684             dprintf (3, ("Allocation Clearing bricks [%Ix, %Ix[",
11685                          b, brick_of (align_on_brick (start + limit_size))));
11686             volatile short* x = &brick_table [b];
11687             short* end_x = &brick_table [brick_of (align_on_brick (start + limit_size))];
11688
11689             for (;x < end_x;x++)
11690                 *x = -1;
11691         }
11692         else
11693 #endif //FFIND_OBJECT
11694         {
11695             gen0_bricks_cleared = FALSE;
11696         }
11697     }
11698
11699     // verifying the memory is completely cleared.
11700     //verify_mem_cleared (start - plug_skew, limit_size);
11701 }
11702
11703 size_t gc_heap::new_allocation_limit (size_t size, size_t physical_limit, int gen_number)
11704 {
11705     dynamic_data* dd = dynamic_data_of (gen_number);
11706     ptrdiff_t new_alloc = dd_new_allocation (dd);
11707     assert (new_alloc == (ptrdiff_t)Align (new_alloc,
11708                                            get_alignment_constant (!(gen_number == (max_generation+1)))));
11709
11710     ptrdiff_t logical_limit = max (new_alloc, (ptrdiff_t)size);
11711     size_t limit = min (logical_limit, (ptrdiff_t)physical_limit);
11712     assert (limit == Align (limit, get_alignment_constant (!(gen_number == (max_generation+1)))));
11713     dd_new_allocation (dd) = (new_alloc - limit);
11714     return limit;
11715 }
11716
11717 size_t gc_heap::limit_from_size (size_t size, size_t physical_limit, int gen_number,
11718                                  int align_const)
11719 {
11720     size_t padded_size = size + Align (min_obj_size, align_const);
11721     // for LOH this is not true...we could select a physical_limit that's exactly the same
11722     // as size.
11723     assert ((gen_number != 0) || (physical_limit >= padded_size));
11724     size_t min_size_to_allocate = ((gen_number == 0) ? allocation_quantum : 0);
11725
11726     // For SOH if the size asked for is very small, we want to allocate more than 
11727     // just what's asked for if possible.
11728     size_t desired_size_to_allocate  = max (padded_size, min_size_to_allocate);
11729     size_t new_physical_limit = min (physical_limit, desired_size_to_allocate);
11730
11731     size_t new_limit = new_allocation_limit (padded_size,
11732                                              new_physical_limit,
11733                                              gen_number);
11734     assert (new_limit >= (size + Align (min_obj_size, align_const)));
11735     dprintf (100, ("requested to allocate %Id bytes, actual size is %Id", size, new_limit));
11736     return new_limit;
11737 }
11738
11739 void gc_heap::handle_oom (int heap_num, oom_reason reason, size_t alloc_size, 
11740                           uint8_t* allocated, uint8_t* reserved)
11741 {
11742     UNREFERENCED_PARAMETER(heap_num);
11743
11744     if (reason == oom_budget)
11745     {
11746         alloc_size = dd_min_size (dynamic_data_of (0)) / 2;
11747     }
11748
11749     if ((reason == oom_budget) && ((!fgm_result.loh_p) && (fgm_result.fgm != fgm_no_failure)))
11750     {
11751         // This means during the last GC we needed to reserve and/or commit more memory
11752         // but we couldn't. We proceeded with the GC and ended up not having enough
11753         // memory at the end. This is a legitimate OOM situtation. Otherwise we 
11754         // probably made a mistake and didn't expand the heap when we should have.
11755         reason = oom_low_mem;
11756     }
11757
11758     oom_info.reason = reason;
11759     oom_info.allocated = allocated;
11760     oom_info.reserved = reserved;
11761     oom_info.alloc_size = alloc_size;
11762     oom_info.gc_index = settings.gc_index;
11763     oom_info.fgm = fgm_result.fgm;
11764     oom_info.size = fgm_result.size;
11765     oom_info.available_pagefile_mb = fgm_result.available_pagefile_mb;
11766     oom_info.loh_p = fgm_result.loh_p;
11767
11768     fgm_result.fgm = fgm_no_failure;
11769
11770     // Break early - before the more_space_lock is release so no other threads
11771     // could have allocated on the same heap when OOM happened.
11772     if (GCConfig::GetBreakOnOOM())
11773     {
11774         GCToOSInterface::DebugBreak();
11775     }
11776 }
11777
11778 #ifdef BACKGROUND_GC
11779 BOOL gc_heap::background_allowed_p()
11780 {
11781     return ( gc_can_use_concurrent && ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)) );
11782 }
11783 #endif //BACKGROUND_GC
11784
11785 void gc_heap::check_for_full_gc (int gen_num, size_t size)
11786 {
11787     BOOL should_notify = FALSE;
11788     // if we detect full gc because of the allocation budget specified this is TRUE;
11789     // it's FALSE if it's due to other factors.
11790     BOOL alloc_factor = TRUE; 
11791     int i = 0;
11792     int n = 0;
11793     int n_initial = gen_num;
11794     BOOL local_blocking_collection = FALSE;
11795     BOOL local_elevation_requested = FALSE;
11796     int new_alloc_remain_percent = 0;
11797
11798     if (full_gc_approach_event_set)
11799     {
11800         return;
11801     }
11802     
11803     if (gen_num != (max_generation + 1))
11804     {
11805         gen_num = max_generation;
11806     }
11807
11808     dynamic_data* dd_full = dynamic_data_of (gen_num);
11809     ptrdiff_t new_alloc_remain = 0;
11810     uint32_t pct = ((gen_num == (max_generation + 1)) ? fgn_loh_percent : fgn_maxgen_percent);
11811
11812     for (int gen_index = 0; gen_index <= (max_generation + 1); gen_index++)
11813     {
11814         dprintf (2, ("FGN: h#%d: gen%d: %Id(%Id)", 
11815                      heap_number, gen_index,
11816                      dd_new_allocation (dynamic_data_of (gen_index)),
11817                      dd_desired_allocation (dynamic_data_of (gen_index))));
11818     }
11819
11820     // For small object allocations we only check every fgn_check_quantum bytes.
11821     if (n_initial == 0)
11822     {
11823         dprintf (2, ("FGN: gen0 last recorded alloc: %Id", fgn_last_alloc));
11824         dynamic_data* dd_0 = dynamic_data_of (n_initial);
11825         if (((fgn_last_alloc - dd_new_allocation (dd_0)) < fgn_check_quantum) &&
11826             (dd_new_allocation (dd_0) >= 0))
11827         {
11828             return;
11829         }
11830         else
11831         {
11832             fgn_last_alloc = dd_new_allocation (dd_0);
11833             dprintf (2, ("FGN: gen0 last recorded alloc is now: %Id", fgn_last_alloc));
11834         }
11835
11836         // We don't consider the size that came from soh 'cause it doesn't contribute to the
11837         // gen2 budget.
11838         size = 0;
11839     }
11840
11841     for (i = n+1; i <= max_generation; i++)
11842     {
11843         if (get_new_allocation (i) <= 0)
11844         {
11845             n = min (i, max_generation);
11846         }
11847         else
11848             break;
11849     }
11850
11851     dprintf (2, ("FGN: h#%d: gen%d budget exceeded", heap_number, n));
11852     if (gen_num == max_generation)
11853     {
11854         // If it's small object heap we should first see if we will even be looking at gen2 budget
11855         // in the next GC or not. If not we should go directly to checking other factors.
11856         if (n < (max_generation - 1))
11857         {
11858             goto check_other_factors;
11859         }
11860     }
11861
11862     new_alloc_remain = dd_new_allocation (dd_full) - size;
11863
11864     new_alloc_remain_percent = (int)(((float)(new_alloc_remain) / (float)dd_desired_allocation (dd_full)) * 100);
11865
11866     dprintf (2, ("FGN: alloc threshold for gen%d is %d%%, current threshold is %d%%", 
11867                  gen_num, pct, new_alloc_remain_percent));
11868
11869     if (new_alloc_remain_percent <= (int)pct)
11870     {
11871 #ifdef BACKGROUND_GC
11872         // If background GC is enabled, we still want to check whether this will
11873         // be a blocking GC or not because we only want to notify when it's a 
11874         // blocking full GC.
11875         if (background_allowed_p())
11876         {
11877             goto check_other_factors;
11878         }
11879 #endif //BACKGROUND_GC
11880
11881         should_notify = TRUE;
11882         goto done;
11883     }
11884
11885 check_other_factors:
11886
11887     dprintf (2, ("FGC: checking other factors"));
11888     n = generation_to_condemn (n, 
11889                                &local_blocking_collection, 
11890                                &local_elevation_requested, 
11891                                TRUE);
11892
11893     if (local_elevation_requested && (n == max_generation))
11894     {
11895         if (settings.should_lock_elevation)
11896         {
11897             int local_elevation_locked_count = settings.elevation_locked_count + 1;
11898             if (local_elevation_locked_count != 6)
11899             {
11900                 dprintf (2, ("FGN: lock count is %d - Condemning max_generation-1", 
11901                     local_elevation_locked_count));
11902                 n = max_generation - 1;
11903             }
11904         }
11905     }
11906
11907     dprintf (2, ("FGN: we estimate gen%d will be collected", n));
11908
11909 #ifdef BACKGROUND_GC
11910     // When background GC is enabled it decreases the accuracy of our predictability -
11911     // by the time the GC happens, we may not be under BGC anymore. If we try to 
11912     // predict often enough it should be ok.
11913     if ((n == max_generation) &&
11914         (recursive_gc_sync::background_running_p()))
11915     {
11916         n = max_generation - 1;
11917         dprintf (2, ("FGN: bgc - 1 instead of 2"));
11918     }
11919
11920     if ((n == max_generation) && !local_blocking_collection)
11921     {
11922         if (!background_allowed_p())
11923         {
11924             local_blocking_collection = TRUE;
11925         }
11926     }
11927 #endif //BACKGROUND_GC
11928
11929     dprintf (2, ("FGN: we estimate gen%d will be collected: %s", 
11930                        n, 
11931                        (local_blocking_collection ? "blocking" : "background")));
11932
11933     if ((n == max_generation) && local_blocking_collection)
11934     {
11935         alloc_factor = FALSE;
11936         should_notify = TRUE;
11937         goto done;
11938     }
11939
11940 done:
11941
11942     if (should_notify)
11943     {
11944         dprintf (2, ("FGN: gen%d detecting full GC approaching(%s) (GC#%d) (%Id%% left in gen%d)", 
11945                      n_initial,
11946                      (alloc_factor ? "alloc" : "other"),
11947                      dd_collection_count (dynamic_data_of (0)),
11948                      new_alloc_remain_percent, 
11949                      gen_num));
11950
11951         send_full_gc_notification (n_initial, alloc_factor);
11952     }
11953 }
11954
11955 void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p)
11956 {
11957     if (!full_gc_approach_event_set)
11958     {
11959         assert (full_gc_approach_event.IsValid());
11960         FIRE_EVENT(GCFullNotify_V1, gen_num, due_to_alloc_p);
11961
11962         full_gc_end_event.Reset();
11963         full_gc_approach_event.Set();
11964         full_gc_approach_event_set = true;
11965     }
11966 }
11967
11968 wait_full_gc_status gc_heap::full_gc_wait (GCEvent *event, int time_out_ms)
11969 {
11970     if (fgn_maxgen_percent == 0)
11971     {
11972         return wait_full_gc_na;
11973     }
11974
11975     uint32_t wait_result = user_thread_wait(event, FALSE, time_out_ms);
11976
11977     if ((wait_result == WAIT_OBJECT_0) || (wait_result == WAIT_TIMEOUT))
11978     {
11979         if (fgn_maxgen_percent == 0)
11980         {
11981             return wait_full_gc_cancelled;
11982         }
11983         
11984         if (wait_result == WAIT_OBJECT_0)
11985         {
11986 #ifdef BACKGROUND_GC
11987             if (fgn_last_gc_was_concurrent)
11988             {
11989                 fgn_last_gc_was_concurrent = FALSE;
11990                 return wait_full_gc_na;
11991             }
11992             else
11993 #endif //BACKGROUND_GC
11994             {
11995                 return wait_full_gc_success;
11996             }
11997         }
11998         else
11999         {
12000             return wait_full_gc_timeout;
12001         }
12002     }
12003     else
12004     {
12005         return wait_full_gc_failed;
12006     }
12007 }
12008
12009 size_t gc_heap::get_full_compact_gc_count()
12010 {
12011     return full_gc_counts[gc_type_compacting];
12012 }
12013
12014 // DTREVIEW - we should check this in dt_low_ephemeral_space_p
12015 // as well.
12016 inline
12017 BOOL gc_heap::short_on_end_of_seg (int gen_number,
12018                                    heap_segment* seg,
12019                                    int align_const)
12020 {
12021     UNREFERENCED_PARAMETER(gen_number);
12022     uint8_t* allocated = heap_segment_allocated(seg);
12023
12024     BOOL sufficient_p = sufficient_space_end_seg (allocated, 
12025                                                   heap_segment_reserved (seg), 
12026                                                   end_space_after_gc(),
12027                                                   tuning_deciding_short_on_seg);
12028     if (!sufficient_p)
12029     {
12030         if (sufficient_gen0_space_p)
12031         {
12032             dprintf (GTC_LOG, ("gen0 has enough free space"));
12033         }
12034
12035         sufficient_p = sufficient_gen0_space_p;
12036     }
12037
12038     return !sufficient_p;
12039 }
12040
12041 #ifdef _MSC_VER
12042 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
12043 #endif // _MSC_VER
12044
12045 inline
12046 BOOL gc_heap::a_fit_free_list_p (int gen_number, 
12047                                  size_t size, 
12048                                  alloc_context* acontext,
12049                                  int align_const)
12050 {
12051     BOOL can_fit = FALSE;
12052     generation* gen = generation_of (gen_number);
12053     allocator* gen_allocator = generation_allocator (gen);
12054     size_t sz_list = gen_allocator->first_bucket_size();
12055     for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
12056     {
12057         if ((size < sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
12058         {
12059             uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
12060             uint8_t* prev_free_item = 0;
12061
12062             while (free_list != 0)
12063             {
12064                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
12065                 size_t free_list_size = unused_array_size (free_list);
12066                 if ((size + Align (min_obj_size, align_const)) <= free_list_size)
12067                 {
12068                     dprintf (3, ("Found adequate unused area: [%Ix, size: %Id",
12069                                  (size_t)free_list, free_list_size));
12070
12071                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
12072                     // We ask for more Align (min_obj_size)
12073                     // to make sure that we can insert a free object
12074                     // in adjust_limit will set the limit lower
12075                     size_t limit = limit_from_size (size, free_list_size, gen_number, align_const);
12076
12077                     uint8_t*  remain = (free_list + limit);
12078                     size_t remain_size = (free_list_size - limit);
12079                     if (remain_size >= Align(min_free_list, align_const))
12080                     {
12081                         make_unused_array (remain, remain_size);
12082                         gen_allocator->thread_item_front (remain, remain_size);
12083                         assert (remain_size >= Align (min_obj_size, align_const));
12084                     }
12085                     else
12086                     {
12087                         //absorb the entire free list
12088                         limit += remain_size;
12089                     }
12090                     generation_free_list_space (gen) -= limit;
12091
12092                     adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
12093
12094                     can_fit = TRUE;
12095                     goto end;
12096                 }
12097                 else if (gen_allocator->discard_if_no_fit_p())
12098                 {
12099                     assert (prev_free_item == 0);
12100                     dprintf (3, ("couldn't use this free area, discarding"));
12101                     generation_free_obj_space (gen) += free_list_size;
12102
12103                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
12104                     generation_free_list_space (gen) -= free_list_size;
12105                 }
12106                 else
12107                 {
12108                     prev_free_item = free_list;
12109                 }
12110                 free_list = free_list_slot (free_list); 
12111             }
12112         }
12113         sz_list = sz_list * 2;
12114     }
12115 end:
12116     return can_fit;
12117 }
12118
12119
12120 #ifdef BACKGROUND_GC
12121 void gc_heap::bgc_loh_alloc_clr (uint8_t* alloc_start,
12122                                  size_t size, 
12123                                  alloc_context* acontext,
12124                                  int align_const, 
12125                                  int lock_index,
12126                                  BOOL check_used_p,
12127                                  heap_segment* seg)
12128 {
12129     make_unused_array (alloc_start, size);
12130
12131 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
12132     if (g_fEnableAppDomainMonitoring)
12133     {
12134         GCToEEInterface::RecordAllocatedBytesForHeap(size, heap_number);
12135     }
12136 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
12137
12138     size_t size_of_array_base = sizeof(ArrayBase);
12139
12140     bgc_alloc_lock->loh_alloc_done_with_index (lock_index);
12141
12142     // clear memory while not holding the lock. 
12143     size_t size_to_skip = size_of_array_base;
12144     size_t size_to_clear = size - size_to_skip - plug_skew;
12145     size_t saved_size_to_clear = size_to_clear;
12146     if (check_used_p)
12147     {
12148         uint8_t* end = alloc_start + size - plug_skew;
12149         uint8_t* used = heap_segment_used (seg);
12150         if (used < end)
12151         {
12152             if ((alloc_start + size_to_skip) < used)
12153             {
12154                 size_to_clear = used - (alloc_start + size_to_skip);
12155             }
12156             else
12157             {
12158                 size_to_clear = 0;
12159             }
12160             dprintf (2, ("bgc loh: setting used to %Ix", end));
12161             heap_segment_used (seg) = end;
12162         }
12163
12164         dprintf (2, ("bgc loh: used: %Ix, alloc: %Ix, end of alloc: %Ix, clear %Id bytes",
12165                      used, alloc_start, end, size_to_clear));
12166     }
12167     else
12168     {
12169         dprintf (2, ("bgc loh: [%Ix-[%Ix(%Id)", alloc_start, alloc_start+size, size));
12170     }
12171
12172 #ifdef VERIFY_HEAP
12173     // since we filled in 0xcc for free object when we verify heap,
12174     // we need to make sure we clear those bytes.
12175     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
12176     {
12177         if (size_to_clear < saved_size_to_clear)
12178         {
12179             size_to_clear = saved_size_to_clear;
12180         }
12181     }
12182 #endif //VERIFY_HEAP
12183     
12184     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear large obj", heap_number));
12185     add_saved_spinlock_info (true, me_release, mt_clr_large_mem);
12186     leave_spin_lock (&more_space_lock_loh);
12187     memclr (alloc_start + size_to_skip, size_to_clear);
12188
12189     bgc_alloc_lock->loh_alloc_set (alloc_start);
12190
12191     acontext->alloc_ptr = alloc_start;
12192     acontext->alloc_limit = (alloc_start + size - Align (min_obj_size, align_const));
12193
12194     // need to clear the rest of the object before we hand it out.
12195     clear_unused_array(alloc_start, size);
12196 }
12197 #endif //BACKGROUND_GC
12198
12199 BOOL gc_heap::a_fit_free_list_large_p (size_t size, 
12200                                        alloc_context* acontext,
12201                                        int align_const)
12202 {
12203     BOOL can_fit = FALSE;
12204     int gen_number = max_generation + 1;
12205     generation* gen = generation_of (gen_number);
12206     allocator* loh_allocator = generation_allocator (gen); 
12207
12208 #ifdef FEATURE_LOH_COMPACTION
12209     size_t loh_pad = Align (loh_padding_obj_size, align_const);
12210 #endif //FEATURE_LOH_COMPACTION
12211
12212 #ifdef BACKGROUND_GC
12213     int cookie = -1;
12214 #endif //BACKGROUND_GC
12215     size_t sz_list = loh_allocator->first_bucket_size();
12216     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
12217     {
12218         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
12219         {
12220             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
12221             uint8_t* prev_free_item = 0;
12222             while (free_list != 0)
12223             {
12224                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
12225
12226                 size_t free_list_size = unused_array_size(free_list);
12227
12228 #ifdef FEATURE_LOH_COMPACTION
12229                 if ((size + loh_pad) <= free_list_size)
12230 #else
12231                 if (((size + Align (min_obj_size, align_const)) <= free_list_size)||
12232                     (size == free_list_size))
12233 #endif //FEATURE_LOH_COMPACTION
12234                 {
12235 #ifdef BACKGROUND_GC
12236                     cookie = bgc_alloc_lock->loh_alloc_set (free_list);
12237                     bgc_track_loh_alloc();
12238 #endif //BACKGROUND_GC
12239
12240                     //unlink the free_item
12241                     loh_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
12242
12243                     // Substract min obj size because limit_from_size adds it. Not needed for LOH
12244                     size_t limit = limit_from_size (size - Align(min_obj_size, align_const), free_list_size, 
12245                                                     gen_number, align_const);
12246
12247 #ifdef FEATURE_LOH_COMPACTION
12248                     make_unused_array (free_list, loh_pad);
12249                     limit -= loh_pad;
12250                     free_list += loh_pad;
12251                     free_list_size -= loh_pad;
12252 #endif //FEATURE_LOH_COMPACTION
12253
12254                     uint8_t*  remain = (free_list + limit);
12255                     size_t remain_size = (free_list_size - limit);
12256                     if (remain_size != 0)
12257                     {
12258                         assert (remain_size >= Align (min_obj_size, align_const));
12259                         make_unused_array (remain, remain_size);
12260                     }
12261                     if (remain_size >= Align(min_free_list, align_const))
12262                     {
12263                         loh_thread_gap_front (remain, remain_size, gen);
12264                         assert (remain_size >= Align (min_obj_size, align_const));
12265                     }
12266                     else
12267                     {
12268                         generation_free_obj_space (gen) += remain_size;
12269                     }
12270                     generation_free_list_space (gen) -= free_list_size;
12271                     dprintf (3, ("found fit on loh at %Ix", free_list));
12272 #ifdef BACKGROUND_GC
12273                     if (cookie != -1)
12274                     {
12275                         bgc_loh_alloc_clr (free_list, limit, acontext, align_const, cookie, FALSE, 0);
12276                     }
12277                     else
12278 #endif //BACKGROUND_GC
12279                     {
12280                         adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
12281                     }
12282
12283                     //fix the limit to compensate for adjust_limit_clr making it too short 
12284                     acontext->alloc_limit += Align (min_obj_size, align_const);
12285                     can_fit = TRUE;
12286                     goto exit;
12287                 }
12288                 prev_free_item = free_list;
12289                 free_list = free_list_slot (free_list); 
12290             }
12291         }
12292         sz_list = sz_list * 2;
12293     }
12294 exit:
12295     return can_fit;
12296 }
12297
12298 #ifdef _MSC_VER
12299 #pragma warning(default:4706)
12300 #endif // _MSC_VER
12301
12302 BOOL gc_heap::a_fit_segment_end_p (int gen_number,
12303                                    heap_segment* seg,
12304                                    size_t size, 
12305                                    alloc_context* acontext,
12306                                    int align_const,
12307                                    BOOL* commit_failed_p)
12308 {
12309     *commit_failed_p = FALSE;
12310     size_t limit = 0;
12311     bool hard_limit_short_seg_end_p = false;
12312 #ifdef BACKGROUND_GC
12313     int cookie = -1;
12314 #endif //BACKGROUND_GC
12315
12316     uint8_t*& allocated = ((gen_number == 0) ?
12317                         alloc_allocated : 
12318                         heap_segment_allocated(seg));
12319
12320     size_t pad = Align (min_obj_size, align_const);
12321
12322 #ifdef FEATURE_LOH_COMPACTION
12323     size_t loh_pad = Align (loh_padding_obj_size, align_const);
12324     if (gen_number == (max_generation + 1))
12325     {
12326         pad += loh_pad;
12327     }
12328 #endif //FEATURE_LOH_COMPACTION
12329
12330     uint8_t* end = heap_segment_committed (seg) - pad;
12331
12332     if (a_size_fit_p (size, allocated, end, align_const))
12333     {
12334         limit = limit_from_size (size, 
12335                                  (end - allocated), 
12336                                  gen_number, align_const);
12337         goto found_fit;
12338     }
12339
12340     end = heap_segment_reserved (seg) - pad;
12341
12342     if (a_size_fit_p (size, allocated, end, align_const))
12343     {
12344         limit = limit_from_size (size, 
12345                                  (end - allocated), 
12346                                  gen_number, align_const);
12347
12348         if (grow_heap_segment (seg, (allocated + limit), &hard_limit_short_seg_end_p))
12349         {
12350             goto found_fit;
12351         }
12352         else
12353         {
12354             if (!hard_limit_short_seg_end_p)
12355             {
12356                 dprintf (2, ("can't grow segment, doing a full gc"));
12357                 *commit_failed_p = TRUE;
12358             }
12359             else
12360             {
12361                 assert (heap_hard_limit);
12362             }
12363         }
12364     }
12365
12366     goto found_no_fit;
12367
12368 found_fit:
12369
12370 #ifdef BACKGROUND_GC
12371     if (gen_number != 0)
12372     {
12373         cookie = bgc_alloc_lock->loh_alloc_set (allocated);
12374         bgc_track_loh_alloc();
12375     }
12376 #endif //BACKGROUND_GC
12377
12378     uint8_t* old_alloc;
12379     old_alloc = allocated;
12380 #ifdef FEATURE_LOH_COMPACTION
12381     if (gen_number == (max_generation + 1))
12382     {
12383         make_unused_array (old_alloc, loh_pad);
12384         old_alloc += loh_pad;
12385         allocated += loh_pad;
12386         limit -= loh_pad;
12387     }
12388 #endif //FEATURE_LOH_COMPACTION
12389
12390 #if defined (VERIFY_HEAP) && defined (_DEBUG)
12391         ((void**) allocated)[-1] = 0;     //clear the sync block
12392 #endif //VERIFY_HEAP && _DEBUG
12393     allocated += limit;
12394
12395     dprintf (3, ("found fit at end of seg: %Ix", old_alloc));
12396
12397 #ifdef BACKGROUND_GC
12398     if (cookie != -1)
12399     {
12400         bgc_loh_alloc_clr (old_alloc, limit, acontext, align_const, cookie, TRUE, seg);
12401     }
12402     else
12403 #endif //BACKGROUND_GC
12404     {
12405         adjust_limit_clr (old_alloc, limit, acontext, seg, align_const, gen_number);
12406     }
12407
12408     return TRUE;
12409
12410 found_no_fit:
12411
12412     return FALSE;
12413 }
12414
12415 BOOL gc_heap::loh_a_fit_segment_end_p (int gen_number,
12416                                        size_t size, 
12417                                        alloc_context* acontext,
12418                                        int align_const,
12419                                        BOOL* commit_failed_p,
12420                                        oom_reason* oom_r)
12421 {
12422     *commit_failed_p = FALSE;
12423     heap_segment* seg = generation_allocation_segment (generation_of (gen_number));
12424     BOOL can_allocate_p = FALSE;
12425
12426     while (seg)
12427     {
12428 #ifdef BACKGROUND_GC
12429         if (seg->flags & heap_segment_flags_loh_delete)
12430         {
12431             dprintf (3, ("h%d skipping seg %Ix to be deleted", heap_number, (size_t)seg));
12432         }
12433         else
12434 #endif //BACKGROUND_GC
12435         {
12436             if (a_fit_segment_end_p (gen_number, seg, (size - Align (min_obj_size, align_const)), 
12437                                         acontext, align_const, commit_failed_p))
12438             {
12439                 acontext->alloc_limit += Align (min_obj_size, align_const);
12440                 can_allocate_p = TRUE;
12441                 break;
12442             }
12443
12444             if (*commit_failed_p)
12445             {
12446                 *oom_r = oom_cant_commit;
12447                 break;
12448             }
12449         }
12450
12451         seg = heap_segment_next_rw (seg);
12452     }
12453
12454     return can_allocate_p;
12455 }
12456
12457 #ifdef BACKGROUND_GC
12458 inline
12459 void gc_heap::wait_for_background (alloc_wait_reason awr, bool loh_p)
12460 {
12461     GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
12462
12463     dprintf (2, ("BGC is already in progress, waiting for it to finish"));
12464     add_saved_spinlock_info (loh_p, me_release, mt_wait_bgc);
12465     leave_spin_lock (msl);
12466     background_gc_wait (awr);
12467     enter_spin_lock (msl);
12468     add_saved_spinlock_info (loh_p, me_acquire, mt_wait_bgc);
12469 }
12470
12471 void gc_heap::wait_for_bgc_high_memory (alloc_wait_reason awr, bool loh_p)
12472 {
12473     if (recursive_gc_sync::background_running_p())
12474     {
12475         uint32_t memory_load;
12476         get_memory_info (&memory_load);
12477         if (memory_load >= m_high_memory_load_th)
12478         {
12479             dprintf (GTC_LOG, ("high mem - wait for BGC to finish, wait reason: %d", awr));
12480             wait_for_background (awr, loh_p);
12481         }
12482     }
12483 }
12484
12485 #endif //BACKGROUND_GC
12486
12487 // We request to trigger an ephemeral GC but we may get a full compacting GC.
12488 // return TRUE if that's the case.
12489 BOOL gc_heap::trigger_ephemeral_gc (gc_reason gr)
12490 {
12491 #ifdef BACKGROUND_GC
12492     wait_for_bgc_high_memory (awr_loh_oos_bgc, false);
12493 #endif //BACKGROUND_GC
12494
12495     BOOL did_full_compact_gc = FALSE;
12496
12497     dprintf (2, ("triggering a gen1 GC"));
12498     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12499     vm_heap->GarbageCollectGeneration(max_generation - 1, gr);
12500
12501 #ifdef MULTIPLE_HEAPS
12502     enter_spin_lock (&more_space_lock_soh);
12503     add_saved_spinlock_info (false, me_acquire, mt_t_eph_gc);
12504 #endif //MULTIPLE_HEAPS
12505
12506     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12507
12508     if (current_full_compact_gc_count > last_full_compact_gc_count)
12509     {
12510         dprintf (2, ("attempted to trigger an ephemeral GC and got a full compacting GC"));
12511         did_full_compact_gc = TRUE;
12512     }
12513
12514     return did_full_compact_gc;
12515 }
12516
12517 BOOL gc_heap::soh_try_fit (int gen_number,
12518                            size_t size, 
12519                            alloc_context* acontext,
12520                            int align_const,
12521                            BOOL* commit_failed_p,
12522                            BOOL* short_seg_end_p)
12523 {
12524     BOOL can_allocate = TRUE;
12525     if (short_seg_end_p)
12526     {
12527         *short_seg_end_p = FALSE;
12528     }
12529
12530     can_allocate = a_fit_free_list_p (gen_number, size, acontext, align_const);
12531     if (!can_allocate)
12532     {
12533         if (short_seg_end_p)
12534         {
12535             *short_seg_end_p = short_on_end_of_seg (gen_number, ephemeral_heap_segment, align_const);
12536         }
12537         // If the caller doesn't care, we always try to fit at the end of seg;
12538         // otherwise we would only try if we are actually not short at end of seg.
12539         if (!short_seg_end_p || !(*short_seg_end_p))
12540         {
12541             can_allocate = a_fit_segment_end_p (gen_number, ephemeral_heap_segment, size, 
12542                                                 acontext, align_const, commit_failed_p);
12543         }
12544     }
12545
12546     return can_allocate;
12547 }
12548
12549 allocation_state gc_heap::allocate_small (int gen_number,
12550                                           size_t size, 
12551                                           alloc_context* acontext,
12552                                           int align_const)
12553 {
12554 #if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS)
12555     if (recursive_gc_sync::background_running_p())
12556     {
12557         background_soh_alloc_count++;
12558         if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0)
12559         {
12560             add_saved_spinlock_info (false, me_release, mt_alloc_small);
12561             leave_spin_lock (&more_space_lock_soh);
12562             bool cooperative_mode = enable_preemptive();
12563             GCToOSInterface::Sleep (bgc_alloc_spin);
12564             disable_preemptive (cooperative_mode);
12565             enter_spin_lock (&more_space_lock_soh);
12566             add_saved_spinlock_info (false, me_acquire, mt_alloc_small);
12567         }
12568         else
12569         {
12570             //GCToOSInterface::YieldThread (0);
12571         }
12572     }
12573 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
12574
12575     gc_reason gr = reason_oos_soh;
12576     oom_reason oom_r = oom_no_failure;
12577
12578     // No variable values should be "carried over" from one state to the other. 
12579     // That's why there are local variable for each state
12580
12581     allocation_state soh_alloc_state = a_state_start;
12582
12583     // If we can get a new seg it means allocation will succeed.
12584     while (1)
12585     {
12586         dprintf (3, ("[h%d]soh state is %s", heap_number, allocation_state_str[soh_alloc_state]));
12587
12588         switch (soh_alloc_state)
12589         {
12590             case a_state_can_allocate:
12591             case a_state_cant_allocate:
12592             {
12593                 goto exit;
12594             }
12595             case a_state_start:
12596             {
12597                 soh_alloc_state = a_state_try_fit;
12598                 break;
12599             }
12600             case a_state_try_fit:
12601             {
12602                 BOOL commit_failed_p = FALSE;
12603                 BOOL can_use_existing_p = FALSE;
12604
12605                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12606                                                   align_const, &commit_failed_p,
12607                                                   NULL);
12608                 soh_alloc_state = (can_use_existing_p ?
12609                                         a_state_can_allocate : 
12610                                         (commit_failed_p ? 
12611                                             a_state_trigger_full_compact_gc :
12612                                             a_state_trigger_ephemeral_gc));
12613                 break;
12614             }
12615             case a_state_try_fit_after_bgc:
12616             {
12617                 BOOL commit_failed_p = FALSE;
12618                 BOOL can_use_existing_p = FALSE;
12619                 BOOL short_seg_end_p = FALSE;
12620
12621                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12622                                                   align_const, &commit_failed_p,
12623                                                   &short_seg_end_p);
12624                 soh_alloc_state = (can_use_existing_p ? 
12625                                         a_state_can_allocate : 
12626                                         (short_seg_end_p ? 
12627                                             a_state_trigger_2nd_ephemeral_gc : 
12628                                             a_state_trigger_full_compact_gc));
12629                 break;
12630             }
12631             case a_state_try_fit_after_cg:
12632             {
12633                 BOOL commit_failed_p = FALSE;
12634                 BOOL can_use_existing_p = FALSE;
12635                 BOOL short_seg_end_p = FALSE;
12636
12637                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12638                                                   align_const, &commit_failed_p,
12639                                                   &short_seg_end_p);
12640
12641                 if (can_use_existing_p)
12642                 {
12643                     soh_alloc_state = a_state_can_allocate;
12644                 }
12645 #ifdef MULTIPLE_HEAPS
12646                 else if (gen0_allocated_after_gc_p)
12647                 {
12648                     // some other threads already grabbed the more space lock and allocated
12649                     // so we should attempt an ephemeral GC again.
12650                     soh_alloc_state = a_state_trigger_ephemeral_gc; 
12651                 }
12652 #endif //MULTIPLE_HEAPS
12653                 else if (short_seg_end_p)
12654                 {
12655                     soh_alloc_state = a_state_cant_allocate;
12656                     oom_r = oom_budget;
12657                 }
12658                 else 
12659                 {
12660                     assert (commit_failed_p);
12661                     soh_alloc_state = a_state_cant_allocate;
12662                     oom_r = oom_cant_commit;
12663                 }
12664                 break;
12665             }
12666             case a_state_check_and_wait_for_bgc:
12667             {
12668                 BOOL bgc_in_progress_p = FALSE;
12669                 BOOL did_full_compacting_gc = FALSE;
12670
12671                 bgc_in_progress_p = check_and_wait_for_bgc (awr_gen0_oos_bgc, &did_full_compacting_gc, false);
12672                 soh_alloc_state = (did_full_compacting_gc ? 
12673                                         a_state_try_fit_after_cg : 
12674                                         a_state_try_fit_after_bgc);
12675                 break;
12676             }
12677             case a_state_trigger_ephemeral_gc:
12678             {
12679                 BOOL commit_failed_p = FALSE;
12680                 BOOL can_use_existing_p = FALSE;
12681                 BOOL short_seg_end_p = FALSE;
12682                 BOOL bgc_in_progress_p = FALSE;
12683                 BOOL did_full_compacting_gc = FALSE;
12684
12685                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12686                 if (did_full_compacting_gc)
12687                 {
12688                     soh_alloc_state = a_state_try_fit_after_cg;
12689                 }
12690                 else
12691                 {
12692                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12693                                                       align_const, &commit_failed_p,
12694                                                       &short_seg_end_p);
12695 #ifdef BACKGROUND_GC
12696                     bgc_in_progress_p = recursive_gc_sync::background_running_p();
12697 #endif //BACKGROUND_GC
12698
12699                     if (can_use_existing_p)
12700                     {
12701                         soh_alloc_state = a_state_can_allocate;
12702                     }
12703                     else
12704                     {
12705                         if (short_seg_end_p)
12706                         {
12707                             if (should_expand_in_full_gc)
12708                             {
12709                                 dprintf (2, ("gen1 GC wanted to expand!"));
12710                                 soh_alloc_state = a_state_trigger_full_compact_gc;
12711                             }
12712                             else
12713                             {
12714                                 soh_alloc_state = (bgc_in_progress_p ? 
12715                                                         a_state_check_and_wait_for_bgc : 
12716                                                         a_state_trigger_full_compact_gc);
12717                             }
12718                         }
12719                         else if (commit_failed_p)
12720                         {
12721                             soh_alloc_state = a_state_trigger_full_compact_gc;
12722                         }
12723                         else
12724                         {
12725 #ifdef MULTIPLE_HEAPS
12726                             // some other threads already grabbed the more space lock and allocated
12727                             // so we should attempt an ephemeral GC again.
12728                             assert (gen0_allocated_after_gc_p);
12729                             soh_alloc_state = a_state_trigger_ephemeral_gc; 
12730 #else //MULTIPLE_HEAPS
12731                             assert (!"shouldn't get here");
12732 #endif //MULTIPLE_HEAPS
12733                         }
12734                     }
12735                 }
12736                 break;
12737             }
12738             case a_state_trigger_2nd_ephemeral_gc:
12739             {
12740                 BOOL commit_failed_p = FALSE;
12741                 BOOL can_use_existing_p = FALSE;
12742                 BOOL short_seg_end_p = FALSE;
12743                 BOOL did_full_compacting_gc = FALSE;
12744
12745
12746                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12747                 
12748                 if (did_full_compacting_gc)
12749                 {
12750                     soh_alloc_state = a_state_try_fit_after_cg;
12751                 }
12752                 else
12753                 {
12754                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12755                                                       align_const, &commit_failed_p,
12756                                                       &short_seg_end_p);
12757                     if (short_seg_end_p || commit_failed_p)
12758                     {
12759                         soh_alloc_state = a_state_trigger_full_compact_gc;
12760                     }
12761                     else
12762                     {
12763                         assert (can_use_existing_p);
12764                         soh_alloc_state = a_state_can_allocate;
12765                     }
12766                 }
12767                 break;
12768             }
12769             case a_state_trigger_full_compact_gc:
12770             {
12771                 if (fgn_maxgen_percent)
12772                 {
12773                     dprintf (2, ("FGN: SOH doing last GC before we throw OOM"));
12774                     send_full_gc_notification (max_generation, FALSE);
12775                 }
12776
12777                 BOOL got_full_compacting_gc = FALSE;
12778
12779                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r, false);
12780                 soh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
12781                 break;
12782             }
12783             default:
12784             {
12785                 assert (!"Invalid state!");
12786                 break;
12787             }
12788         }
12789     }
12790
12791 exit:
12792     if (soh_alloc_state == a_state_cant_allocate)
12793     {
12794         assert (oom_r != oom_no_failure);
12795         handle_oom (heap_number, 
12796                     oom_r, 
12797                     size,
12798                     heap_segment_allocated (ephemeral_heap_segment),
12799                     heap_segment_reserved (ephemeral_heap_segment));
12800
12801         add_saved_spinlock_info (false, me_release, mt_alloc_small_cant);
12802         leave_spin_lock (&more_space_lock_soh);
12803     }
12804
12805     assert ((soh_alloc_state == a_state_can_allocate) ||
12806             (soh_alloc_state == a_state_cant_allocate) ||
12807             (soh_alloc_state == a_state_retry_allocate));
12808
12809     return soh_alloc_state;
12810 }
12811
12812 #ifdef BACKGROUND_GC
12813 inline
12814 void gc_heap::bgc_track_loh_alloc()
12815 {
12816     if (current_c_gc_state == c_gc_state_planning)
12817     {
12818         Interlocked::Increment (&loh_alloc_thread_count);
12819         dprintf (3, ("h%d: inc lc: %d", heap_number, (int32_t)loh_alloc_thread_count));
12820     }
12821 }
12822
12823 inline
12824 void gc_heap::bgc_untrack_loh_alloc()
12825 {
12826     if (current_c_gc_state == c_gc_state_planning)
12827     {
12828         Interlocked::Decrement (&loh_alloc_thread_count);
12829         dprintf (3, ("h%d: dec lc: %d", heap_number, (int32_t)loh_alloc_thread_count));
12830     }
12831 }
12832
12833 BOOL gc_heap::bgc_loh_should_allocate()
12834 {
12835     size_t min_gc_size = dd_min_size (dynamic_data_of (max_generation + 1));
12836
12837     if ((bgc_begin_loh_size + bgc_loh_size_increased) < (min_gc_size * 10))
12838     {
12839         return TRUE;
12840     }
12841
12842     if (((bgc_begin_loh_size / end_loh_size) >= 2) || (bgc_loh_size_increased >= bgc_begin_loh_size))
12843     {
12844         if ((bgc_begin_loh_size / end_loh_size) > 2)
12845         {
12846             dprintf (3, ("alloc-ed too much before bgc started"));
12847         }
12848         else
12849         {
12850             dprintf (3, ("alloc-ed too much after bgc started"));
12851         }
12852         return FALSE;
12853     }
12854     else
12855     {
12856         bgc_alloc_spin_loh = (uint32_t)(((float)bgc_loh_size_increased / (float)bgc_begin_loh_size) * 10);
12857         return TRUE;
12858     }
12859 }
12860 #endif //BACKGROUND_GC
12861
12862 size_t gc_heap::get_large_seg_size (size_t size)
12863 {
12864     size_t default_seg_size = min_loh_segment_size;
12865 #ifdef SEG_MAPPING_TABLE
12866     size_t align_size =  default_seg_size;
12867 #else //SEG_MAPPING_TABLE
12868     size_t align_size =  default_seg_size / 2;
12869 #endif //SEG_MAPPING_TABLE
12870     int align_const = get_alignment_constant (FALSE);
12871     size_t large_seg_size = align_on_page (
12872         max (default_seg_size,
12873             ((size + 2 * Align(min_obj_size, align_const) + OS_PAGE_SIZE +
12874             align_size) / align_size * align_size)));
12875     return large_seg_size;
12876 }
12877
12878 BOOL gc_heap::loh_get_new_seg (generation* gen,
12879                                size_t size,
12880                                int align_const,
12881                                BOOL* did_full_compact_gc,
12882                                oom_reason* oom_r)
12883 {
12884     UNREFERENCED_PARAMETER(gen);
12885     UNREFERENCED_PARAMETER(align_const);
12886
12887     *did_full_compact_gc = FALSE;
12888
12889     size_t seg_size = get_large_seg_size (size);
12890
12891     heap_segment* new_seg = get_large_segment (seg_size, did_full_compact_gc);
12892
12893     if (new_seg)
12894     {
12895         loh_alloc_since_cg += seg_size;
12896     }
12897     else
12898     {
12899         *oom_r = oom_loh;
12900     }
12901
12902     return (new_seg != 0);
12903 }
12904
12905 // PERF TODO: this is too aggressive; and in hard limit we should
12906 // count the actual allocated bytes instead of only updating it during
12907 // getting a new seg.
12908 BOOL gc_heap::retry_full_compact_gc (size_t size)
12909 {
12910     size_t seg_size = get_large_seg_size (size);
12911
12912     if (loh_alloc_since_cg >= (2 * (uint64_t)seg_size))
12913     {
12914         return TRUE;
12915     }
12916
12917 #ifdef MULTIPLE_HEAPS
12918     uint64_t total_alloc_size = 0;
12919     for (int i = 0; i < n_heaps; i++)
12920     {
12921         total_alloc_size += g_heaps[i]->loh_alloc_since_cg;
12922     }
12923
12924     if (total_alloc_size >= (2 * (uint64_t)seg_size))
12925     {
12926         return TRUE;
12927     }
12928 #endif //MULTIPLE_HEAPS
12929
12930     return FALSE;
12931 }
12932
12933 BOOL gc_heap::check_and_wait_for_bgc (alloc_wait_reason awr,
12934                                       BOOL* did_full_compact_gc,
12935                                       bool loh_p)
12936 {
12937     BOOL bgc_in_progress = FALSE;
12938     *did_full_compact_gc = FALSE;
12939 #ifdef BACKGROUND_GC
12940     if (recursive_gc_sync::background_running_p())
12941     {
12942         bgc_in_progress = TRUE;
12943         size_t last_full_compact_gc_count = get_full_compact_gc_count();
12944         wait_for_background (awr, loh_p);
12945         size_t current_full_compact_gc_count = get_full_compact_gc_count();
12946         if (current_full_compact_gc_count > last_full_compact_gc_count)
12947         {
12948             *did_full_compact_gc = TRUE;
12949         }
12950     }
12951 #endif //BACKGROUND_GC
12952
12953     return bgc_in_progress;
12954 }
12955
12956 BOOL gc_heap::loh_try_fit (int gen_number,
12957                            size_t size, 
12958                            alloc_context* acontext,
12959                            int align_const,
12960                            BOOL* commit_failed_p,
12961                            oom_reason* oom_r)
12962 {
12963     BOOL can_allocate = TRUE;
12964
12965     if (!a_fit_free_list_large_p (size, acontext, align_const))
12966     {
12967         can_allocate = loh_a_fit_segment_end_p (gen_number, size, 
12968                                                 acontext, align_const, 
12969                                                 commit_failed_p, oom_r);
12970
12971 #ifdef BACKGROUND_GC
12972         if (can_allocate && recursive_gc_sync::background_running_p())
12973         {
12974             bgc_loh_size_increased += size;
12975         }
12976 #endif //BACKGROUND_GC
12977     }
12978 #ifdef BACKGROUND_GC
12979     else
12980     {
12981         if (recursive_gc_sync::background_running_p())
12982         {
12983             bgc_loh_allocated_in_free += size;
12984         }
12985     }
12986 #endif //BACKGROUND_GC
12987
12988     return can_allocate;
12989 }
12990
12991 BOOL gc_heap::trigger_full_compact_gc (gc_reason gr, 
12992                                        oom_reason* oom_r,
12993                                        bool loh_p)
12994 {
12995     BOOL did_full_compact_gc = FALSE;
12996
12997     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12998
12999     // Set this so the next GC will be a full compacting GC.
13000     if (!last_gc_before_oom)
13001     {
13002         last_gc_before_oom = TRUE;
13003     }
13004
13005 #ifdef BACKGROUND_GC
13006     if (recursive_gc_sync::background_running_p())
13007     {
13008         wait_for_background (((gr == reason_oos_soh) ? awr_gen0_oos_bgc : awr_loh_oos_bgc), loh_p);
13009         dprintf (2, ("waited for BGC - done"));
13010     }
13011 #endif //BACKGROUND_GC
13012
13013     GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
13014     size_t current_full_compact_gc_count = get_full_compact_gc_count();
13015     if (current_full_compact_gc_count > last_full_compact_gc_count)
13016     {
13017         dprintf (3, ("a full compacting GC triggered while waiting for BGC (%d->%d)", last_full_compact_gc_count, current_full_compact_gc_count));
13018         assert (current_full_compact_gc_count > last_full_compact_gc_count);
13019         did_full_compact_gc = TRUE;
13020         goto exit;
13021     }
13022
13023     dprintf (3, ("h%d full GC", heap_number));
13024
13025     trigger_gc_for_alloc (max_generation, gr, msl, loh_p, mt_t_full_gc);
13026
13027     current_full_compact_gc_count = get_full_compact_gc_count();
13028
13029     if (current_full_compact_gc_count == last_full_compact_gc_count)
13030     {
13031         dprintf (2, ("attempted to trigger a full compacting GC but didn't get it"));
13032         // We requested a full GC but didn't get because of the elevation logic
13033         // which means we should fail.
13034         *oom_r = oom_unproductive_full_gc;
13035     }
13036     else
13037     {
13038         dprintf (3, ("h%d: T full compacting GC (%d->%d)", 
13039             heap_number, 
13040             last_full_compact_gc_count, 
13041             current_full_compact_gc_count));
13042
13043         assert (current_full_compact_gc_count > last_full_compact_gc_count);
13044         did_full_compact_gc = TRUE;
13045     }
13046
13047 exit:
13048     return did_full_compact_gc;
13049 }
13050
13051 #ifdef RECORD_LOH_STATE
13052 void gc_heap::add_saved_loh_state (allocation_state loh_state_to_save, EEThreadId thread_id)
13053 {
13054     // When the state is can_allocate we already have released the more
13055     // space lock. So we are not logging states here since this code
13056     // is not thread safe.
13057     if (loh_state_to_save != a_state_can_allocate)
13058     {
13059         last_loh_states[loh_state_index].alloc_state = loh_state_to_save;
13060         last_loh_states[loh_state_index].thread_id = thread_id;
13061         loh_state_index++;
13062
13063         if (loh_state_index == max_saved_loh_states)
13064         {
13065             loh_state_index = 0;
13066         }
13067
13068         assert (loh_state_index < max_saved_loh_states);
13069     }
13070 }
13071 #endif //RECORD_LOH_STATE
13072
13073 bool gc_heap::should_retry_other_heap (size_t size)
13074 {
13075 #ifdef MULTIPLE_HEAPS
13076     if (heap_hard_limit)
13077     {
13078         size_t total_heap_committed_recorded = 
13079             current_total_committed - current_total_committed_bookkeeping;
13080         size_t min_size = dd_min_size (g_heaps[0]->dynamic_data_of (max_generation + 1));
13081         size_t slack_space = max (commit_min_th, min_size);
13082         bool retry_p = ((total_heap_committed_recorded + size) < (heap_hard_limit - slack_space));
13083         dprintf (1, ("%Id - %Id - total committed %Id - size %Id = %Id, %s",
13084             heap_hard_limit, slack_space, total_heap_committed_recorded, size,
13085             (heap_hard_limit - slack_space - total_heap_committed_recorded - size),
13086             (retry_p ? "retry" : "no retry")));
13087         return retry_p;
13088     }
13089     else
13090 #endif //MULTIPLE_HEAPS
13091     {
13092         return false;
13093     }
13094 }
13095
13096 allocation_state gc_heap::allocate_large (int gen_number,
13097                                           size_t size, 
13098                                           alloc_context* acontext,
13099                                           int align_const)
13100 {
13101 #ifdef BACKGROUND_GC
13102     if (recursive_gc_sync::background_running_p())
13103     {
13104         background_loh_alloc_count++;
13105         //if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0)
13106         {
13107             if (bgc_loh_should_allocate())
13108             {
13109                 if (!bgc_alloc_spin_loh)
13110                 {
13111                     add_saved_spinlock_info (true, me_release, mt_alloc_large);
13112                     leave_spin_lock (&more_space_lock_loh);
13113                     bool cooperative_mode = enable_preemptive();
13114                     GCToOSInterface::YieldThread (bgc_alloc_spin_loh);
13115                     disable_preemptive (cooperative_mode);
13116                     enter_spin_lock (&more_space_lock_loh);
13117                     add_saved_spinlock_info (true, me_acquire, mt_alloc_large);
13118                     dprintf (SPINLOCK_LOG, ("[%d]spin Emsl loh", heap_number));
13119                 }
13120             }
13121             else
13122             {
13123                 wait_for_background (awr_loh_alloc_during_bgc, true);
13124             }
13125         }
13126     }
13127 #endif //BACKGROUND_GC
13128
13129     gc_reason gr = reason_oos_loh;
13130     generation* gen = generation_of (gen_number);
13131     oom_reason oom_r = oom_no_failure;
13132     size_t current_full_compact_gc_count = 0;
13133
13134     // No variable values should be "carried over" from one state to the other. 
13135     // That's why there are local variable for each state
13136     allocation_state loh_alloc_state = a_state_start;
13137 #ifdef RECORD_LOH_STATE
13138     EEThreadId current_thread_id;
13139     current_thread_id.SetToCurrentThread();
13140 #endif //RECORD_LOH_STATE
13141
13142     // If we can get a new seg it means allocation will succeed.
13143     while (1)
13144     {
13145         dprintf (3, ("[h%d]loh state is %s", heap_number, allocation_state_str[loh_alloc_state]));
13146
13147 #ifdef RECORD_LOH_STATE
13148         add_saved_loh_state (loh_alloc_state, current_thread_id);
13149 #endif //RECORD_LOH_STATE
13150         switch (loh_alloc_state)
13151         {
13152             case a_state_can_allocate:
13153             case a_state_cant_allocate:
13154             {
13155                 goto exit;
13156             }
13157             case a_state_start:
13158             {
13159                 loh_alloc_state = a_state_try_fit;
13160                 break;
13161             }
13162             case a_state_try_fit:
13163             {
13164                 BOOL commit_failed_p = FALSE;
13165                 BOOL can_use_existing_p = FALSE;
13166
13167                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
13168                                                   align_const, &commit_failed_p, &oom_r);
13169                 loh_alloc_state = (can_use_existing_p ?
13170                                         a_state_can_allocate : 
13171                                         (commit_failed_p ? 
13172                                             a_state_trigger_full_compact_gc :
13173                                             a_state_acquire_seg));
13174                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13175                 break;
13176             }
13177             case a_state_try_fit_new_seg:
13178             {
13179                 BOOL commit_failed_p = FALSE;
13180                 BOOL can_use_existing_p = FALSE;
13181
13182                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
13183                                                   align_const, &commit_failed_p, &oom_r);
13184                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
13185                 // another LOH allocating thread could have beat us to acquire the msl so 
13186                 // we need to try again.
13187                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_try_fit);
13188                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13189                 break;
13190             }
13191             case a_state_try_fit_after_cg:
13192             {
13193                 BOOL commit_failed_p = FALSE;
13194                 BOOL can_use_existing_p = FALSE;
13195
13196                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
13197                                                   align_const, &commit_failed_p, &oom_r);
13198                 // If we failed to commit, we bail right away 'cause we already did a 
13199                 // full compacting GC.
13200                 loh_alloc_state = (can_use_existing_p ?
13201                                         a_state_can_allocate : 
13202                                         (commit_failed_p ? 
13203                                             a_state_cant_allocate :
13204                                             a_state_acquire_seg_after_cg));
13205                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13206                 break;
13207             }
13208             case a_state_try_fit_after_bgc:
13209             {
13210                 BOOL commit_failed_p = FALSE;
13211                 BOOL can_use_existing_p = FALSE;
13212
13213                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
13214                                                   align_const, &commit_failed_p, &oom_r);
13215                 loh_alloc_state = (can_use_existing_p ?
13216                                         a_state_can_allocate : 
13217                                         (commit_failed_p ? 
13218                                             a_state_trigger_full_compact_gc :
13219                                             a_state_acquire_seg_after_bgc));
13220                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13221                 break;
13222             }
13223             case a_state_acquire_seg:
13224             {
13225                 BOOL can_get_new_seg_p = FALSE;
13226                 BOOL did_full_compacting_gc = FALSE;
13227
13228                 current_full_compact_gc_count = get_full_compact_gc_count();
13229
13230                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
13231                 loh_alloc_state = (can_get_new_seg_p ? 
13232                                         a_state_try_fit_new_seg : 
13233                                         (did_full_compacting_gc ? 
13234                                             a_state_check_retry_seg :
13235                                             a_state_check_and_wait_for_bgc));
13236                 break;
13237             }
13238             case a_state_acquire_seg_after_cg:
13239             {
13240                 BOOL can_get_new_seg_p = FALSE;
13241                 BOOL did_full_compacting_gc = FALSE;
13242
13243                 current_full_compact_gc_count = get_full_compact_gc_count();
13244
13245                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
13246                 // Since we release the msl before we try to allocate a seg, other
13247                 // threads could have allocated a bunch of segments before us so
13248                 // we might need to retry.
13249                 loh_alloc_state = (can_get_new_seg_p ? 
13250                                         a_state_try_fit_after_cg : 
13251                                         a_state_check_retry_seg);
13252                 break;
13253             }
13254             case a_state_acquire_seg_after_bgc:
13255             {
13256                 BOOL can_get_new_seg_p = FALSE;
13257                 BOOL did_full_compacting_gc = FALSE;
13258              
13259                 current_full_compact_gc_count = get_full_compact_gc_count();
13260
13261                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r); 
13262                 loh_alloc_state = (can_get_new_seg_p ? 
13263                                         a_state_try_fit_new_seg : 
13264                                         (did_full_compacting_gc ? 
13265                                             a_state_check_retry_seg :
13266                                             a_state_trigger_full_compact_gc));
13267                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13268                 break;
13269             }
13270             case a_state_check_and_wait_for_bgc:
13271             {
13272                 BOOL bgc_in_progress_p = FALSE;
13273                 BOOL did_full_compacting_gc = FALSE;
13274
13275                 bgc_in_progress_p = check_and_wait_for_bgc (awr_loh_oos_bgc, &did_full_compacting_gc, true);
13276                 loh_alloc_state = (!bgc_in_progress_p ?
13277                                         a_state_trigger_full_compact_gc : 
13278                                         (did_full_compacting_gc ? 
13279                                             a_state_try_fit_after_cg :
13280                                             a_state_try_fit_after_bgc));
13281                 break;
13282             }
13283             case a_state_trigger_full_compact_gc:
13284             {
13285                 if (fgn_maxgen_percent)
13286                 {
13287                     dprintf (2, ("FGN: LOH doing last GC before we throw OOM"));
13288                     send_full_gc_notification (max_generation, FALSE);
13289                 }
13290
13291                 BOOL got_full_compacting_gc = FALSE;
13292
13293                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r, true);
13294                 loh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
13295                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13296                 break;
13297             }
13298             case a_state_check_retry_seg:
13299             {
13300                 BOOL should_retry_gc = retry_full_compact_gc (size);
13301                 BOOL should_retry_get_seg = FALSE;
13302                 if (!should_retry_gc)
13303                 {
13304                     size_t last_full_compact_gc_count = current_full_compact_gc_count;
13305                     current_full_compact_gc_count = get_full_compact_gc_count();
13306                     if (current_full_compact_gc_count > last_full_compact_gc_count)
13307                     {
13308                         should_retry_get_seg = TRUE;
13309                     }
13310                 }
13311     
13312                 loh_alloc_state = (should_retry_gc ? 
13313                                         a_state_trigger_full_compact_gc : 
13314                                         (should_retry_get_seg ?
13315                                             a_state_try_fit_after_cg :
13316                                             a_state_cant_allocate));
13317                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13318                 break;
13319             }
13320             default:
13321             {
13322                 assert (!"Invalid state!");
13323                 break;
13324             }
13325         }
13326     }
13327
13328 exit:
13329     if (loh_alloc_state == a_state_cant_allocate)
13330     {
13331         assert (oom_r != oom_no_failure);
13332         if (should_retry_other_heap (size))
13333         {
13334             loh_alloc_state = a_state_retry_allocate;
13335         }
13336         else
13337         {
13338             handle_oom (heap_number, 
13339                         oom_r, 
13340                         size,
13341                         0,
13342                         0);
13343         }
13344         add_saved_spinlock_info (true, me_release, mt_alloc_large_cant);
13345         leave_spin_lock (&more_space_lock_loh);
13346     }
13347
13348     assert ((loh_alloc_state == a_state_can_allocate) ||
13349             (loh_alloc_state == a_state_cant_allocate) ||
13350             (loh_alloc_state == a_state_retry_allocate));
13351     return loh_alloc_state;
13352 }
13353
13354 // BGC's final mark phase will acquire the msl, so release it here and re-acquire.
13355 void gc_heap::trigger_gc_for_alloc (int gen_number, gc_reason gr, 
13356                                     GCSpinLock* msl, bool loh_p, 
13357                                     msl_take_state take_state)
13358 {
13359 #ifdef BACKGROUND_GC
13360     if (loh_p)
13361     {
13362         add_saved_spinlock_info (loh_p, me_release, take_state);
13363         leave_spin_lock (msl);
13364     }
13365 #endif //BACKGROUND_GC
13366
13367     vm_heap->GarbageCollectGeneration (gen_number, gr);
13368
13369 #ifdef MULTIPLE_HEAPS
13370     if (!loh_p)
13371     {
13372         enter_spin_lock (msl);
13373         add_saved_spinlock_info (loh_p, me_acquire, take_state);
13374     }
13375 #endif //MULTIPLE_HEAPS
13376
13377 #ifdef BACKGROUND_GC
13378     if (loh_p)
13379     {
13380         enter_spin_lock (msl);
13381         add_saved_spinlock_info (loh_p, me_acquire, take_state);
13382     }
13383 #endif //BACKGROUND_GC
13384 }
13385
13386 allocation_state gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size,
13387                                    int gen_number)
13388 {
13389     if (gc_heap::gc_started)
13390     {
13391         wait_for_gc_done();
13392         return a_state_retry_allocate;
13393     }
13394
13395     bool loh_p = (gen_number > 0);
13396     GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
13397
13398 #ifdef SYNCHRONIZATION_STATS
13399     int64_t msl_acquire_start = GCToOSInterface::QueryPerformanceCounter();
13400 #endif //SYNCHRONIZATION_STATS
13401     enter_spin_lock (msl);
13402     add_saved_spinlock_info (loh_p, me_acquire, mt_try_alloc);
13403     dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc", heap_number));
13404 #ifdef SYNCHRONIZATION_STATS
13405     int64_t msl_acquire = GCToOSInterface::QueryPerformanceCounter() - msl_acquire_start;
13406     total_msl_acquire += msl_acquire;
13407     num_msl_acquired++;
13408     if (msl_acquire > 200)
13409     {
13410         num_high_msl_acquire++;
13411     }
13412     else
13413     {
13414         num_low_msl_acquire++;
13415     }
13416 #endif //SYNCHRONIZATION_STATS
13417
13418     /*
13419     // We are commenting this out 'cause we don't see the point - we already
13420     // have checked gc_started when we were acquiring the msl - no need to check
13421     // again. This complicates the logic in bgc_suspend_EE 'cause that one would
13422     // need to release msl which causes all sorts of trouble.
13423     if (gc_heap::gc_started)
13424     {
13425 #ifdef SYNCHRONIZATION_STATS
13426         good_suspension++;
13427 #endif //SYNCHRONIZATION_STATS
13428         BOOL fStress = (g_pConfig->GetGCStressLevel() & GCConfig::GCSTRESS_TRANSITION) != 0;
13429         if (!fStress)
13430         {
13431             //Rendez vous early (MP scaling issue)
13432             //dprintf (1, ("[%d]waiting for gc", heap_number));
13433             wait_for_gc_done();
13434 #ifdef MULTIPLE_HEAPS
13435             return -1;
13436 #endif //MULTIPLE_HEAPS
13437         }
13438     }
13439     */
13440
13441     dprintf (3, ("requested to allocate %d bytes on gen%d", size, gen_number));
13442
13443     int align_const = get_alignment_constant (gen_number != (max_generation+1));
13444
13445     if (fgn_maxgen_percent)
13446     {
13447         check_for_full_gc (gen_number, size);
13448     }
13449
13450     if (!(new_allocation_allowed (gen_number)))
13451     {
13452         if (fgn_maxgen_percent && (gen_number == 0))
13453         {
13454             // We only check gen0 every so often, so take this opportunity to check again.
13455             check_for_full_gc (gen_number, size);
13456         }
13457
13458 #ifdef BACKGROUND_GC
13459         wait_for_bgc_high_memory (awr_gen0_alloc, loh_p);
13460 #endif //BACKGROUND_GC
13461
13462 #ifdef SYNCHRONIZATION_STATS
13463         bad_suspension++;
13464 #endif //SYNCHRONIZATION_STATS
13465         dprintf (/*100*/ 2, ("running out of budget on gen%d, gc", gen_number));
13466
13467         if (!settings.concurrent || (gen_number == 0))
13468         {
13469             trigger_gc_for_alloc (0, ((gen_number == 0) ? reason_alloc_soh : reason_alloc_loh),
13470                                   msl, loh_p, mt_try_budget);
13471         }
13472     }
13473
13474     allocation_state can_allocate = ((gen_number == 0) ?
13475         allocate_small (gen_number, size, acontext, align_const) :
13476         allocate_large (gen_number, size, acontext, align_const));
13477    
13478     if (can_allocate == a_state_can_allocate)
13479     {
13480         size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr;
13481         int etw_allocation_index = ((gen_number == 0) ? 0 : 1);
13482
13483         etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes;
13484
13485         allocated_since_last_gc += alloc_context_bytes;
13486
13487         if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick)
13488         {
13489 #ifdef FEATURE_REDHAWK
13490             FIRE_EVENT(GCAllocationTick_V1, (uint32_t)etw_allocation_running_amount[etw_allocation_index],
13491                                             (gen_number == 0) ? gc_etw_alloc_soh : gc_etw_alloc_loh);
13492 #else
13493             // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled.
13494             // The ones that do are much less efficient.
13495 #if defined(FEATURE_EVENT_TRACE)
13496             if (EVENT_ENABLED(GCAllocationTick_V3))
13497             {
13498                 fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], gen_number, acontext->alloc_ptr);
13499             }
13500 #endif //FEATURE_EVENT_TRACE
13501 #endif //FEATURE_REDHAWK
13502             etw_allocation_running_amount[etw_allocation_index] = 0;
13503         }
13504     }
13505
13506     return can_allocate;
13507 }
13508
13509 #ifdef MULTIPLE_HEAPS
13510 void gc_heap::balance_heaps (alloc_context* acontext)
13511 {
13512     if (acontext->alloc_count < 4)
13513     {
13514         if (acontext->alloc_count == 0)
13515         {
13516             acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, 0) ));
13517             gc_heap* hp = acontext->get_home_heap()->pGenGCHeap;
13518             dprintf (3, ("First allocation for context %Ix on heap %d\n", (size_t)acontext, (size_t)hp->heap_number));
13519             acontext->set_alloc_heap(acontext->get_home_heap());
13520             hp->alloc_context_count++;
13521         }
13522     }
13523     else
13524     {
13525         BOOL set_home_heap = FALSE;
13526         int hint = 0;
13527
13528         if (heap_select::can_find_heap_fast())
13529         {
13530             if (acontext->get_home_heap() != NULL)
13531                 hint = acontext->get_home_heap()->pGenGCHeap->heap_number;
13532             if (acontext->get_home_heap() != GCHeap::GetHeap(hint = heap_select::select_heap(acontext, hint)) || ((acontext->alloc_count & 15) == 0))
13533             {
13534                 set_home_heap = TRUE;
13535             }
13536         }
13537         else
13538         {
13539             // can't use gdt
13540             if ((acontext->alloc_count & 3) == 0)
13541                 set_home_heap = TRUE;
13542         }
13543
13544         if (set_home_heap)
13545         {
13546 /*
13547             // Since we are balancing up to MAX_SUPPORTED_CPUS, no need for this.
13548             if (n_heaps > MAX_SUPPORTED_CPUS)
13549             {
13550                 // on machines with many processors cache affinity is really king, so don't even try
13551                 // to balance on these.
13552                 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
13553                 acontext->alloc_heap = acontext->home_heap;
13554             }
13555             else
13556 */
13557             {
13558                 gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13559
13560                 dynamic_data* dd = org_hp->dynamic_data_of (0);
13561                 ptrdiff_t org_size = dd_new_allocation (dd);
13562                 int org_alloc_context_count;
13563                 int max_alloc_context_count;
13564                 gc_heap* max_hp;
13565                 ptrdiff_t max_size;
13566                 size_t delta = dd_min_size (dd)/4;
13567
13568                 int start, end, finish;
13569                 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13570                 finish = start + n_heaps;
13571
13572 try_again:
13573                 do
13574                 {
13575                     max_hp = org_hp;
13576                     max_size = org_size + delta;
13577                     acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, hint) ));
13578
13579                     if (org_hp == acontext->get_home_heap()->pGenGCHeap)
13580                         max_size = max_size + delta;
13581
13582                     org_alloc_context_count = org_hp->alloc_context_count;
13583                     max_alloc_context_count = org_alloc_context_count;
13584                     if (max_alloc_context_count > 1)
13585                         max_size /= max_alloc_context_count;
13586
13587                     for (int i = start; i < end; i++)
13588                     {
13589                         gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13590                         dd = hp->dynamic_data_of (0);
13591                         ptrdiff_t size = dd_new_allocation (dd);
13592                         if (hp == acontext->get_home_heap()->pGenGCHeap)
13593                             size = size + delta;
13594                         int hp_alloc_context_count = hp->alloc_context_count;
13595                         if (hp_alloc_context_count > 0)
13596                             size /= (hp_alloc_context_count + 1);
13597                         if (size > max_size)
13598                         {
13599                             max_hp = hp;
13600                             max_size = size;
13601                             max_alloc_context_count = hp_alloc_context_count;
13602                         }
13603                     }
13604                 }
13605                 while (org_alloc_context_count != org_hp->alloc_context_count ||
13606                        max_alloc_context_count != max_hp->alloc_context_count);
13607
13608                 if ((max_hp == org_hp) && (end < finish))
13609                 {   
13610                     start = end; end = finish; 
13611                     delta = dd_min_size(dd)/2; // Make it twice as hard to balance to remote nodes on NUMA.
13612                     goto try_again;
13613                 }
13614
13615                 if (max_hp != org_hp)
13616                 {
13617                     org_hp->alloc_context_count--;
13618                     max_hp->alloc_context_count++;
13619                     acontext->set_alloc_heap(GCHeap::GetHeap(max_hp->heap_number));
13620                     if (!gc_thread_no_affinitize_p)
13621                     {
13622                         if (GCToOSInterface::CanEnableGCCPUGroups())
13623                         {   //only set ideal processor when max_hp and org_hp are in the same cpu
13624                             //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
13625                             uint16_t org_gn = heap_select::find_cpu_group_from_heap_no(org_hp->heap_number);
13626                             uint16_t max_gn = heap_select::find_cpu_group_from_heap_no(max_hp->heap_number);
13627                             if (org_gn == max_gn) //only set within CPU group, so SetThreadIdealProcessor is enough
13628                             {   
13629                                 uint16_t group_proc_no = heap_select::find_group_proc_from_heap_no(max_hp->heap_number);
13630
13631                                 GCThreadAffinity affinity;
13632                                 affinity.Processor = group_proc_no;
13633                                 affinity.Group = org_gn;
13634                                 if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13635                                 {
13636                                     dprintf (3, ("Failed to set the ideal processor and group for heap %d.",
13637                                                 org_hp->heap_number));
13638                                 }
13639                             }
13640                         }
13641                         else 
13642                         {
13643                             uint16_t proc_no = heap_select::find_proc_no_from_heap_no(max_hp->heap_number);
13644
13645                             GCThreadAffinity affinity;
13646                             affinity.Processor = proc_no;
13647                             affinity.Group = GCThreadAffinity::None;
13648
13649                             if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13650                             {
13651                                 dprintf (3, ("Failed to set the ideal processor for heap %d.",
13652                                             org_hp->heap_number));
13653                             }
13654                         }
13655                     }
13656                     dprintf (3, ("Switching context %p (home heap %d) ", 
13657                                  acontext,
13658                         acontext->get_home_heap()->pGenGCHeap->heap_number));
13659                     dprintf (3, (" from heap %d (%Id free bytes, %d contexts) ", 
13660                                  org_hp->heap_number,
13661                                  org_size,
13662                                  org_alloc_context_count));
13663                     dprintf (3, (" to heap %d (%Id free bytes, %d contexts)\n", 
13664                                  max_hp->heap_number,
13665                                  dd_new_allocation(max_hp->dynamic_data_of(0)),
13666                                                    max_alloc_context_count));
13667                 }
13668             }
13669         }
13670     }
13671     acontext->alloc_count++;
13672 }
13673
13674 gc_heap* gc_heap::balance_heaps_loh (alloc_context* acontext, size_t alloc_size)
13675 {
13676     gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13677     dprintf (3, ("[h%d] LA: %Id", org_hp->heap_number, alloc_size));
13678
13679     //if (size > 128*1024)
13680     if (1)
13681     {
13682         dynamic_data* dd = org_hp->dynamic_data_of (max_generation + 1);
13683
13684         ptrdiff_t org_size = dd_new_allocation (dd);
13685         gc_heap* max_hp;
13686         ptrdiff_t max_size;
13687         size_t delta = dd_min_size (dd) * 4;
13688
13689         int start, end, finish;
13690         heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13691         finish = start + n_heaps;
13692
13693 try_again:
13694         {
13695             max_hp = org_hp;
13696             max_size = org_size + delta;
13697             dprintf (3, ("orig hp: %d, max size: %d",
13698                 org_hp->heap_number,
13699                 max_size));
13700
13701             for (int i = start; i < end; i++)
13702             {
13703                 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13704                 dd = hp->dynamic_data_of (max_generation + 1);
13705                 ptrdiff_t size = dd_new_allocation (dd);
13706                 dprintf (3, ("hp: %d, size: %d",
13707                     hp->heap_number,
13708                     size));
13709                 if (size > max_size)
13710                 {
13711                     max_hp = hp;
13712                     max_size = size;
13713                     dprintf (3, ("max hp: %d, max size: %d",
13714                         max_hp->heap_number,
13715                         max_size));
13716                 }
13717             }
13718         }
13719
13720         if ((max_hp == org_hp) && (end < finish))
13721         {
13722             start = end; end = finish;
13723             delta = dd_min_size(dd) * 4;   // Need to tuning delta
13724             goto try_again;
13725         }
13726
13727         if (max_hp != org_hp)
13728         {
13729             dprintf (3, ("loh: %d(%Id)->%d(%Id)", 
13730                 org_hp->heap_number, dd_new_allocation (org_hp->dynamic_data_of (max_generation + 1)),
13731                 max_hp->heap_number, dd_new_allocation (max_hp->dynamic_data_of (max_generation + 1))));
13732         }
13733
13734         return max_hp;
13735     }
13736     else
13737     {
13738         return org_hp;
13739     }
13740 }
13741 #endif //MULTIPLE_HEAPS
13742
13743 BOOL gc_heap::allocate_more_space(alloc_context* acontext, size_t size,
13744                                   int alloc_generation_number)
13745 {
13746     allocation_state status;
13747     do
13748     { 
13749 #ifdef MULTIPLE_HEAPS
13750         if (alloc_generation_number == 0)
13751         {
13752             balance_heaps (acontext);
13753             status = acontext->get_alloc_heap()->pGenGCHeap->try_allocate_more_space (acontext, size, alloc_generation_number);
13754         }
13755         else
13756         {
13757             gc_heap* alloc_heap = balance_heaps_loh (acontext, size);
13758             status = alloc_heap->try_allocate_more_space (acontext, size, alloc_generation_number);
13759             if (status == a_state_retry_allocate)
13760             {
13761                 dprintf (3, ("LOH h%d alloc retry!", alloc_heap->heap_number));
13762             }
13763         }
13764 #else
13765         status = try_allocate_more_space (acontext, size, alloc_generation_number);
13766 #endif //MULTIPLE_HEAPS
13767     }
13768     while (status == a_state_retry_allocate);
13769     
13770     return (status == a_state_can_allocate);
13771 }
13772
13773 inline
13774 CObjectHeader* gc_heap::allocate (size_t jsize, alloc_context* acontext)
13775 {
13776     size_t size = Align (jsize);
13777     assert (size >= Align (min_obj_size));
13778     {
13779     retry:
13780         uint8_t*  result = acontext->alloc_ptr;
13781         acontext->alloc_ptr+=size;
13782         if (acontext->alloc_ptr <= acontext->alloc_limit)
13783         {
13784             CObjectHeader* obj = (CObjectHeader*)result;
13785             assert (obj != 0);
13786             return obj;
13787         }
13788         else
13789         {
13790             acontext->alloc_ptr -= size;
13791
13792 #ifdef _MSC_VER
13793 #pragma inline_depth(0)
13794 #endif //_MSC_VER
13795
13796             if (! allocate_more_space (acontext, size, 0))
13797                 return 0;
13798
13799 #ifdef _MSC_VER
13800 #pragma inline_depth(20)
13801 #endif //_MSC_VER
13802
13803             goto retry;
13804         }
13805     }
13806 }
13807
13808 void  gc_heap::leave_allocation_segment (generation* gen)
13809 {
13810     adjust_limit (0, 0, gen, max_generation);
13811 }
13812
13813 void gc_heap::init_free_and_plug()
13814 {
13815 #ifdef FREE_USAGE_STATS
13816     for (int i = 0; i <= settings.condemned_generation; i++)
13817     {
13818         generation* gen = generation_of (i);
13819         memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces));
13820         memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13821         memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces));
13822     }
13823
13824     if (settings.condemned_generation != max_generation)
13825     {
13826         for (int i = (settings.condemned_generation + 1); i <= max_generation; i++)
13827         {
13828             generation* gen = generation_of (i);
13829             memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13830         }
13831     }
13832 #endif //FREE_USAGE_STATS
13833 }
13834
13835 void gc_heap::print_free_and_plug (const char* msg)
13836 {
13837 #if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF)
13838     int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1));
13839     for (int i = 0; i <= older_gen; i++)
13840     {
13841         generation* gen = generation_of (i);
13842         for (int j = 0; j < NUM_GEN_POWER2; j++)
13843         {
13844             if ((gen->gen_free_spaces[j] != 0) || (gen->gen_plugs[j] != 0))
13845             {
13846                 dprintf (2, ("[%s][h%d][%s#%d]gen%d: 2^%d: F: %Id, P: %Id", 
13847                     msg, 
13848                     heap_number, 
13849                     (settings.concurrent ? "BGC" : "GC"),
13850                     settings.gc_index,
13851                     i,
13852                     (j + 9), gen->gen_free_spaces[j], gen->gen_plugs[j]));
13853             }
13854         }
13855     }
13856 #else
13857     UNREFERENCED_PARAMETER(msg);
13858 #endif //FREE_USAGE_STATS && SIMPLE_DPRINTF
13859 }
13860
13861 void gc_heap::add_gen_plug (int gen_number, size_t plug_size)
13862 {
13863 #ifdef FREE_USAGE_STATS
13864     dprintf (3, ("adding plug size %Id to gen%d", plug_size, gen_number));
13865     generation* gen = generation_of (gen_number);
13866     size_t sz = BASE_GEN_SIZE;
13867     int i = 0;
13868
13869     for (; i < NUM_GEN_POWER2; i++)
13870     {
13871         if (plug_size < sz)
13872         {
13873             break;
13874         }
13875         sz = sz * 2;
13876     }
13877     
13878     (gen->gen_plugs[i])++;
13879 #else
13880     UNREFERENCED_PARAMETER(gen_number);
13881     UNREFERENCED_PARAMETER(plug_size);
13882 #endif //FREE_USAGE_STATS
13883 }
13884
13885 void gc_heap::add_item_to_current_pinned_free (int gen_number, size_t free_size)
13886 {
13887 #ifdef FREE_USAGE_STATS
13888     generation* gen = generation_of (gen_number);
13889     size_t sz = BASE_GEN_SIZE;
13890     int i = 0;
13891
13892     for (; i < NUM_GEN_POWER2; i++)
13893     {
13894         if (free_size < sz)
13895         {
13896             break;
13897         }
13898         sz = sz * 2;
13899     }
13900     
13901     (gen->gen_current_pinned_free_spaces[i])++;
13902     generation_pinned_free_obj_space (gen) += free_size;
13903     dprintf (3, ("left pin free %Id(2^%d) to gen%d, total %Id bytes (%Id)", 
13904         free_size, (i + 10), gen_number, 
13905         generation_pinned_free_obj_space (gen),
13906         gen->gen_current_pinned_free_spaces[i]));
13907 #else
13908     UNREFERENCED_PARAMETER(gen_number);
13909     UNREFERENCED_PARAMETER(free_size);
13910 #endif //FREE_USAGE_STATS
13911 }
13912
13913 void gc_heap::add_gen_free (int gen_number, size_t free_size)
13914 {
13915 #ifdef FREE_USAGE_STATS
13916     dprintf (3, ("adding free size %Id to gen%d", free_size, gen_number));
13917     generation* gen = generation_of (gen_number);
13918     size_t sz = BASE_GEN_SIZE;
13919     int i = 0;
13920
13921     for (; i < NUM_GEN_POWER2; i++)
13922     {
13923         if (free_size < sz)
13924         {
13925             break;
13926         }
13927         sz = sz * 2;
13928     }
13929     
13930     (gen->gen_free_spaces[i])++;
13931 #else
13932     UNREFERENCED_PARAMETER(gen_number);
13933     UNREFERENCED_PARAMETER(free_size);
13934 #endif //FREE_USAGE_STATS
13935 }
13936
13937 void gc_heap::remove_gen_free (int gen_number, size_t free_size)
13938 {
13939 #ifdef FREE_USAGE_STATS
13940     dprintf (3, ("removing free %Id from gen%d", free_size, gen_number));
13941     generation* gen = generation_of (gen_number);
13942     size_t sz = BASE_GEN_SIZE;
13943     int i = 0;
13944
13945     for (; i < NUM_GEN_POWER2; i++)
13946     {
13947         if (free_size < sz)
13948         {
13949             break;
13950         }
13951         sz = sz * 2;
13952     }
13953     
13954     (gen->gen_free_spaces[i])--;
13955 #else
13956     UNREFERENCED_PARAMETER(gen_number);
13957     UNREFERENCED_PARAMETER(free_size);
13958 #endif //FREE_USAGE_STATS
13959 }
13960
13961 uint8_t* gc_heap::allocate_in_older_generation (generation* gen, size_t size,
13962                                              int from_gen_number,
13963                                              uint8_t* old_loc REQD_ALIGN_AND_OFFSET_DCL)
13964 {
13965     size = Align (size);
13966     assert (size >= Align (min_obj_size));
13967     assert (from_gen_number < max_generation);
13968     assert (from_gen_number >= 0);
13969     assert (generation_of (from_gen_number + 1) == gen);
13970
13971     allocator* gen_allocator = generation_allocator (gen);
13972     BOOL discard_p = gen_allocator->discard_if_no_fit_p ();
13973     int pad_in_front = ((old_loc != 0) && ((from_gen_number+1) != max_generation)) ? USE_PADDING_FRONT : 0;
13974
13975     size_t real_size = size + Align (min_obj_size);
13976     if (pad_in_front)
13977         real_size += Align (min_obj_size);
13978
13979     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13980                        generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front)))
13981     {
13982         size_t sz_list = gen_allocator->first_bucket_size();
13983         for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
13984         {
13985             if ((real_size < (sz_list / 2)) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
13986             {
13987                 uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
13988                 uint8_t* prev_free_item = 0;
13989                 while (free_list != 0)
13990                 {
13991                     dprintf (3, ("considering free list %Ix", (size_t)free_list));
13992
13993                     size_t free_list_size = unused_array_size (free_list);
13994
13995                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + free_list_size),
13996                                     old_loc, USE_PADDING_TAIL | pad_in_front))
13997                     {
13998                         dprintf (4, ("F:%Ix-%Id",
13999                                      (size_t)free_list, free_list_size));
14000
14001                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, !discard_p);
14002                         generation_free_list_space (gen) -= free_list_size;
14003                         remove_gen_free (gen->gen_num, free_list_size);
14004
14005                         adjust_limit (free_list, free_list_size, gen, from_gen_number+1);
14006                         generation_allocate_end_seg_p (gen) = FALSE;
14007                         goto finished;
14008                     }
14009                     // We do first fit on bucket 0 because we are not guaranteed to find a fit there.
14010                     else if (discard_p || (a_l_idx == 0))
14011                     {
14012                         dprintf (3, ("couldn't use this free area, discarding"));
14013                         generation_free_obj_space (gen) += free_list_size;
14014
14015                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
14016                         generation_free_list_space (gen) -= free_list_size;
14017                         remove_gen_free (gen->gen_num, free_list_size);
14018                     }
14019                     else
14020                     {
14021                         prev_free_item = free_list;
14022                     }
14023                     free_list = free_list_slot (free_list); 
14024                 }
14025             }
14026             sz_list = sz_list * 2;
14027         }
14028         //go back to the beginning of the segment list 
14029         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
14030         if (seg != generation_allocation_segment (gen))
14031         {
14032             leave_allocation_segment (gen);
14033             generation_allocation_segment (gen) = seg;
14034         }
14035         while (seg != ephemeral_heap_segment)
14036         {
14037             if (size_fit_p(size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
14038                            heap_segment_committed (seg), old_loc, USE_PADDING_TAIL | pad_in_front))
14039             {
14040                 dprintf (3, ("using what's left in committed"));
14041                 adjust_limit (heap_segment_plan_allocated (seg),
14042                               heap_segment_committed (seg) -
14043                               heap_segment_plan_allocated (seg),
14044                               gen, from_gen_number+1);
14045                 generation_allocate_end_seg_p (gen) = TRUE;
14046                 // dformat (t, 3, "Expanding segment allocation");
14047                 heap_segment_plan_allocated (seg) =
14048                     heap_segment_committed (seg);
14049                 goto finished;
14050             }
14051             else
14052             {
14053                 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
14054                                 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
14055                     grow_heap_segment (seg, heap_segment_plan_allocated (seg), old_loc, size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG))
14056                 {
14057                     dprintf (3, ("using what's left in reserved"));
14058                     adjust_limit (heap_segment_plan_allocated (seg),
14059                                   heap_segment_committed (seg) -
14060                                   heap_segment_plan_allocated (seg),
14061                                   gen, from_gen_number+1);
14062                     generation_allocate_end_seg_p (gen) = TRUE;
14063                     heap_segment_plan_allocated (seg) =
14064                         heap_segment_committed (seg);
14065
14066                     goto finished;
14067                 }
14068                 else
14069                 {
14070                     leave_allocation_segment (gen);
14071                     heap_segment*   next_seg = heap_segment_next_rw (seg);
14072                     if (next_seg)
14073                     {
14074                         dprintf (3, ("getting next segment"));
14075                         generation_allocation_segment (gen) = next_seg;
14076                         generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
14077                         generation_allocation_limit (gen) = generation_allocation_pointer (gen);
14078                     }
14079                     else
14080                     {
14081                         size = 0;
14082                         goto finished;
14083                     }
14084                 }
14085             }
14086             seg = generation_allocation_segment (gen);
14087         }
14088         //No need to fix the last region. Will be done later
14089         size = 0;
14090         goto finished;
14091     }
14092     finished:
14093     if (0 == size)
14094     {
14095         return 0;
14096     }
14097     else
14098     {
14099         uint8_t*  result = generation_allocation_pointer (gen);
14100         size_t pad = 0;
14101
14102 #ifdef SHORT_PLUGS
14103         if ((pad_in_front & USE_PADDING_FRONT) &&
14104             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14105              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14106         {
14107             pad = Align (min_obj_size);
14108             set_plug_padded (old_loc);
14109         }
14110 #endif //SHORT_PLUGS
14111
14112 #ifdef FEATURE_STRUCTALIGN
14113         _ASSERTE(!old_loc || alignmentOffset != 0);
14114         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14115         if (old_loc != 0)
14116         {
14117             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14118             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14119             pad += pad1;
14120         }
14121 #else // FEATURE_STRUCTALIGN
14122         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14123         {
14124             pad += switch_alignment_size (is_plug_padded (old_loc));
14125             set_node_realigned (old_loc);
14126             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14127                          (size_t)old_loc, (size_t)(result+pad)));
14128             assert (same_large_alignment_p (result + pad, old_loc));
14129         }
14130 #endif // FEATURE_STRUCTALIGN
14131         dprintf (3, ("Allocate %Id bytes", size));
14132
14133         if ((old_loc == 0) || (pad != 0))
14134         {
14135             //allocating a non plug or a gap, so reset the start region
14136             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14137         }
14138
14139         generation_allocation_pointer (gen) += size + pad;
14140         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14141         if (generation_allocate_end_seg_p (gen))
14142         {
14143             generation_end_seg_allocated (gen) += size;
14144         }
14145         else
14146         {
14147             generation_free_list_allocated (gen) += size;
14148         }
14149         generation_allocation_size (gen) += size;
14150
14151         dprintf (3, ("aio: ptr: %Ix, limit: %Ix, sr: %Ix", 
14152             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14153             generation_allocation_context_start_region (gen)));
14154
14155         return result + pad;;
14156     }
14157 }
14158
14159 void gc_heap::repair_allocation_in_expanded_heap (generation* consing_gen)
14160 {
14161     //make sure that every generation has a planned allocation start
14162     int  gen_number = max_generation - 1;
14163     while (gen_number>= 0)
14164     {
14165         generation* gen = generation_of (gen_number);
14166         if (0 == generation_plan_allocation_start (gen))
14167         {
14168             realloc_plan_generation_start (gen, consing_gen);
14169
14170             assert (generation_plan_allocation_start (gen));
14171         }
14172         gen_number--;
14173     }
14174
14175     // now we know the planned allocation size
14176     size_t  size = (generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
14177     heap_segment* seg = generation_allocation_segment (consing_gen);
14178     if (generation_allocation_limit (consing_gen) == heap_segment_plan_allocated (seg))
14179     {
14180         if (size != 0)
14181         {
14182             heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
14183         }
14184     }
14185     else
14186     {
14187         assert (settings.condemned_generation == max_generation);
14188         uint8_t* first_address = generation_allocation_limit (consing_gen);
14189         //look through the pinned plugs for relevant ones.
14190         //Look for the right pinned plug to start from.
14191         size_t mi = 0;
14192         mark* m = 0;
14193         while (mi != mark_stack_tos)
14194         {
14195             m = pinned_plug_of (mi);
14196             if ((pinned_plug (m) == first_address))
14197                 break;
14198             else
14199                 mi++;
14200         }
14201         assert (mi != mark_stack_tos);
14202         pinned_len (m) = size;
14203     }
14204 }
14205
14206 //tododefrag optimize for new segment (plan_allocated == mem)
14207 uint8_t* gc_heap::allocate_in_expanded_heap (generation* gen,
14208                                           size_t size,
14209                                           BOOL& adjacentp,
14210                                           uint8_t* old_loc,
14211 #ifdef SHORT_PLUGS
14212                                           BOOL set_padding_on_saved_p,
14213                                           mark* pinned_plug_entry,
14214 #endif //SHORT_PLUGS
14215                                           BOOL consider_bestfit,
14216                                           int active_new_gen_number
14217                                           REQD_ALIGN_AND_OFFSET_DCL)
14218 {
14219     UNREFERENCED_PARAMETER(active_new_gen_number);
14220     dprintf (3, ("aie: P: %Ix, size: %Ix", old_loc, size));
14221
14222     size = Align (size);
14223     assert (size >= Align (min_obj_size));
14224     int pad_in_front = ((old_loc != 0) && (active_new_gen_number != max_generation)) ? USE_PADDING_FRONT : 0;
14225
14226     if (consider_bestfit && use_bestfit)
14227     {
14228         assert (bestfit_seg);
14229         dprintf (SEG_REUSE_LOG_1, ("reallocating 0x%Ix in expanded heap, size: %Id", 
14230                     old_loc, size));
14231         return bestfit_seg->fit (old_loc, 
14232 #ifdef SHORT_PLUGS
14233                                  set_padding_on_saved_p,
14234                                  pinned_plug_entry,
14235 #endif //SHORT_PLUGS
14236                                  size REQD_ALIGN_AND_OFFSET_ARG);
14237     }
14238
14239     heap_segment* seg = generation_allocation_segment (gen);
14240
14241     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14242                        generation_allocation_limit (gen), old_loc,
14243                        ((generation_allocation_limit (gen) !=
14244                           heap_segment_plan_allocated (seg))? USE_PADDING_TAIL : 0) | pad_in_front)))
14245     {
14246         dprintf (3, ("aie: can't fit: ptr: %Ix, limit: %Ix", generation_allocation_pointer (gen),
14247             generation_allocation_limit (gen)));
14248
14249         adjacentp = FALSE;
14250         uint8_t* first_address = (generation_allocation_limit (gen) ?
14251                                generation_allocation_limit (gen) :
14252                                heap_segment_mem (seg));
14253         assert (in_range_for_segment (first_address, seg));
14254
14255         uint8_t* end_address   = heap_segment_reserved (seg);
14256
14257         dprintf (3, ("aie: first_addr: %Ix, gen alloc limit: %Ix, end_address: %Ix",
14258             first_address, generation_allocation_limit (gen), end_address));
14259
14260         size_t mi = 0;
14261         mark* m = 0;
14262
14263         if (heap_segment_allocated (seg) != heap_segment_mem (seg))
14264         {
14265             assert (settings.condemned_generation == max_generation);
14266             //look through the pinned plugs for relevant ones.
14267             //Look for the right pinned plug to start from.
14268             while (mi != mark_stack_tos)
14269             {
14270                 m = pinned_plug_of (mi);
14271                 if ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))
14272                 {
14273                     dprintf (3, ("aie: found pin: %Ix", pinned_plug (m)));
14274                     break;
14275                 }
14276                 else
14277                     mi++;
14278             }
14279             if (mi != mark_stack_tos)
14280             {
14281                 //fix old free list.
14282                 size_t  hsize = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
14283                 {
14284                     dprintf(3,("gc filling up hole"));
14285                     ptrdiff_t mi1 = (ptrdiff_t)mi;
14286                     while ((mi1 >= 0) &&
14287                            (pinned_plug (pinned_plug_of(mi1)) != generation_allocation_limit (gen)))
14288                     {
14289                         dprintf (3, ("aie: checking pin %Ix", pinned_plug (pinned_plug_of(mi1))));
14290                         mi1--;
14291                     }
14292                     if (mi1 >= 0)
14293                     {
14294                         size_t saved_pinned_len = pinned_len (pinned_plug_of(mi1));
14295                         pinned_len (pinned_plug_of(mi1)) = hsize;
14296                         dprintf (3, ("changing %Ix len %Ix->%Ix", 
14297                             pinned_plug (pinned_plug_of(mi1)), 
14298                             saved_pinned_len, pinned_len (pinned_plug_of(mi1))));
14299                     }
14300                 }
14301             }
14302         }
14303         else
14304         {
14305             assert (generation_allocation_limit (gen) ==
14306                     generation_allocation_pointer (gen));
14307             mi = mark_stack_tos;
14308         }
14309
14310         while ((mi != mark_stack_tos) && in_range_for_segment (pinned_plug (m), seg))
14311         {
14312             size_t len = pinned_len (m);
14313             uint8_t*  free_list = (pinned_plug (m) - len);
14314             dprintf (3, ("aie: testing free item: %Ix->%Ix(%Ix)", 
14315                 free_list, (free_list + len), len));
14316             if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + len), old_loc, USE_PADDING_TAIL | pad_in_front))
14317             {
14318                 dprintf (3, ("aie: Found adequate unused area: %Ix, size: %Id",
14319                             (size_t)free_list, len));
14320                 {
14321                     generation_allocation_pointer (gen) = free_list;
14322                     generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14323                     generation_allocation_limit (gen) = (free_list + len);
14324                 }
14325                 goto allocate_in_free;
14326             }
14327             mi++;
14328             m = pinned_plug_of (mi);
14329         }
14330
14331         //switch to the end of the segment.
14332         generation_allocation_pointer (gen) = heap_segment_plan_allocated (seg);
14333         generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14334         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14335         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14336         dprintf (3, ("aie: switching to end of seg: %Ix->%Ix(%Ix)", 
14337             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14338             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
14339
14340         if (!size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14341                          generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front))
14342         {
14343             dprintf (3, ("aie: ptr: %Ix, limit: %Ix, can't alloc", generation_allocation_pointer (gen),
14344                 generation_allocation_limit (gen)));
14345             assert (!"Can't allocate if no free space");
14346             return 0;
14347         }
14348     }
14349     else
14350     {
14351         adjacentp = TRUE;
14352     }
14353
14354 allocate_in_free:
14355     {
14356         uint8_t*  result = generation_allocation_pointer (gen);
14357         size_t pad = 0;
14358
14359 #ifdef SHORT_PLUGS
14360         if ((pad_in_front & USE_PADDING_FRONT) &&
14361             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14362              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14363
14364         {
14365             pad = Align (min_obj_size);
14366             set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
14367         }
14368 #endif //SHORT_PLUGS
14369
14370 #ifdef FEATURE_STRUCTALIGN
14371         _ASSERTE(!old_loc || alignmentOffset != 0);
14372         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14373         if (old_loc != 0)
14374         {
14375             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14376             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14377             pad += pad1;
14378             adjacentp = FALSE;
14379         }
14380 #else // FEATURE_STRUCTALIGN
14381         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14382         {
14383             pad += switch_alignment_size (is_plug_padded (old_loc));
14384             set_node_realigned (old_loc);
14385             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14386                          (size_t)old_loc, (size_t)(result+pad)));
14387             assert (same_large_alignment_p (result + pad, old_loc));
14388             adjacentp = FALSE;
14389         }
14390 #endif // FEATURE_STRUCTALIGN
14391
14392         if ((old_loc == 0) || (pad != 0))
14393         {
14394             //allocating a non plug or a gap, so reset the start region
14395             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14396         }
14397
14398         generation_allocation_pointer (gen) += size + pad;
14399         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14400         dprintf (3, ("Allocated in expanded heap %Ix:%Id", (size_t)(result+pad), size));
14401
14402         dprintf (3, ("aie: ptr: %Ix, limit: %Ix, sr: %Ix", 
14403             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14404             generation_allocation_context_start_region (gen)));
14405
14406         return result + pad;
14407     }
14408 }
14409
14410 generation*  gc_heap::ensure_ephemeral_heap_segment (generation* consing_gen)
14411 {
14412     heap_segment* seg = generation_allocation_segment (consing_gen);
14413     if (seg != ephemeral_heap_segment)
14414     {
14415         assert (generation_allocation_pointer (consing_gen)>= heap_segment_mem (seg));
14416         assert (generation_allocation_pointer (consing_gen)<= heap_segment_committed (seg));
14417
14418         //fix the allocated size of the segment.
14419         heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
14420
14421         generation* new_consing_gen = generation_of (max_generation - 1);
14422         generation_allocation_pointer (new_consing_gen) =
14423                 heap_segment_mem (ephemeral_heap_segment);
14424         generation_allocation_limit (new_consing_gen) =
14425             generation_allocation_pointer (new_consing_gen);
14426         generation_allocation_context_start_region (new_consing_gen) = 
14427             generation_allocation_pointer (new_consing_gen);
14428         generation_allocation_segment (new_consing_gen) = ephemeral_heap_segment;
14429
14430         return new_consing_gen;
14431     }
14432     else
14433         return consing_gen;
14434 }
14435
14436 uint8_t* gc_heap::allocate_in_condemned_generations (generation* gen,
14437                                                   size_t size,
14438                                                   int from_gen_number,
14439 #ifdef SHORT_PLUGS
14440                                                   BOOL* convert_to_pinned_p,
14441                                                   uint8_t* next_pinned_plug,
14442                                                   heap_segment* current_seg,
14443 #endif //SHORT_PLUGS
14444                                                   uint8_t* old_loc
14445                                                   REQD_ALIGN_AND_OFFSET_DCL)
14446 {
14447     // Make sure that the youngest generation gap hasn't been allocated
14448     if (settings.promotion)
14449     {
14450         assert (generation_plan_allocation_start (youngest_generation) == 0);
14451     }
14452
14453     size = Align (size);
14454     assert (size >= Align (min_obj_size));
14455     int to_gen_number = from_gen_number;
14456     if (from_gen_number != (int)max_generation)
14457     {
14458         to_gen_number = from_gen_number + (settings.promotion ? 1 : 0);
14459     }
14460
14461     dprintf (3, ("aic gen%d: s: %Id", gen->gen_num, size));
14462
14463     int pad_in_front = ((old_loc != 0) && (to_gen_number != max_generation)) ? USE_PADDING_FRONT : 0;
14464     
14465     if ((from_gen_number != -1) && (from_gen_number != (int)max_generation) && settings.promotion)
14466     {
14467         generation_condemned_allocated (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14468         generation_allocation_size (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14469     }
14470 retry:
14471     {
14472         heap_segment* seg = generation_allocation_segment (gen);
14473         if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14474                            generation_allocation_limit (gen), old_loc,
14475                            ((generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))?USE_PADDING_TAIL:0)|pad_in_front)))
14476         {
14477             if ((! (pinned_plug_que_empty_p()) &&
14478                  (generation_allocation_limit (gen) ==
14479                   pinned_plug (oldest_pin()))))
14480             {
14481                 size_t entry = deque_pinned_plug();
14482                 mark* pinned_plug_entry = pinned_plug_of (entry);
14483                 size_t len = pinned_len (pinned_plug_entry);
14484                 uint8_t* plug = pinned_plug (pinned_plug_entry);
14485                 set_new_pin_info (pinned_plug_entry, generation_allocation_pointer (gen));
14486
14487 #ifdef FREE_USAGE_STATS
14488                 generation_allocated_in_pinned_free (gen) += generation_allocated_since_last_pin (gen);
14489                 dprintf (3, ("allocated %Id so far within pin %Ix, total->%Id", 
14490                     generation_allocated_since_last_pin (gen), 
14491                     plug,
14492                     generation_allocated_in_pinned_free (gen)));
14493                 generation_allocated_since_last_pin (gen) = 0;
14494
14495                 add_item_to_current_pinned_free (gen->gen_num, pinned_len (pinned_plug_of (entry)));
14496 #endif //FREE_USAGE_STATS
14497
14498                 dprintf (3, ("mark stack bos: %Id, tos: %Id, aic: p %Ix len: %Ix->%Ix", 
14499                     mark_stack_bos, mark_stack_tos, plug, len, pinned_len (pinned_plug_of (entry))));
14500
14501                 assert(mark_stack_array[entry].len == 0 ||
14502                        mark_stack_array[entry].len >= Align(min_obj_size));
14503                 generation_allocation_pointer (gen) = plug + len;
14504                 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14505                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14506                 set_allocator_next_pin (gen);
14507
14508                 //Add the size of the pinned plug to the right pinned allocations
14509                 //find out which gen this pinned plug came from 
14510                 int frgn = object_gennum (plug);
14511                 if ((frgn != (int)max_generation) && settings.promotion)
14512                 {
14513                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
14514                     int togn = object_gennum_plan (plug);
14515                     if (frgn < togn)
14516                     {
14517                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
14518                     }
14519                 }
14520                 goto retry;
14521             }
14522             
14523             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
14524             {
14525                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14526                 dprintf (3, ("changed limit to plan alloc: %Ix", generation_allocation_limit (gen)));
14527             }
14528             else
14529             {
14530                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
14531                 {
14532                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14533                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14534                     dprintf (3, ("changed limit to commit: %Ix", generation_allocation_limit (gen)));
14535                 }
14536                 else
14537                 {
14538 #ifndef RESPECT_LARGE_ALIGNMENT
14539                     assert (gen != youngest_generation);
14540 #endif //RESPECT_LARGE_ALIGNMENT
14541
14542                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14543                                     heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
14544                         (grow_heap_segment (seg, generation_allocation_pointer (gen), old_loc,
14545                                             size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG)))
14546                     {
14547                         dprintf (3, ("Expanded segment allocation by committing more memory"));
14548                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14549                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14550                     }
14551                     else
14552                     {
14553                         heap_segment*   next_seg = heap_segment_next (seg);
14554                         assert (generation_allocation_pointer (gen)>=
14555                                 heap_segment_mem (seg));
14556                         // Verify that all pinned plugs for this segment are consumed
14557                         if (!pinned_plug_que_empty_p() &&
14558                             ((pinned_plug (oldest_pin()) <
14559                               heap_segment_allocated (seg)) &&
14560                              (pinned_plug (oldest_pin()) >=
14561                               generation_allocation_pointer (gen))))
14562                         {
14563                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
14564                                          pinned_plug (oldest_pin())));
14565                             FATAL_GC_ERROR();
14566                         }
14567                         assert (generation_allocation_pointer (gen)>=
14568                                 heap_segment_mem (seg));
14569                         assert (generation_allocation_pointer (gen)<=
14570                                 heap_segment_committed (seg));
14571                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
14572
14573                         if (next_seg)
14574                         {
14575                             generation_allocation_segment (gen) = next_seg;
14576                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
14577                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
14578                             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14579                         }
14580                         else
14581                         {
14582                             return 0; //should only happen during allocation of generation 0 gap
14583                             // in that case we are going to grow the heap anyway
14584                         }
14585                     }
14586                 }
14587             }
14588             set_allocator_next_pin (gen);
14589
14590             goto retry;
14591         }
14592     }
14593
14594     {
14595         assert (generation_allocation_pointer (gen)>=
14596                 heap_segment_mem (generation_allocation_segment (gen)));
14597         uint8_t* result = generation_allocation_pointer (gen);
14598         size_t pad = 0;
14599 #ifdef SHORT_PLUGS
14600         if ((pad_in_front & USE_PADDING_FRONT) &&
14601             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14602              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14603         {
14604             ptrdiff_t dist = old_loc - result;
14605             if (dist == 0)
14606             {
14607                 dprintf (3, ("old alloc: %Ix, same as new alloc, not padding", old_loc));
14608                 pad = 0;
14609             }
14610             else
14611             {
14612                 if ((dist > 0) && (dist < (ptrdiff_t)Align (min_obj_size)))
14613                 {
14614                     dprintf (3, ("old alloc: %Ix, only %d bytes > new alloc! Shouldn't happen", old_loc, dist));
14615                     FATAL_GC_ERROR();
14616                 }
14617
14618                 pad = Align (min_obj_size);
14619                 set_plug_padded (old_loc);
14620             }
14621         }
14622 #endif //SHORT_PLUGS
14623 #ifdef FEATURE_STRUCTALIGN
14624         _ASSERTE(!old_loc || alignmentOffset != 0);
14625         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14626         if ((old_loc != 0))
14627         {
14628             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14629             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14630             pad += pad1;
14631         }
14632 #else // FEATURE_STRUCTALIGN
14633         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14634         {
14635             pad += switch_alignment_size (is_plug_padded (old_loc));
14636             set_node_realigned(old_loc);
14637             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14638                          (size_t)old_loc, (size_t)(result+pad)));
14639             assert (same_large_alignment_p (result + pad, old_loc));
14640         }
14641 #endif // FEATURE_STRUCTALIGN
14642
14643 #ifdef SHORT_PLUGS
14644         if ((next_pinned_plug != 0) && (pad != 0) && (generation_allocation_segment (gen) == current_seg))
14645         {
14646             assert (old_loc != 0);
14647             ptrdiff_t dist_to_next_pin = (ptrdiff_t)(next_pinned_plug - (generation_allocation_pointer (gen) + size + pad));
14648             assert (dist_to_next_pin >= 0);
14649
14650             if ((dist_to_next_pin >= 0) && (dist_to_next_pin < (ptrdiff_t)Align (min_obj_size)))
14651             {
14652                 dprintf (3, ("%Ix->(%Ix,%Ix),%Ix(%Ix)(%Ix),NP->PP", 
14653                     old_loc, 
14654                     generation_allocation_pointer (gen),
14655                     generation_allocation_limit (gen),
14656                     next_pinned_plug,
14657                     size, 
14658                     dist_to_next_pin));
14659                 clear_plug_padded (old_loc);
14660                 pad = 0;
14661                 *convert_to_pinned_p = TRUE;
14662                 record_interesting_data_point (idp_converted_pin);
14663
14664                 return 0;
14665             }
14666         }
14667 #endif //SHORT_PLUGS
14668
14669         if ((old_loc == 0) || (pad != 0))
14670         {
14671             //allocating a non plug or a gap, so reset the start region
14672             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14673         }
14674
14675         generation_allocation_pointer (gen) += size + pad;
14676         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14677
14678 #ifdef FREE_USAGE_STATS
14679         generation_allocated_since_last_pin (gen) += size;
14680 #endif //FREE_USAGE_STATS
14681
14682         dprintf (3, ("aic: ptr: %Ix, limit: %Ix, sr: %Ix", 
14683             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14684             generation_allocation_context_start_region (gen)));
14685
14686         assert (result + pad);
14687         return result + pad;
14688     }
14689 }
14690
14691 inline int power (int x, int y)
14692 {
14693     int z = 1;
14694     for (int i = 0; i < y; i++)
14695     {
14696         z = z*x;
14697     }
14698     return z;
14699 }
14700
14701 int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation, 
14702                                            int initial_gen,
14703                                            int current_gen,
14704                                            BOOL* blocking_collection_p
14705                                            STRESS_HEAP_ARG(int n_original))
14706 {
14707     int n = current_gen;
14708 #ifdef MULTIPLE_HEAPS
14709     BOOL joined_last_gc_before_oom = FALSE;
14710     for (int i = 0; i < n_heaps; i++)
14711     {
14712         if (g_heaps[i]->last_gc_before_oom)
14713         {
14714             dprintf (GTC_LOG, ("h%d is setting blocking to TRUE", i));
14715             joined_last_gc_before_oom = TRUE;
14716             break;
14717         }
14718     }
14719 #else
14720     BOOL joined_last_gc_before_oom = last_gc_before_oom;
14721 #endif //MULTIPLE_HEAPS
14722
14723     if (joined_last_gc_before_oom && settings.pause_mode != pause_low_latency)
14724     {
14725         assert (*blocking_collection_p);
14726     }
14727
14728     if (should_evaluate_elevation && (n == max_generation))
14729     {
14730         dprintf (GTC_LOG, ("lock: %d(%d)", 
14731             (settings.should_lock_elevation ? 1 : 0), 
14732             settings.elevation_locked_count));
14733
14734         if (settings.should_lock_elevation)
14735         {
14736             settings.elevation_locked_count++;
14737             if (settings.elevation_locked_count == 6)
14738             {
14739                 settings.elevation_locked_count = 0;
14740             }
14741             else
14742             {
14743                 n = max_generation - 1;
14744                 settings.elevation_reduced = TRUE;
14745             }
14746         }
14747         else
14748         {
14749             settings.elevation_locked_count = 0;
14750         }
14751     }
14752     else
14753     {
14754         settings.should_lock_elevation = FALSE;
14755         settings.elevation_locked_count = 0;
14756     }
14757
14758     if (provisional_mode_triggered && (n == max_generation))
14759     {
14760         // There are a few cases where we should not reduce the generation.
14761         if ((initial_gen == max_generation) || (settings.reason == reason_alloc_loh))
14762         {
14763             // If we are doing a full GC in the provisional mode, we always
14764             // make it blocking because we don't want to get into a situation
14765             // where foreground GCs are asking for a compacting full GC right away
14766             // and not getting it.
14767             dprintf (GTC_LOG, ("full GC induced, not reducing gen"));
14768             *blocking_collection_p = TRUE;
14769         }
14770         else if (should_expand_in_full_gc || joined_last_gc_before_oom)
14771         {
14772             dprintf (GTC_LOG, ("need full blocking GCs to expand heap or avoid OOM, not reducing gen"));
14773             assert (*blocking_collection_p);
14774         }
14775         else
14776         {
14777             dprintf (GTC_LOG, ("reducing gen in PM: %d->%d->%d", initial_gen, n, (max_generation - 1)));
14778             n = max_generation - 1;
14779         }
14780     }
14781
14782     if (should_expand_in_full_gc)
14783     {
14784         should_expand_in_full_gc = FALSE;
14785     }
14786
14787     if (heap_hard_limit)
14788     {
14789         // If we have already consumed 90% of the limit, we should check to see if we should compact LOH.
14790         // TODO: should unify this with gen2.
14791         dprintf (GTC_LOG, ("committed %Id is %d%% of limit %Id", 
14792             current_total_committed, (int)((float)current_total_committed * 100.0 / (float)heap_hard_limit),
14793             heap_hard_limit));
14794         if ((current_total_committed * 10) >= (heap_hard_limit * 9))
14795         {
14796             bool full_compact_gc_p = false;
14797
14798             size_t loh_frag = get_total_gen_fragmentation (max_generation + 1);
14799             
14800             // If the LOH frag is >= 1/8 it's worth compacting it
14801             if ((loh_frag * 8) >= heap_hard_limit)
14802             {
14803                 dprintf (GTC_LOG, ("loh frag: %Id > 1/8 of limit %Id", loh_frag, (heap_hard_limit / 8)));
14804                 full_compact_gc_p = true;
14805             }
14806             else
14807             {
14808                 // If there's not much fragmentation but it looks like it'll be productive to
14809                 // collect LOH, do that.
14810                 size_t est_loh_reclaim = get_total_gen_estimated_reclaim (max_generation + 1);
14811                 full_compact_gc_p = ((est_loh_reclaim * 8) >= heap_hard_limit);
14812                 dprintf (GTC_LOG, ("loh est reclaim: %Id, 1/8 of limit %Id", est_loh_reclaim, (heap_hard_limit / 8)));
14813             }
14814
14815             if (full_compact_gc_p)
14816             {
14817                 n = max_generation;
14818                 *blocking_collection_p = TRUE;
14819                 settings.loh_compaction = TRUE;
14820                 dprintf (GTC_LOG, ("compacting LOH due to hard limit"));
14821             }
14822         }
14823     }
14824
14825     if ((n == max_generation) && (*blocking_collection_p == FALSE))
14826     {
14827         // If we are doing a gen2 we should reset elevation regardless and let the gen2
14828         // decide if we should lock again or in the bgc case by design we will not retract
14829         // gen1 start.
14830         settings.should_lock_elevation = FALSE;
14831         settings.elevation_locked_count = 0;
14832         dprintf (1, ("doing bgc, reset elevation"));
14833     }
14834
14835 #ifdef STRESS_HEAP
14836 #ifdef BACKGROUND_GC
14837     // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
14838     // generations to be collected,
14839     //
14840     // [LOCALGC TODO] STRESS_HEAP is not defined for a standalone GC so there are multiple
14841     // things that need to be fixed in this code block.
14842     if (n_original != max_generation &&
14843         g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
14844     {
14845 #ifndef FEATURE_REDHAWK
14846         // for the GC stress mix mode throttle down gen2 collections
14847         if (g_pConfig->IsGCStressMix())
14848         {
14849             size_t current_gc_count = 0;
14850
14851 #ifdef MULTIPLE_HEAPS
14852             current_gc_count = (size_t)dd_collection_count (g_heaps[0]->dynamic_data_of (0));
14853 #else
14854             current_gc_count = (size_t)dd_collection_count (dynamic_data_of (0));
14855 #endif //MULTIPLE_HEAPS
14856             // in gc stress, only escalate every 10th non-gen2 collection to a gen2...
14857             if ((current_gc_count % 10) == 0)
14858             {
14859                 n = max_generation;
14860             }
14861         }
14862         // for traditional GC stress
14863         else
14864 #endif // !FEATURE_REDHAWK
14865         if (*blocking_collection_p)
14866         {
14867             // We call StressHeap() a lot for Concurrent GC Stress. However,
14868             // if we can not do a concurrent collection, no need to stress anymore.
14869             // @TODO: Enable stress when the memory pressure goes down again
14870             GCStressPolicy::GlobalDisable();
14871         }
14872         else
14873         {
14874             n = max_generation;
14875         }
14876     }
14877 #endif //BACKGROUND_GC
14878 #endif //STRESS_HEAP
14879
14880     return n;
14881 }
14882
14883 inline
14884 size_t get_survived_size (gc_history_per_heap* hist)
14885 {
14886     size_t surv_size = 0;
14887     gc_generation_data* gen_data;
14888
14889     for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
14890     {
14891         gen_data = &(hist->gen_data[gen_number]); 
14892         surv_size += (gen_data->size_after - 
14893                       gen_data->free_list_space_after - 
14894                       gen_data->free_obj_space_after);
14895     }
14896
14897     return surv_size;
14898 }
14899
14900 size_t gc_heap::get_total_survived_size()
14901 {
14902     size_t total_surv_size = 0;
14903 #ifdef MULTIPLE_HEAPS
14904     for (int i = 0; i < gc_heap::n_heaps; i++)
14905     {
14906         gc_heap* hp = gc_heap::g_heaps[i];
14907         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
14908         total_surv_size += get_survived_size (current_gc_data_per_heap);
14909     }
14910 #else
14911     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
14912     total_surv_size = get_survived_size (current_gc_data_per_heap);
14913 #endif //MULTIPLE_HEAPS
14914     return total_surv_size;
14915 }
14916
14917 size_t gc_heap::get_total_allocated_since_last_gc()
14918 {
14919     size_t total_allocated_size = 0;
14920 #ifdef MULTIPLE_HEAPS
14921     for (int i = 0; i < gc_heap::n_heaps; i++)
14922     {
14923         gc_heap* hp = gc_heap::g_heaps[i];
14924         total_allocated_size += hp->allocated_since_last_gc;
14925         hp->allocated_since_last_gc = 0;
14926     }
14927 #else
14928     total_allocated_size = allocated_since_last_gc;
14929     allocated_since_last_gc = 0;
14930 #endif //MULTIPLE_HEAPS
14931     return total_allocated_size;
14932 }
14933
14934 // Gets what's allocated on both SOH and LOH that hasn't been collected.
14935 size_t gc_heap::get_current_allocated()
14936 {
14937     dynamic_data* dd = dynamic_data_of (0);
14938     size_t current_alloc = dd_desired_allocation (dd) - dd_new_allocation (dd);
14939     dd = dynamic_data_of (max_generation + 1);
14940     current_alloc += dd_desired_allocation (dd) - dd_new_allocation (dd);
14941
14942     return current_alloc;
14943 }
14944
14945 size_t gc_heap::get_total_allocated()
14946 {
14947     size_t total_current_allocated = 0;
14948 #ifdef MULTIPLE_HEAPS
14949     for (int i = 0; i < gc_heap::n_heaps; i++)
14950     {
14951         gc_heap* hp = gc_heap::g_heaps[i];
14952         total_current_allocated += hp->get_current_allocated();
14953     }
14954 #else
14955     total_current_allocated = get_current_allocated();
14956 #endif //MULTIPLE_HEAPS
14957     return total_current_allocated;
14958 }
14959
14960 size_t gc_heap::current_generation_size (int gen_number)
14961 {
14962     dynamic_data* dd = dynamic_data_of (gen_number);
14963     size_t gen_size = (dd_current_size (dd) + dd_desired_allocation (dd)
14964                         - dd_new_allocation (dd));
14965
14966     return gen_size;
14967 }
14968
14969 #ifdef _PREFAST_
14970 #pragma warning(push)
14971 #pragma warning(disable:6326) // "Potential comparison of a constant with another constant" is intentional in this function.
14972 #endif //_PREFAST_
14973
14974 /*
14975     This is called by when we are actually doing a GC, or when we are just checking whether
14976     we would do a full blocking GC, in which case check_only_p is TRUE.
14977
14978     The difference between calling this with check_only_p TRUE and FALSE is that when it's
14979     TRUE: 
14980             settings.reason is ignored
14981             budgets are not checked (since they are checked before this is called)
14982             it doesn't change anything non local like generation_skip_ratio
14983 */
14984 int gc_heap::generation_to_condemn (int n_initial, 
14985                                     BOOL* blocking_collection_p, 
14986                                     BOOL* elevation_requested_p,
14987                                     BOOL check_only_p)
14988 {
14989     gc_mechanisms temp_settings = settings;
14990     gen_to_condemn_tuning temp_condemn_reasons;
14991     gc_mechanisms* local_settings = (check_only_p ? &temp_settings : &settings);
14992     gen_to_condemn_tuning* local_condemn_reasons = (check_only_p ? &temp_condemn_reasons : &gen_to_condemn_reasons);
14993     if (!check_only_p)
14994     {
14995         if ((local_settings->reason == reason_oos_soh) || (local_settings->reason == reason_oos_loh))
14996         {
14997             assert (n_initial >= 1);
14998         }
14999
15000         assert (settings.reason != reason_empty);
15001     }
15002
15003     local_condemn_reasons->init();
15004
15005     int n = n_initial;
15006     int n_alloc = n;
15007     if (heap_number == 0)
15008     {
15009         dprintf (GTC_LOG, ("init: %d(%d)", n_initial, settings.reason));
15010     }
15011     int i = 0;
15012     int temp_gen = 0;
15013     BOOL low_memory_detected = g_low_memory_status;
15014     uint32_t memory_load = 0;
15015     uint64_t available_physical = 0;
15016     uint64_t available_page_file = 0;
15017     BOOL check_memory = FALSE;
15018     BOOL high_fragmentation  = FALSE;
15019     BOOL v_high_memory_load  = FALSE;
15020     BOOL high_memory_load    = FALSE;
15021     BOOL low_ephemeral_space = FALSE;
15022     BOOL evaluate_elevation  = TRUE;
15023     *elevation_requested_p   = FALSE;
15024     *blocking_collection_p   = FALSE;
15025
15026     BOOL check_max_gen_alloc = TRUE;
15027
15028 #ifdef STRESS_HEAP
15029     int orig_gen = n;
15030 #endif //STRESS_HEAP
15031
15032     if (!check_only_p)
15033     {
15034         dd_fragmentation (dynamic_data_of (0)) = 
15035             generation_free_list_space (youngest_generation) + 
15036             generation_free_obj_space (youngest_generation);
15037
15038         dd_fragmentation (dynamic_data_of (max_generation + 1)) = 
15039             generation_free_list_space (large_object_generation) + 
15040             generation_free_obj_space (large_object_generation);
15041
15042         //save new_allocation
15043         for (i = 0; i <= max_generation+1; i++)
15044         {
15045             dynamic_data* dd = dynamic_data_of (i);
15046             dprintf (GTC_LOG, ("h%d: g%d: l: %Id (%Id)", 
15047                             heap_number, i,
15048                             dd_new_allocation (dd),
15049                             dd_desired_allocation (dd)));
15050             dd_gc_new_allocation (dd) = dd_new_allocation (dd);
15051         }
15052
15053         local_condemn_reasons->set_gen (gen_initial, n);
15054         temp_gen = n;
15055
15056 #ifdef BACKGROUND_GC
15057         if (recursive_gc_sync::background_running_p())
15058         {
15059             dprintf (GTC_LOG, ("bgc in prog, 1"));
15060             check_max_gen_alloc = FALSE;
15061         }
15062 #endif //BACKGROUND_GC
15063
15064         if (check_max_gen_alloc)
15065         {
15066             //figure out if large objects need to be collected.
15067             if (get_new_allocation (max_generation+1) <= 0)
15068             {
15069                 n = max_generation;
15070                 local_condemn_reasons->set_gen (gen_alloc_budget, n);
15071             }
15072         }
15073
15074         //figure out which generation ran out of allocation
15075         for (i = n+1; i <= (check_max_gen_alloc ? max_generation : (max_generation - 1)); i++)
15076         {
15077             if (get_new_allocation (i) <= 0)
15078             {
15079                 n = i;
15080             }
15081             else
15082                 break;
15083         }
15084     }
15085
15086     if (n > temp_gen)
15087     {
15088         local_condemn_reasons->set_gen (gen_alloc_budget, n);
15089     }
15090
15091     dprintf (GTC_LOG, ("h%d: g%d budget", heap_number, ((get_new_allocation (max_generation+1) <= 0) ? 3 : n)));
15092
15093     n_alloc = n;
15094
15095 #if defined(BACKGROUND_GC) && !defined(MULTIPLE_HEAPS)
15096     //time based tuning
15097     // if enough time has elapsed since the last gc
15098     // and the number of gc is too low (1/10 of lower gen) then collect
15099     // This should also be enabled if we have memory concerns
15100     int n_time_max = max_generation;
15101
15102     if (!check_only_p)
15103     {
15104         if (recursive_gc_sync::background_running_p())
15105         {
15106             n_time_max = max_generation - 1;
15107         }
15108     }
15109
15110     if ((local_settings->pause_mode == pause_interactive) ||
15111         (local_settings->pause_mode == pause_sustained_low_latency))
15112     {
15113         dynamic_data* dd0 = dynamic_data_of (0);
15114         size_t now = GetHighPrecisionTimeStamp();
15115         temp_gen = n;
15116         for (i = (temp_gen+1); i <= n_time_max; i++)
15117         {
15118             dynamic_data* dd = dynamic_data_of (i);
15119             if ((now > dd_time_clock(dd) + dd_time_clock_interval(dd)) &&
15120                 (dd_gc_clock (dd0) > (dd_gc_clock (dd) + dd_gc_clock_interval(dd))) &&
15121                 ((n < max_generation) || ((dd_current_size (dd) < dd_max_size (dd0)))))
15122             {
15123                 n = min (i, n_time_max);
15124                 dprintf (GTC_LOG, ("time %d", n));
15125             }
15126         }
15127         if (n > temp_gen)
15128         {
15129             local_condemn_reasons->set_gen (gen_time_tuning, n);
15130         }
15131     }
15132
15133     if (n != n_alloc)
15134     {
15135         dprintf (GTC_LOG, ("Condemning %d based on time tuning and fragmentation", n));
15136     }
15137 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
15138
15139     if (n < (max_generation - 1))
15140     {
15141         if (dt_low_card_table_efficiency_p (tuning_deciding_condemned_gen))
15142         {
15143             n = max (n, max_generation - 1);
15144             local_settings->promotion = TRUE;
15145             dprintf (GTC_LOG, ("h%d: skip %d, c %d",
15146                         heap_number, generation_skip_ratio, n));
15147             local_condemn_reasons->set_condition (gen_low_card_p);
15148         }
15149     }
15150
15151     if (!check_only_p)
15152     {
15153         generation_skip_ratio = 100;
15154     }
15155
15156     if (dt_low_ephemeral_space_p (check_only_p ? 
15157                                   tuning_deciding_full_gc : 
15158                                   tuning_deciding_condemned_gen))
15159     {
15160         low_ephemeral_space = TRUE;
15161
15162         n = max (n, max_generation - 1);
15163         local_condemn_reasons->set_condition (gen_low_ephemeral_p);
15164         dprintf (GTC_LOG, ("h%d: low eph", heap_number));
15165
15166         if (!provisional_mode_triggered)
15167         {
15168 #ifdef BACKGROUND_GC
15169             if (!gc_can_use_concurrent || (generation_free_list_space (generation_of (max_generation)) == 0))
15170 #endif //BACKGROUND_GC
15171             {
15172                 //It is better to defragment first if we are running out of space for
15173                 //the ephemeral generation but we have enough fragmentation to make up for it
15174                 //in the non ephemeral generation. Essentially we are trading a gen2 for 
15175                 // having to expand heap in ephemeral collections.
15176                 if (dt_high_frag_p (tuning_deciding_condemned_gen, 
15177                                     max_generation - 1, 
15178                                     TRUE))
15179                 {
15180                     high_fragmentation = TRUE;
15181                     local_condemn_reasons->set_condition (gen_max_high_frag_e_p);
15182                     dprintf (GTC_LOG, ("heap%d: gen1 frag", heap_number));
15183                 }
15184             }
15185         }
15186     }
15187
15188     //figure out which ephemeral generation is too fragramented
15189     temp_gen = n;
15190     for (i = n+1; i < max_generation; i++)
15191     {
15192         if (dt_high_frag_p (tuning_deciding_condemned_gen, i))
15193         {
15194             dprintf (GTC_LOG, ("h%d g%d too frag", heap_number, i));
15195             n = i;
15196         }
15197         else
15198             break;
15199     }
15200
15201     if (low_ephemeral_space)
15202     {
15203         //enable promotion
15204         local_settings->promotion = TRUE;
15205     }
15206
15207     if (n > temp_gen)
15208     {
15209         local_condemn_reasons->set_condition (gen_eph_high_frag_p);
15210     }
15211
15212     if (!check_only_p)
15213     {
15214         if (settings.pause_mode == pause_low_latency)
15215         {
15216             if (!is_induced (settings.reason))
15217             {
15218                 n = min (n, max_generation - 1);
15219                 dprintf (GTC_LOG, ("low latency mode is enabled, condemning %d", n));
15220                 evaluate_elevation = FALSE;
15221                 goto exit;
15222             }
15223         }
15224     }
15225
15226     // It's hard to catch when we get to the point that the memory load is so high
15227     // we get an induced GC from the finalizer thread so we are checking the memory load
15228     // for every gen0 GC.
15229     check_memory = (check_only_p ? 
15230                     (n >= 0) : 
15231                     ((n >= 1) || low_memory_detected));
15232
15233     if (check_memory)
15234     {
15235         //find out if we are short on memory
15236         get_memory_info (&memory_load, &available_physical, &available_page_file);
15237         if (heap_number == 0)
15238         {
15239             dprintf (GTC_LOG, ("ml: %d", memory_load));
15240         }
15241         
15242         // Need to get it early enough for all heaps to use.
15243         entry_available_physical_mem = available_physical;
15244         local_settings->entry_memory_load = memory_load;
15245
15246         // @TODO: Force compaction more often under GCSTRESS
15247         if (memory_load >= high_memory_load_th || low_memory_detected)
15248         {
15249 #ifdef SIMPLE_DPRINTF
15250             // stress log can't handle any parameter that's bigger than a void*.
15251             if (heap_number == 0)
15252             {
15253                 dprintf (GTC_LOG, ("tp: %I64d, ap: %I64d", total_physical_mem, available_physical));
15254             }
15255 #endif //SIMPLE_DPRINTF
15256
15257             high_memory_load = TRUE;
15258
15259             if (memory_load >= v_high_memory_load_th || low_memory_detected)
15260             {
15261                 // TODO: Perhaps in 64-bit we should be estimating gen1's fragmentation as well since
15262                 // gen1/gen0 may take a lot more memory than gen2.
15263                 if (!high_fragmentation)
15264                 {
15265                     high_fragmentation = dt_estimate_reclaim_space_p (tuning_deciding_condemned_gen, max_generation);
15266                 }
15267                 v_high_memory_load = TRUE;
15268             }
15269             else
15270             {
15271                 if (!high_fragmentation)
15272                 {
15273                     high_fragmentation = dt_estimate_high_frag_p (tuning_deciding_condemned_gen, max_generation, available_physical);
15274                 }
15275             }
15276
15277             if (high_fragmentation)
15278             {
15279                 if (high_memory_load)
15280                 {
15281                     local_condemn_reasons->set_condition (gen_max_high_frag_m_p);
15282                 }
15283                 else if (v_high_memory_load)
15284                 {
15285                     local_condemn_reasons->set_condition (gen_max_high_frag_vm_p);
15286                 }
15287             }
15288         }
15289     }
15290
15291     dprintf (GTC_LOG, ("h%d: le: %d, hm: %d, vm: %d, f: %d",
15292                  heap_number, low_ephemeral_space, high_memory_load, v_high_memory_load,
15293                  high_fragmentation));
15294
15295     if (should_expand_in_full_gc)
15296     {
15297         dprintf (GTC_LOG, ("h%d: expand_in_full - BLOCK", heap_number));
15298         *blocking_collection_p = TRUE;
15299         evaluate_elevation = FALSE;
15300         n = max_generation;
15301         local_condemn_reasons->set_condition (gen_expand_fullgc_p);
15302     }
15303
15304     if (last_gc_before_oom)
15305     {
15306         dprintf (GTC_LOG, ("h%d: alloc full - BLOCK", heap_number));
15307         n = max_generation;
15308         *blocking_collection_p = TRUE;
15309
15310         if ((local_settings->reason == reason_oos_loh) ||
15311             (local_settings->reason == reason_alloc_loh))
15312         {
15313             evaluate_elevation = FALSE;
15314         }
15315
15316         local_condemn_reasons->set_condition (gen_before_oom);
15317     }
15318
15319     if (!check_only_p)
15320     {
15321         if (is_induced_blocking (settings.reason) && 
15322             n_initial == max_generation
15323             IN_STRESS_HEAP( && !settings.stress_induced ))
15324         {
15325             if (heap_number == 0)
15326             {
15327                 dprintf (GTC_LOG, ("induced - BLOCK"));
15328             }
15329
15330             *blocking_collection_p = TRUE;
15331             local_condemn_reasons->set_condition (gen_induced_fullgc_p);
15332             evaluate_elevation = FALSE;
15333         }
15334
15335         if (settings.reason == reason_induced_noforce)
15336         {
15337             local_condemn_reasons->set_condition (gen_induced_noforce_p);
15338             evaluate_elevation = FALSE;
15339         }
15340     }
15341
15342     if (!provisional_mode_triggered && evaluate_elevation && (low_ephemeral_space || high_memory_load || v_high_memory_load))
15343     {
15344         *elevation_requested_p = TRUE;
15345 #ifdef BIT64
15346         // if we are in high memory load and have consumed 10% of the gen2 budget, do a gen2 now.
15347         if (high_memory_load || v_high_memory_load)
15348         {
15349             dynamic_data* dd_max = dynamic_data_of (max_generation);
15350             if (((float)dd_new_allocation (dd_max) / (float)dd_desired_allocation (dd_max)) < 0.9)
15351             {
15352                 dprintf (GTC_LOG, ("%Id left in gen2 alloc (%Id)", 
15353                     dd_new_allocation (dd_max), dd_desired_allocation (dd_max)));
15354                 n = max_generation;
15355                 local_condemn_reasons->set_condition (gen_almost_max_alloc);
15356             }
15357         }
15358
15359         if (n <= max_generation)
15360         {
15361 #endif // BIT64
15362             if (high_fragmentation)
15363             {
15364                 //elevate to max_generation
15365                 n = max_generation;
15366                 dprintf (GTC_LOG, ("h%d: f full", heap_number));
15367
15368 #ifdef BACKGROUND_GC
15369                 if (high_memory_load || v_high_memory_load)
15370                 {
15371                     // For background GC we want to do blocking collections more eagerly because we don't
15372                     // want to get into the situation where the memory load becomes high while we are in
15373                     // a background GC and we'd have to wait for the background GC to finish to start
15374                     // a blocking collection (right now the implemenation doesn't handle converting 
15375                     // a background GC to a blocking collection midway.
15376                     dprintf (GTC_LOG, ("h%d: bgc - BLOCK", heap_number));
15377                     *blocking_collection_p = TRUE;
15378                 }
15379 #else
15380                 if (v_high_memory_load)
15381                 {
15382                     dprintf (GTC_LOG, ("h%d: - BLOCK", heap_number));
15383                     *blocking_collection_p = TRUE;
15384                 }
15385 #endif //BACKGROUND_GC
15386             }
15387             else
15388             {
15389                 n = max (n, max_generation - 1);
15390                 dprintf (GTC_LOG, ("h%d: nf c %d", heap_number, n));
15391             }
15392 #ifdef BIT64
15393         }
15394 #endif // BIT64
15395     }
15396
15397     if (!provisional_mode_triggered && (n == (max_generation - 1)) && (n_alloc < (max_generation -1)))
15398     {
15399         dprintf (GTC_LOG, ("h%d: budget %d, check 2",
15400                       heap_number, n_alloc));
15401         if (get_new_allocation (max_generation) <= 0)
15402         {
15403             dprintf (GTC_LOG, ("h%d: budget alloc", heap_number));
15404             n = max_generation;
15405             local_condemn_reasons->set_condition (gen_max_gen1);
15406         }
15407     }
15408
15409     //figure out if max_generation is too fragmented -> blocking collection
15410     if (!provisional_mode_triggered && (n == max_generation))
15411     {
15412         if (dt_high_frag_p (tuning_deciding_condemned_gen, n))
15413         {
15414             dprintf (GTC_LOG, ("h%d: g%d too frag", heap_number, n));
15415             local_condemn_reasons->set_condition (gen_max_high_frag_p);
15416             if (local_settings->pause_mode != pause_sustained_low_latency)
15417             {
15418                 *blocking_collection_p = TRUE;
15419             }
15420         }
15421     }
15422
15423 #ifdef BACKGROUND_GC
15424     if (n == max_generation)
15425     {
15426         if (heap_number == 0)
15427         {
15428             BOOL bgc_heap_too_small = TRUE;
15429             size_t gen2size = 0;
15430             size_t gen3size = 0;
15431 #ifdef MULTIPLE_HEAPS
15432             for (int i = 0; i < n_heaps; i++)
15433             {
15434                 if (((g_heaps[i]->current_generation_size (max_generation)) > bgc_min_per_heap) || 
15435                     ((g_heaps[i]->current_generation_size (max_generation + 1)) > bgc_min_per_heap))
15436                 {
15437                     bgc_heap_too_small = FALSE;
15438                     break;
15439                 }
15440             }
15441 #else //MULTIPLE_HEAPS
15442             if ((current_generation_size (max_generation) > bgc_min_per_heap) || 
15443                 (current_generation_size (max_generation + 1) > bgc_min_per_heap))
15444             {
15445                 bgc_heap_too_small = FALSE;
15446             }            
15447 #endif //MULTIPLE_HEAPS
15448
15449             if (bgc_heap_too_small)
15450             {
15451                 dprintf (GTC_LOG, ("gen2 and gen3 too small"));
15452
15453 #ifdef STRESS_HEAP
15454                 // do not turn stress-induced collections into blocking GCs
15455                 if (!settings.stress_induced)
15456 #endif //STRESS_HEAP
15457                 {
15458                     *blocking_collection_p = TRUE;
15459                 }
15460
15461                 local_condemn_reasons->set_condition (gen_gen2_too_small);
15462             }
15463         }
15464     }
15465 #endif //BACKGROUND_GC
15466
15467 exit:
15468     if (!check_only_p)
15469     {
15470 #ifdef STRESS_HEAP
15471 #ifdef BACKGROUND_GC
15472         // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
15473         // generations to be collected,
15474
15475         if (orig_gen != max_generation &&
15476             g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
15477         {
15478             *elevation_requested_p = FALSE;
15479         }
15480 #endif //BACKGROUND_GC
15481 #endif //STRESS_HEAP
15482
15483         if (check_memory)
15484         {
15485             fgm_result.available_pagefile_mb = (size_t)(available_page_file / (1024 * 1024));
15486         }
15487
15488         local_condemn_reasons->set_gen (gen_final_per_heap, n);
15489         get_gc_data_per_heap()->gen_to_condemn_reasons.init (local_condemn_reasons);
15490
15491 #ifdef DT_LOG
15492         local_condemn_reasons->print (heap_number);
15493 #endif //DT_LOG
15494
15495         if ((local_settings->reason == reason_oos_soh) || 
15496             (local_settings->reason == reason_oos_loh))
15497         {
15498             assert (n >= 1);
15499         }
15500     }
15501
15502     return n;
15503 }
15504
15505 #ifdef _PREFAST_
15506 #pragma warning(pop)
15507 #endif //_PREFAST_
15508
15509 inline
15510 size_t gc_heap::min_reclaim_fragmentation_threshold (uint32_t num_heaps)
15511 {
15512     // if the memory load is higher, the threshold we'd want to collect gets lower.
15513     size_t min_mem_based_on_available = 
15514         (500 - (settings.entry_memory_load - high_memory_load_th) * 40) * 1024 * 1024 / num_heaps;
15515
15516     size_t ten_percent_size = (size_t)((float)generation_size (max_generation) * 0.10);
15517     uint64_t three_percent_mem = mem_one_percent * 3 / num_heaps;
15518
15519 #ifdef SIMPLE_DPRINTF
15520     dprintf (GTC_LOG, ("min av: %Id, 10%% gen2: %Id, 3%% mem: %I64d", 
15521         min_mem_based_on_available, ten_percent_size, three_percent_mem));
15522 #endif //SIMPLE_DPRINTF
15523     return (size_t)(min (min_mem_based_on_available, min (ten_percent_size, three_percent_mem)));
15524 }
15525
15526 inline
15527 uint64_t gc_heap::min_high_fragmentation_threshold(uint64_t available_mem, uint32_t num_heaps)
15528 {
15529     return min (available_mem, (256*1024*1024)) / num_heaps;
15530 }
15531
15532 enum {
15533 CORINFO_EXCEPTION_GC = 0xE0004743 // 'GC'
15534 };
15535
15536
15537 #ifdef BACKGROUND_GC
15538 void gc_heap::init_background_gc ()
15539 {
15540     //reset the allocation so foreground gc can allocate into older (max_generation) generation
15541     generation* gen = generation_of (max_generation);
15542     generation_allocation_pointer (gen)= 0;
15543     generation_allocation_limit (gen) = 0;
15544     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
15545
15546     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
15547
15548     //reset the plan allocation for each segment
15549     for (heap_segment* seg = generation_allocation_segment (gen); seg != ephemeral_heap_segment;
15550         seg = heap_segment_next_rw (seg))
15551     {
15552         heap_segment_plan_allocated (seg) = heap_segment_allocated (seg);
15553     }
15554
15555     if (heap_number == 0)
15556     {
15557         dprintf (2, ("heap%d: bgc lowest: %Ix, highest: %Ix", 
15558             heap_number,
15559             background_saved_lowest_address, 
15560             background_saved_highest_address));
15561     }
15562
15563     gc_lh_block_event.Reset();
15564 }
15565
15566 #endif //BACKGROUND_GC
15567
15568 inline
15569 void fire_drain_mark_list_event (size_t mark_list_objects)
15570 {
15571     FIRE_EVENT(BGCDrainMark, mark_list_objects);
15572 }
15573
15574 inline
15575 void fire_revisit_event (size_t dirtied_pages, 
15576                          size_t marked_objects,
15577                          BOOL large_objects_p)
15578 {
15579     FIRE_EVENT(BGCRevisit, dirtied_pages, marked_objects, large_objects_p);
15580 }
15581
15582 inline
15583 void fire_overflow_event (uint8_t* overflow_min,
15584                           uint8_t* overflow_max,
15585                           size_t marked_objects, 
15586                           int large_objects_p)
15587 {
15588     FIRE_EVENT(BGCOverflow, (uint64_t)overflow_min, (uint64_t)overflow_max, marked_objects, large_objects_p);
15589 }
15590
15591 void gc_heap::concurrent_print_time_delta (const char* msg)
15592 {
15593 #ifdef TRACE_GC
15594     size_t current_time = GetHighPrecisionTimeStamp();
15595     size_t elapsed_time = current_time - time_bgc_last;
15596     time_bgc_last = current_time;
15597
15598     dprintf (2, ("h%d: %s T %Id ms", heap_number, msg, elapsed_time));
15599 #else
15600     UNREFERENCED_PARAMETER(msg);
15601 #endif //TRACE_GC
15602 }
15603
15604 void gc_heap::free_list_info (int gen_num, const char* msg)
15605 {
15606     UNREFERENCED_PARAMETER(gen_num);
15607 #if defined (BACKGROUND_GC) && defined (TRACE_GC)
15608     dprintf (3, ("h%d: %s", heap_number, msg));
15609     for (int i = 0; i <= (max_generation + 1); i++)
15610     {
15611         generation* gen = generation_of (i);
15612         if ((generation_allocation_size (gen) == 0) && 
15613             (generation_free_list_space (gen) == 0) && 
15614             (generation_free_obj_space (gen) == 0))
15615         {
15616             // don't print if everything is 0.
15617         }
15618         else
15619         {
15620             dprintf (3, ("h%d: g%d: a-%Id, fl-%Id, fo-%Id",
15621                 heap_number, i, 
15622                 generation_allocation_size (gen), 
15623                 generation_free_list_space (gen), 
15624                 generation_free_obj_space (gen)));
15625         }
15626     }
15627 #else
15628     UNREFERENCED_PARAMETER(msg);
15629 #endif // BACKGROUND_GC && TRACE_GC
15630 }
15631
15632 void gc_heap::update_collection_counts_for_no_gc()
15633 {
15634     assert (settings.pause_mode == pause_no_gc);
15635
15636     settings.condemned_generation = max_generation;
15637 #ifdef MULTIPLE_HEAPS
15638     for (int i = 0; i < n_heaps; i++)
15639         g_heaps[i]->update_collection_counts();
15640 #else //MULTIPLE_HEAPS
15641     update_collection_counts();
15642 #endif //MULTIPLE_HEAPS
15643
15644     full_gc_counts[gc_type_blocking]++;
15645 }
15646
15647 BOOL gc_heap::should_proceed_with_gc()
15648 {
15649     if (gc_heap::settings.pause_mode == pause_no_gc)
15650     {
15651         if (current_no_gc_region_info.started)
15652         {
15653             // The no_gc mode was already in progress yet we triggered another GC,
15654             // this effectively exits the no_gc mode.
15655             restore_data_for_no_gc();
15656         }
15657         else
15658             return should_proceed_for_no_gc();
15659     }
15660
15661     return TRUE;
15662 }
15663
15664 //internal part of gc used by the serial and concurrent version
15665 void gc_heap::gc1()
15666 {
15667 #ifdef BACKGROUND_GC
15668     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15669 #endif //BACKGROUND_GC
15670
15671 #ifdef TIME_GC
15672     mark_time = plan_time = reloc_time = compact_time = sweep_time = 0;
15673 #endif //TIME_GC
15674
15675     verify_soh_segment_list();
15676
15677     int n = settings.condemned_generation;
15678
15679     if (settings.reason == reason_pm_full_gc)
15680     {
15681         assert (n == max_generation);
15682         init_records();
15683
15684         gen_to_condemn_tuning* local_condemn_reasons = &(get_gc_data_per_heap()->gen_to_condemn_reasons);
15685         local_condemn_reasons->init();
15686         local_condemn_reasons->set_gen (gen_initial, n);
15687         local_condemn_reasons->set_gen (gen_final_per_heap, n);
15688     }
15689
15690     update_collection_counts ();
15691
15692 #ifdef BACKGROUND_GC
15693     bgc_alloc_lock->check();
15694 #endif //BACKGROUND_GC
15695
15696     free_list_info (max_generation, "beginning");
15697
15698     vm_heap->GcCondemnedGeneration = settings.condemned_generation;
15699
15700     assert (g_gc_card_table == card_table);
15701
15702 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
15703     assert (g_gc_card_bundle_table == card_bundle_table);
15704 #endif    
15705
15706     {
15707         if (n == max_generation)
15708         {
15709             gc_low = lowest_address;
15710             gc_high = highest_address;
15711         }
15712         else
15713         {
15714             gc_low = generation_allocation_start (generation_of (n));
15715             gc_high = heap_segment_reserved (ephemeral_heap_segment);
15716         }   
15717 #ifdef BACKGROUND_GC
15718         if (settings.concurrent)
15719         {
15720 #ifdef TRACE_GC
15721             time_bgc_last = GetHighPrecisionTimeStamp();
15722 #endif //TRACE_GC
15723
15724             FIRE_EVENT(BGCBegin);
15725
15726             concurrent_print_time_delta ("BGC");
15727
15728 //#ifdef WRITE_WATCH
15729             //reset_write_watch (FALSE);
15730 //#endif //WRITE_WATCH
15731
15732             concurrent_print_time_delta ("RW");
15733             background_mark_phase();
15734             free_list_info (max_generation, "after mark phase");
15735             
15736             background_sweep();
15737             free_list_info (max_generation, "after sweep phase");
15738         }
15739         else
15740 #endif //BACKGROUND_GC
15741         {
15742             mark_phase (n, FALSE);
15743
15744             GCScan::GcRuntimeStructuresValid (FALSE);
15745             plan_phase (n);
15746             GCScan::GcRuntimeStructuresValid (TRUE);
15747         }
15748     }
15749
15750     size_t end_gc_time = GetHighPrecisionTimeStamp();
15751 //    printf ("generation: %d, elapsed time: %Id\n", n,  end_gc_time - dd_time_clock (dynamic_data_of (0)));
15752
15753     //adjust the allocation size from the pinned quantities. 
15754     for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++)
15755     {
15756         generation* gn = generation_of (gen_number);
15757         if (settings.compaction)
15758         {
15759             generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn);
15760             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn);
15761         }
15762         else
15763         {
15764             generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn);
15765             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn);
15766         }
15767         generation_pinned_allocation_sweep_size (gn) = 0;
15768         generation_pinned_allocation_compact_size (gn) = 0;
15769     }
15770
15771 #ifdef BACKGROUND_GC
15772     if (settings.concurrent)
15773     {
15774         dynamic_data* dd = dynamic_data_of (n);
15775         dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15776
15777         free_list_info (max_generation, "after computing new dynamic data");
15778
15779         gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
15780
15781         for (int gen_number = 0; gen_number < max_generation; gen_number++)
15782         {
15783             dprintf (2, ("end of BGC: gen%d new_alloc: %Id", 
15784                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15785             current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number);
15786             current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15787             current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15788         }
15789     }
15790     else
15791 #endif //BACKGROUND_GC
15792     {
15793         free_list_info (max_generation, "end");
15794         for (int gen_number = 0; gen_number <= n; gen_number++)
15795         {
15796             dynamic_data* dd = dynamic_data_of (gen_number);
15797             dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15798             compute_new_dynamic_data (gen_number);
15799         }
15800
15801         if (n != max_generation)
15802         {
15803             int gen_num_for_data = ((n < (max_generation - 1)) ? (n + 1) : (max_generation + 1));
15804             for (int gen_number = (n + 1); gen_number <= gen_num_for_data; gen_number++)
15805             {
15806                 get_gc_data_per_heap()->gen_data[gen_number].size_after = generation_size (gen_number);
15807                 get_gc_data_per_heap()->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15808                 get_gc_data_per_heap()->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15809             }
15810         }
15811
15812         get_gc_data_per_heap()->maxgen_size_info.running_free_list_efficiency = (uint32_t)(generation_allocator_efficiency (generation_of (max_generation)) * 100);
15813
15814         free_list_info (max_generation, "after computing new dynamic data");
15815         
15816         if (heap_number == 0)
15817         {
15818             dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms", 
15819                 dd_collection_count (dynamic_data_of (0)), 
15820                 settings.condemned_generation,
15821                 dd_gc_elapsed_time (dynamic_data_of (0))));
15822         }
15823
15824         for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
15825         {
15826             dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id", 
15827                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15828         }
15829     }
15830
15831     if (n < max_generation)
15832     {
15833         compute_promoted_allocation (1 + n);
15834
15835         dynamic_data* dd = dynamic_data_of (1 + n);
15836         size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) + 
15837                                    generation_free_obj_space (generation_of (1 + n));
15838
15839 #ifdef BACKGROUND_GC
15840         if (current_c_gc_state != c_gc_state_planning)
15841 #endif //BACKGROUND_GC
15842         {
15843             if (settings.promotion)
15844             {
15845                 dd_fragmentation (dd) = new_fragmentation;
15846             }
15847             else
15848             {
15849                 //assert (dd_fragmentation (dd) == new_fragmentation);
15850             }
15851         }
15852     }
15853
15854 #ifdef BACKGROUND_GC
15855     if (!settings.concurrent)
15856 #endif //BACKGROUND_GC
15857     {
15858 #ifndef FEATURE_REDHAWK
15859         // GCToEEInterface::IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR.
15860         assert(GCToEEInterface::IsGCThread());
15861 #endif // FEATURE_REDHAWK
15862         adjust_ephemeral_limits();
15863     }
15864
15865 #ifdef BACKGROUND_GC
15866     assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1)));
15867     assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
15868 #endif //BACKGROUND_GC
15869
15870     if (fgn_maxgen_percent)
15871     {
15872         if (settings.condemned_generation == (max_generation - 1))
15873         {
15874             check_for_full_gc (max_generation - 1, 0);
15875         }
15876         else if (settings.condemned_generation == max_generation)
15877         {
15878             if (full_gc_approach_event_set 
15879 #ifdef MULTIPLE_HEAPS
15880                 && (heap_number == 0)
15881 #endif //MULTIPLE_HEAPS
15882                 )
15883             {
15884                 dprintf (2, ("FGN-GC: setting gen2 end event"));
15885
15886                 full_gc_approach_event.Reset();
15887 #ifdef BACKGROUND_GC
15888                 // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A
15889                 fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE;
15890 #endif //BACKGROUND_GC
15891                 full_gc_end_event.Set();
15892                 full_gc_approach_event_set = false;            
15893             }
15894         }
15895     }
15896
15897 #ifdef BACKGROUND_GC
15898     if (!settings.concurrent)
15899 #endif //BACKGROUND_GC
15900     {
15901         //decide on the next allocation quantum
15902         if (alloc_contexts_used >= 1)
15903         {
15904             allocation_quantum = Align (min ((size_t)CLR_SIZE,
15905                                             (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))),
15906                                             get_alignment_constant(FALSE));
15907             dprintf (3, ("New allocation quantum: %d(0x%Ix)", allocation_quantum, allocation_quantum));
15908         }
15909     }
15910
15911     descr_generations (FALSE);
15912
15913     verify_soh_segment_list();
15914
15915 #ifdef BACKGROUND_GC
15916     add_to_history_per_heap();
15917     if (heap_number == 0)
15918     {
15919         add_to_history();
15920     }
15921 #endif // BACKGROUND_GC
15922
15923 #ifdef GC_STATS
15924     if (GCStatistics::Enabled() && heap_number == 0)
15925         g_GCStatistics.AddGCStats(settings, 
15926             dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation)));
15927 #endif // GC_STATS
15928
15929 #ifdef TIME_GC
15930     fprintf (stdout, "%d,%d,%d,%d,%d,%d\n",
15931              n, mark_time, plan_time, reloc_time, compact_time, sweep_time);
15932 #endif //TIME_GC
15933
15934 #ifdef BACKGROUND_GC
15935     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15936 #endif //BACKGROUND_GC
15937
15938 #if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15939     if (FALSE 
15940 #ifdef VERIFY_HEAP
15941         // Note that right now g_pConfig->GetHeapVerifyLevel always returns the same
15942         // value. If we ever allow randomly adjusting this as the process runs,
15943         // we cannot call it this way as joins need to match - we must have the same
15944         // value for all heaps like we do with bgc_heap_walk_for_etw_p.
15945         || (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
15946 #endif
15947 #if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)
15948         || (bgc_heap_walk_for_etw_p && settings.concurrent)
15949 #endif
15950         )
15951     {
15952 #ifdef BACKGROUND_GC
15953         bool cooperative_mode = true;
15954
15955         if (settings.concurrent)
15956         {
15957             cooperative_mode = enable_preemptive ();
15958
15959 #ifdef MULTIPLE_HEAPS
15960             bgc_t_join.join(this, gc_join_suspend_ee_verify);
15961             if (bgc_t_join.joined())
15962             {
15963                 bgc_threads_sync_event.Reset();
15964
15965                 dprintf(2, ("Joining BGC threads to suspend EE for verify heap"));
15966                 bgc_t_join.restart();
15967             }
15968             if (heap_number == 0)
15969             {
15970                 suspend_EE();
15971                 bgc_threads_sync_event.Set();
15972             }
15973             else
15974             {
15975                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15976                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15977             }
15978 #else
15979             suspend_EE();
15980 #endif //MULTIPLE_HEAPS
15981
15982             //fix the allocation area so verify_heap can proceed.
15983             fix_allocation_contexts (FALSE);
15984         }
15985 #endif //BACKGROUND_GC
15986
15987 #ifdef BACKGROUND_GC
15988         assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15989 #ifdef FEATURE_EVENT_TRACE
15990         if (bgc_heap_walk_for_etw_p && settings.concurrent)
15991         {
15992             GCToEEInterface::DiagWalkBGCSurvivors(__this);
15993
15994 #ifdef MULTIPLE_HEAPS
15995             bgc_t_join.join(this, gc_join_after_profiler_heap_walk);
15996             if (bgc_t_join.joined())
15997             {
15998                 bgc_t_join.restart();
15999             }
16000 #endif // MULTIPLE_HEAPS
16001         }
16002 #endif // FEATURE_EVENT_TRACE
16003 #endif //BACKGROUND_GC
16004
16005 #ifdef VERIFY_HEAP
16006         if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
16007             verify_heap (FALSE);
16008 #endif // VERIFY_HEAP
16009
16010 #ifdef BACKGROUND_GC
16011         if (settings.concurrent)
16012         {
16013             repair_allocation_contexts (TRUE);
16014
16015 #ifdef MULTIPLE_HEAPS
16016             bgc_t_join.join(this, gc_join_restart_ee_verify);
16017             if (bgc_t_join.joined())
16018             {
16019                 bgc_threads_sync_event.Reset();
16020
16021                 dprintf(2, ("Joining BGC threads to restart EE after verify heap"));
16022                 bgc_t_join.restart();
16023             }
16024             if (heap_number == 0)
16025             {
16026                 restart_EE();
16027                 bgc_threads_sync_event.Set();
16028             }
16029             else
16030             {
16031                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
16032                 dprintf (2, ("bgc_threads_sync_event is signalled"));
16033             }
16034 #else
16035             restart_EE();
16036 #endif //MULTIPLE_HEAPS
16037
16038             disable_preemptive (cooperative_mode);
16039         }
16040 #endif //BACKGROUND_GC
16041     }
16042 #endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
16043
16044 #ifdef MULTIPLE_HEAPS
16045     if (!settings.concurrent)
16046     {
16047         gc_t_join.join(this, gc_join_done);
16048         if (gc_t_join.joined ())
16049         {
16050             gc_heap::internal_gc_done = false;
16051
16052             //equalize the new desired size of the generations
16053             int limit = settings.condemned_generation;
16054             if (limit == max_generation)
16055             {
16056                 limit = max_generation+1;
16057             }
16058             for (int gen = 0; gen <= limit; gen++)
16059             {
16060                 size_t total_desired = 0;
16061
16062                 for (int i = 0; i < gc_heap::n_heaps; i++)
16063                 {
16064                     gc_heap* hp = gc_heap::g_heaps[i];
16065                     dynamic_data* dd = hp->dynamic_data_of (gen);
16066                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
16067                     if (temp_total_desired < total_desired)
16068                     {
16069                         // we overflowed.
16070                         total_desired = (size_t)MAX_PTR;
16071                         break;
16072                     }
16073                     total_desired = temp_total_desired;
16074                 }
16075
16076                 size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps,
16077                                                     get_alignment_constant ((gen != (max_generation+1))));
16078
16079                 if (gen == 0)
16080                 {
16081 #if 1 //subsumed by the linear allocation model 
16082                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
16083                     // apply some smoothing.
16084                     static size_t smoothed_desired_per_heap = 0;
16085                     size_t smoothing = 3; // exponential smoothing factor
16086                     if (smoothing  > VolatileLoad(&settings.gc_index))
16087                         smoothing  = VolatileLoad(&settings.gc_index);
16088                     smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1));
16089                     dprintf (1, ("sn = %Id  n = %Id", smoothed_desired_per_heap, desired_per_heap));
16090                     desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true));
16091 #endif //0
16092
16093                     if (!heap_hard_limit)
16094                     {
16095                         // if desired_per_heap is close to min_gc_size, trim it
16096                         // down to min_gc_size to stay in the cache
16097                         gc_heap* hp = gc_heap::g_heaps[0];
16098                         dynamic_data* dd = hp->dynamic_data_of (gen);
16099                         size_t min_gc_size = dd_min_size(dd);
16100                         // if min GC size larger than true on die cache, then don't bother
16101                         // limiting the desired size
16102                         if ((min_gc_size <= GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)) &&
16103                             desired_per_heap <= 2*min_gc_size)
16104                         {
16105                             desired_per_heap = min_gc_size;
16106                         }
16107                     }
16108 #ifdef BIT64
16109                     desired_per_heap = joined_youngest_desired (desired_per_heap);
16110                     dprintf (2, ("final gen0 new_alloc: %Id", desired_per_heap));
16111 #endif // BIT64
16112                     gc_data_global.final_youngest_desired = desired_per_heap;
16113                 }
16114 #if 1 //subsumed by the linear allocation model 
16115                 if (gen == (max_generation + 1))
16116                 {
16117                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
16118                     // apply some smoothing.
16119                     static size_t smoothed_desired_per_heap_loh = 0;
16120                     size_t smoothing = 3; // exponential smoothing factor
16121                     size_t loh_count = dd_collection_count (dynamic_data_of (max_generation));
16122                     if (smoothing  > loh_count)
16123                         smoothing  = loh_count;
16124                     smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1));
16125                     dprintf (2, ("smoothed_desired_per_heap_loh  = %Id  desired_per_heap = %Id", smoothed_desired_per_heap_loh, desired_per_heap));
16126                     desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false));
16127                 }
16128 #endif //0
16129                 for (int i = 0; i < gc_heap::n_heaps; i++)
16130                 {
16131                     gc_heap* hp = gc_heap::g_heaps[i];
16132                     dynamic_data* dd = hp->dynamic_data_of (gen);
16133                     dd_desired_allocation (dd) = desired_per_heap;
16134                     dd_gc_new_allocation (dd) = desired_per_heap;
16135                     dd_new_allocation (dd) = desired_per_heap;
16136
16137                     if (gen == 0)
16138                     {
16139                         hp->fgn_last_alloc = desired_per_heap;
16140                     }
16141                 }
16142             }
16143
16144 #ifdef FEATURE_LOH_COMPACTION
16145             BOOL all_heaps_compacted_p = TRUE;
16146 #endif //FEATURE_LOH_COMPACTION
16147             for (int i = 0; i < gc_heap::n_heaps; i++)
16148             {
16149                 gc_heap* hp = gc_heap::g_heaps[i];
16150                 hp->decommit_ephemeral_segment_pages();
16151                 hp->rearrange_large_heap_segments();
16152 #ifdef FEATURE_LOH_COMPACTION
16153                 all_heaps_compacted_p &= hp->loh_compacted_p;
16154 #endif //FEATURE_LOH_COMPACTION
16155             }
16156
16157 #ifdef FEATURE_LOH_COMPACTION
16158             check_loh_compact_mode (all_heaps_compacted_p);
16159 #endif //FEATURE_LOH_COMPACTION
16160
16161             fire_pevents();
16162             pm_full_gc_init_or_clear();
16163
16164             gc_t_join.restart();
16165         }
16166         alloc_context_count = 0;
16167         heap_select::mark_heap (heap_number);
16168     }
16169
16170 #else
16171     gc_data_global.final_youngest_desired = 
16172         dd_desired_allocation (dynamic_data_of (0));
16173
16174     check_loh_compact_mode (loh_compacted_p);
16175
16176     decommit_ephemeral_segment_pages();
16177     fire_pevents();
16178
16179     if (!(settings.concurrent))
16180     {
16181         rearrange_large_heap_segments();
16182         do_post_gc();
16183     }
16184
16185     pm_full_gc_init_or_clear();
16186
16187 #ifdef BACKGROUND_GC
16188     recover_bgc_settings();
16189 #endif //BACKGROUND_GC
16190 #endif //MULTIPLE_HEAPS
16191 }
16192
16193 void gc_heap::save_data_for_no_gc()
16194 {
16195     current_no_gc_region_info.saved_pause_mode = settings.pause_mode;
16196 #ifdef MULTIPLE_HEAPS
16197     // This is to affect heap balancing. 
16198     for (int i = 0; i < n_heaps; i++)
16199     {
16200         current_no_gc_region_info.saved_gen0_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (0));
16201         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = min_balance_threshold;
16202         current_no_gc_region_info.saved_gen3_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1));
16203         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = 0;
16204     }
16205 #endif //MULTIPLE_HEAPS
16206 }
16207
16208 void gc_heap::restore_data_for_no_gc()
16209 {
16210     gc_heap::settings.pause_mode = current_no_gc_region_info.saved_pause_mode;
16211 #ifdef MULTIPLE_HEAPS
16212     for (int i = 0; i < n_heaps; i++)
16213     {
16214         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = current_no_gc_region_info.saved_gen0_min_size;
16215         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = current_no_gc_region_info.saved_gen3_min_size;
16216     }
16217 #endif //MULTIPLE_HEAPS
16218 }
16219
16220 start_no_gc_region_status gc_heap::prepare_for_no_gc_region (uint64_t total_size,
16221                                                              BOOL loh_size_known, 
16222                                                              uint64_t loh_size,
16223                                                              BOOL disallow_full_blocking)
16224 {
16225     if (current_no_gc_region_info.started)
16226     {
16227         return start_no_gc_in_progress;
16228     }
16229
16230     start_no_gc_region_status status = start_no_gc_success;
16231
16232     save_data_for_no_gc();
16233     settings.pause_mode = pause_no_gc;
16234     current_no_gc_region_info.start_status = start_no_gc_success;
16235
16236     uint64_t allocation_no_gc_loh = 0;
16237     uint64_t allocation_no_gc_soh = 0;
16238     assert(total_size != 0);
16239     if (loh_size_known)
16240     {
16241         assert(loh_size != 0);
16242         assert(loh_size <= total_size);
16243         allocation_no_gc_loh = loh_size;
16244         allocation_no_gc_soh = total_size - loh_size;
16245     }
16246     else
16247     {
16248         allocation_no_gc_soh = total_size;
16249         allocation_no_gc_loh = total_size;
16250     }
16251
16252     int soh_align_const = get_alignment_constant (TRUE);
16253     size_t max_soh_allocated = soh_segment_size - segment_info_size - eph_gen_starts_size;
16254     size_t size_per_heap = 0;
16255     const double scale_factor = 1.05;
16256
16257     int num_heaps = 1;
16258 #ifdef MULTIPLE_HEAPS
16259     num_heaps = n_heaps;
16260 #endif // MULTIPLE_HEAPS
16261
16262     uint64_t total_allowed_soh_allocation = max_soh_allocated * num_heaps;
16263     // [LOCALGC TODO]
16264     // In theory, the upper limit here is the physical memory of the machine, not
16265     // SIZE_T_MAX. This is not true today because total_physical_mem can be
16266     // larger than SIZE_T_MAX if running in wow64 on a machine with more than
16267     // 4GB of RAM. Once Local GC code divergence is resolved and code is flowing
16268     // more freely between branches, it would be good to clean this up to use
16269     // total_physical_mem instead of SIZE_T_MAX.
16270     assert(total_allowed_soh_allocation <= SIZE_T_MAX);
16271     uint64_t total_allowed_loh_allocation = SIZE_T_MAX;
16272     uint64_t total_allowed_soh_alloc_scaled = allocation_no_gc_soh > 0 ? static_cast<uint64_t>(total_allowed_soh_allocation / scale_factor) : 0;
16273     uint64_t total_allowed_loh_alloc_scaled = allocation_no_gc_loh > 0 ? static_cast<uint64_t>(total_allowed_loh_allocation / scale_factor) : 0;
16274
16275     if (allocation_no_gc_soh > total_allowed_soh_alloc_scaled ||
16276         allocation_no_gc_loh > total_allowed_loh_alloc_scaled)
16277     {
16278         status = start_no_gc_too_large;
16279         goto done;
16280     }
16281
16282     if (allocation_no_gc_soh > 0)
16283     {
16284         allocation_no_gc_soh = static_cast<uint64_t>(allocation_no_gc_soh * scale_factor);
16285         allocation_no_gc_soh = min (allocation_no_gc_soh, total_allowed_soh_alloc_scaled);
16286     }
16287
16288     if (allocation_no_gc_loh > 0)
16289     {
16290         allocation_no_gc_loh = static_cast<uint64_t>(allocation_no_gc_loh * scale_factor);
16291         allocation_no_gc_loh = min (allocation_no_gc_loh, total_allowed_loh_alloc_scaled);
16292     }
16293
16294     if (disallow_full_blocking)
16295         current_no_gc_region_info.minimal_gc_p = TRUE;
16296
16297     if (allocation_no_gc_soh != 0)
16298     {
16299         current_no_gc_region_info.soh_allocation_size = static_cast<size_t>(allocation_no_gc_soh);
16300         size_per_heap = current_no_gc_region_info.soh_allocation_size;
16301 #ifdef MULTIPLE_HEAPS
16302         size_per_heap /= n_heaps;
16303         for (int i = 0; i < n_heaps; i++)
16304         {
16305             // due to heap balancing we need to allow some room before we even look to balance to another heap.
16306             g_heaps[i]->soh_allocation_no_gc = min (Align ((size_per_heap + min_balance_threshold), soh_align_const), max_soh_allocated);
16307         }
16308 #else //MULTIPLE_HEAPS
16309         soh_allocation_no_gc = min (Align (size_per_heap, soh_align_const), max_soh_allocated);
16310 #endif //MULTIPLE_HEAPS
16311     }
16312
16313     if (allocation_no_gc_loh != 0)
16314     {
16315         current_no_gc_region_info.loh_allocation_size = static_cast<size_t>(allocation_no_gc_loh);
16316         size_per_heap = current_no_gc_region_info.loh_allocation_size;
16317 #ifdef MULTIPLE_HEAPS
16318         size_per_heap /= n_heaps;
16319         for (int i = 0; i < n_heaps; i++)
16320             g_heaps[i]->loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
16321 #else //MULTIPLE_HEAPS
16322         loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
16323 #endif //MULTIPLE_HEAPS
16324     }
16325
16326 done:
16327     if (status != start_no_gc_success)
16328         restore_data_for_no_gc();
16329     return status;
16330 }
16331
16332 void gc_heap::handle_failure_for_no_gc()
16333 {
16334     gc_heap::restore_data_for_no_gc();
16335     // sets current_no_gc_region_info.started to FALSE here.
16336     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
16337 }
16338
16339 start_no_gc_region_status gc_heap::get_start_no_gc_region_status()
16340 {
16341     return current_no_gc_region_info.start_status;
16342 }
16343
16344 void gc_heap::record_gcs_during_no_gc()
16345 {
16346     if (current_no_gc_region_info.started)
16347     {
16348         current_no_gc_region_info.num_gcs++;
16349         if (is_induced (settings.reason))
16350             current_no_gc_region_info.num_gcs_induced++;
16351     }
16352 }
16353
16354 BOOL gc_heap::find_loh_free_for_no_gc()
16355 {
16356     allocator* loh_allocator = generation_allocator (generation_of (max_generation + 1));
16357     size_t sz_list = loh_allocator->first_bucket_size();
16358     size_t size = loh_allocation_no_gc;
16359     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
16360     {
16361         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
16362         {
16363             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
16364             while (free_list)
16365             {
16366                 size_t free_list_size = unused_array_size(free_list);
16367
16368                 if (free_list_size > loh_allocation_no_gc)
16369                 {
16370                     dprintf (3, ("free item %Ix(%Id) for no gc", (size_t)free_list, free_list_size));
16371                     return TRUE;
16372                 }
16373
16374                 free_list = free_list_slot (free_list); 
16375             }
16376         }
16377         sz_list = sz_list * 2;
16378     }
16379
16380     return FALSE;
16381 }
16382
16383 BOOL gc_heap::find_loh_space_for_no_gc()
16384 {
16385     saved_loh_segment_no_gc = 0;
16386
16387     if (find_loh_free_for_no_gc())
16388         return TRUE;
16389
16390     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16391
16392     while (seg)
16393     {
16394         size_t remaining = heap_segment_reserved (seg) - heap_segment_allocated (seg);
16395         if (remaining >= loh_allocation_no_gc)
16396         {
16397             saved_loh_segment_no_gc = seg;
16398             break;
16399         }
16400         seg = heap_segment_next (seg);
16401     }
16402
16403     if (!saved_loh_segment_no_gc && current_no_gc_region_info.minimal_gc_p)
16404     {
16405         // If no full GC is allowed, we try to get a new seg right away.
16406         saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc)
16407 #ifdef MULTIPLE_HEAPS
16408                                                       , this
16409 #endif //MULTIPLE_HEAPS
16410                                                       );
16411     }
16412
16413     return (saved_loh_segment_no_gc != 0);
16414 }
16415
16416 BOOL gc_heap::loh_allocated_for_no_gc()
16417 {
16418     if (!saved_loh_segment_no_gc)
16419         return FALSE;
16420
16421     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16422     do 
16423     {
16424         if (seg == saved_loh_segment_no_gc)
16425         {
16426             return FALSE;
16427         }
16428         seg = heap_segment_next (seg);
16429     } while (seg);
16430
16431     return TRUE;
16432 }
16433
16434 BOOL gc_heap::commit_loh_for_no_gc (heap_segment* seg)
16435 {
16436     uint8_t* end_committed = heap_segment_allocated (seg) + loh_allocation_no_gc;
16437     assert (end_committed <= heap_segment_reserved (seg));
16438     return (grow_heap_segment (seg, end_committed));
16439 }
16440
16441 void gc_heap::thread_no_gc_loh_segments()
16442 {
16443 #ifdef MULTIPLE_HEAPS
16444     for (int i = 0; i < n_heaps; i++)
16445     {
16446         gc_heap* hp = g_heaps[i];
16447         if (hp->loh_allocated_for_no_gc())
16448         {
16449             hp->thread_loh_segment (hp->saved_loh_segment_no_gc);
16450             hp->saved_loh_segment_no_gc = 0;
16451         }
16452     }
16453 #else //MULTIPLE_HEAPS
16454     if (loh_allocated_for_no_gc())
16455     {
16456         thread_loh_segment (saved_loh_segment_no_gc);
16457         saved_loh_segment_no_gc = 0;
16458     }
16459 #endif //MULTIPLE_HEAPS    
16460 }
16461
16462 void gc_heap::set_loh_allocations_for_no_gc()
16463 {
16464     if (current_no_gc_region_info.loh_allocation_size != 0)
16465     {
16466         dynamic_data* dd = dynamic_data_of (max_generation + 1);
16467         dd_new_allocation (dd) = loh_allocation_no_gc;
16468         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16469     }
16470 }
16471
16472 void gc_heap::set_soh_allocations_for_no_gc()
16473 {
16474     if (current_no_gc_region_info.soh_allocation_size != 0)
16475     {
16476         dynamic_data* dd = dynamic_data_of (0);
16477         dd_new_allocation (dd) = soh_allocation_no_gc;
16478         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16479 #ifdef MULTIPLE_HEAPS
16480         alloc_context_count = 0;
16481 #endif //MULTIPLE_HEAPS
16482     }
16483 }
16484
16485 void gc_heap::set_allocations_for_no_gc()
16486 {
16487 #ifdef MULTIPLE_HEAPS
16488     for (int i = 0; i < n_heaps; i++)
16489     {
16490         gc_heap* hp = g_heaps[i];
16491         hp->set_loh_allocations_for_no_gc();
16492         hp->set_soh_allocations_for_no_gc();
16493     }
16494 #else //MULTIPLE_HEAPS
16495     set_loh_allocations_for_no_gc();
16496     set_soh_allocations_for_no_gc();
16497 #endif //MULTIPLE_HEAPS
16498 }
16499
16500 BOOL gc_heap::should_proceed_for_no_gc()
16501 {
16502     BOOL gc_requested = FALSE;
16503     BOOL loh_full_gc_requested = FALSE;
16504     BOOL soh_full_gc_requested = FALSE;
16505     BOOL no_gc_requested = FALSE;
16506     BOOL get_new_loh_segments = FALSE;
16507
16508     if (current_no_gc_region_info.soh_allocation_size)
16509     {
16510 #ifdef MULTIPLE_HEAPS
16511         for (int i = 0; i < n_heaps; i++)
16512         {
16513             gc_heap* hp = g_heaps[i];
16514             if ((size_t)(heap_segment_reserved (hp->ephemeral_heap_segment) - hp->alloc_allocated) < hp->soh_allocation_no_gc)
16515             {
16516                 gc_requested = TRUE;
16517                 break;
16518             }
16519         }
16520 #else //MULTIPLE_HEAPS
16521         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated) < soh_allocation_no_gc)
16522             gc_requested = TRUE;
16523 #endif //MULTIPLE_HEAPS
16524
16525         if (!gc_requested)
16526         {
16527 #ifdef MULTIPLE_HEAPS
16528             for (int i = 0; i < n_heaps; i++)
16529             {
16530                 gc_heap* hp = g_heaps[i];
16531                 if (!(hp->grow_heap_segment (hp->ephemeral_heap_segment, (hp->alloc_allocated + hp->soh_allocation_no_gc))))
16532                 {
16533                     soh_full_gc_requested = TRUE;
16534                     break;
16535                 }
16536             }
16537 #else //MULTIPLE_HEAPS
16538             if (!grow_heap_segment (ephemeral_heap_segment, (alloc_allocated + soh_allocation_no_gc)))
16539                 soh_full_gc_requested = TRUE;
16540 #endif //MULTIPLE_HEAPS
16541         }
16542     }
16543
16544     if (!current_no_gc_region_info.minimal_gc_p && gc_requested)
16545     {
16546         soh_full_gc_requested = TRUE;
16547     }
16548
16549     no_gc_requested = !(soh_full_gc_requested || gc_requested);
16550
16551     if (soh_full_gc_requested && current_no_gc_region_info.minimal_gc_p)
16552     {
16553         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16554         goto done;
16555     }
16556
16557     if (!soh_full_gc_requested && current_no_gc_region_info.loh_allocation_size)
16558     {
16559         // Check to see if we have enough reserved space. 
16560 #ifdef MULTIPLE_HEAPS
16561         for (int i = 0; i < n_heaps; i++)
16562         {
16563             gc_heap* hp = g_heaps[i];
16564             if (!hp->find_loh_space_for_no_gc())
16565             {
16566                 loh_full_gc_requested = TRUE;
16567                 break;
16568             }
16569         }
16570 #else //MULTIPLE_HEAPS
16571         if (!find_loh_space_for_no_gc())
16572             loh_full_gc_requested = TRUE;
16573 #endif //MULTIPLE_HEAPS
16574
16575         // Check to see if we have committed space.
16576         if (!loh_full_gc_requested)
16577         {
16578 #ifdef MULTIPLE_HEAPS
16579             for (int i = 0; i < n_heaps; i++)
16580             {
16581                 gc_heap* hp = g_heaps[i];
16582                 if (hp->saved_loh_segment_no_gc &&!hp->commit_loh_for_no_gc (hp->saved_loh_segment_no_gc))
16583                 {
16584                     loh_full_gc_requested = TRUE;
16585                     break;
16586                 }
16587             }
16588 #else //MULTIPLE_HEAPS
16589             if (saved_loh_segment_no_gc && !commit_loh_for_no_gc (saved_loh_segment_no_gc))
16590                 loh_full_gc_requested = TRUE;
16591 #endif //MULTIPLE_HEAPS
16592         }
16593     }
16594
16595     if (loh_full_gc_requested || soh_full_gc_requested)
16596     {
16597         if (current_no_gc_region_info.minimal_gc_p)
16598             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16599     }
16600
16601     no_gc_requested = !(loh_full_gc_requested || soh_full_gc_requested || gc_requested);
16602
16603     if (current_no_gc_region_info.start_status == start_no_gc_success)
16604     {
16605         if (no_gc_requested)
16606             set_allocations_for_no_gc();
16607     }
16608
16609 done:
16610
16611     if ((current_no_gc_region_info.start_status == start_no_gc_success) && !no_gc_requested)
16612         return TRUE;
16613     else
16614     {
16615         // We are done with starting the no_gc_region.
16616         current_no_gc_region_info.started = TRUE;
16617         return FALSE;
16618     }
16619 }
16620
16621 end_no_gc_region_status gc_heap::end_no_gc_region()
16622 {
16623     dprintf (1, ("end no gc called"));
16624
16625     end_no_gc_region_status status = end_no_gc_success;
16626
16627     if (!(current_no_gc_region_info.started))
16628         status = end_no_gc_not_in_progress;
16629     if (current_no_gc_region_info.num_gcs_induced)
16630         status = end_no_gc_induced;
16631     else if (current_no_gc_region_info.num_gcs)
16632         status = end_no_gc_alloc_exceeded;
16633
16634     if (settings.pause_mode == pause_no_gc)
16635         restore_data_for_no_gc();
16636
16637     // sets current_no_gc_region_info.started to FALSE here.
16638     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
16639
16640     return status;
16641 }
16642
16643 //update counters
16644 void gc_heap::update_collection_counts ()
16645 {
16646     dynamic_data* dd0 = dynamic_data_of (0);
16647     dd_gc_clock (dd0) += 1;
16648
16649     size_t now = GetHighPrecisionTimeStamp();
16650
16651     for (int i = 0; i <= settings.condemned_generation;i++)
16652     {
16653         dynamic_data* dd = dynamic_data_of (i);
16654         dd_collection_count (dd)++;
16655         //this is needed by the linear allocation model
16656         if (i == max_generation)
16657             dd_collection_count (dynamic_data_of (max_generation+1))++;
16658         dd_gc_clock (dd) = dd_gc_clock (dd0);
16659         dd_time_clock (dd) = now;
16660     }
16661 }
16662
16663 BOOL gc_heap::expand_soh_with_minimal_gc()
16664 {
16665     if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) >= soh_allocation_no_gc)
16666         return TRUE;
16667
16668     heap_segment* new_seg = soh_get_segment_to_expand();
16669     if (new_seg)
16670     {
16671         if (g_gc_card_table != card_table)
16672             copy_brick_card_table();
16673
16674         settings.promotion = TRUE;
16675         settings.demotion = FALSE;
16676         ephemeral_promotion = TRUE;
16677         int condemned_gen_number = max_generation - 1;
16678
16679         generation* gen = 0;
16680         int align_const = get_alignment_constant (TRUE);
16681
16682         for (int i = 0; i <= condemned_gen_number; i++)
16683         {
16684             gen = generation_of (i);
16685             saved_ephemeral_plan_start[i] = generation_allocation_start (gen);
16686             saved_ephemeral_plan_start_size[i] = Align (size (generation_allocation_start (gen)), align_const);
16687         }
16688
16689         // We do need to clear the bricks here as we are converting a bunch of ephemeral objects to gen2
16690         // and need to make sure that there are no left over bricks from the previous GCs for the space 
16691         // we just used for gen0 allocation. We will need to go through the bricks for these objects for 
16692         // ephemeral GCs later.
16693         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
16694              b < brick_of (align_on_brick (heap_segment_allocated (ephemeral_heap_segment)));
16695              b++)
16696         {
16697             set_brick (b, -1);
16698         }
16699
16700         size_t ephemeral_size = (heap_segment_allocated (ephemeral_heap_segment) - 
16701                                 generation_allocation_start (generation_of (max_generation - 1)));
16702         heap_segment_next (ephemeral_heap_segment) = new_seg;
16703         ephemeral_heap_segment = new_seg;
16704         uint8_t*  start = heap_segment_mem (ephemeral_heap_segment);
16705
16706         for (int i = condemned_gen_number; i >= 0; i--)
16707         {
16708             gen = generation_of (i);
16709             size_t gen_start_size = Align (min_obj_size);
16710             make_generation (generation_table[i], ephemeral_heap_segment, start, 0);
16711             generation_plan_allocation_start (gen) = start;
16712             generation_plan_allocation_start_size (gen) = gen_start_size;
16713             start += gen_start_size;
16714         }
16715         heap_segment_used (ephemeral_heap_segment) = start - plug_skew;
16716         heap_segment_plan_allocated (ephemeral_heap_segment) = start;
16717
16718         fix_generation_bounds (condemned_gen_number, generation_of (0));
16719
16720         dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size;
16721         dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation));
16722
16723         adjust_ephemeral_limits();
16724         return TRUE;
16725     }
16726     else
16727         return FALSE;
16728 }
16729
16730 // Only to be done on the thread that calls restart in a join for server GC
16731 // and reset the oom status per heap.
16732 void gc_heap::check_and_set_no_gc_oom()
16733 {
16734 #ifdef MULTIPLE_HEAPS
16735     for (int i = 0; i < n_heaps; i++)
16736     {
16737         gc_heap* hp = g_heaps[i];
16738         if (hp->no_gc_oom_p)
16739         {
16740             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16741             hp->no_gc_oom_p = false;
16742         }
16743     }
16744 #else
16745     if (no_gc_oom_p)
16746     {
16747         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16748         no_gc_oom_p = false;
16749     }
16750 #endif //MULTIPLE_HEAPS
16751 }
16752
16753 void gc_heap::allocate_for_no_gc_after_gc()
16754 {
16755     if (current_no_gc_region_info.minimal_gc_p)
16756         repair_allocation_contexts (TRUE);
16757
16758     no_gc_oom_p = false;
16759
16760     if (current_no_gc_region_info.start_status != start_no_gc_no_memory)
16761     {
16762         if (current_no_gc_region_info.soh_allocation_size != 0)
16763         {
16764             if (((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) < soh_allocation_no_gc) ||
16765                 (!grow_heap_segment (ephemeral_heap_segment, (heap_segment_allocated (ephemeral_heap_segment) + soh_allocation_no_gc))))
16766             {
16767                 no_gc_oom_p = true;
16768             }
16769
16770 #ifdef MULTIPLE_HEAPS
16771             gc_t_join.join(this, gc_join_after_commit_soh_no_gc);
16772             if (gc_t_join.joined())
16773             {
16774 #endif //MULTIPLE_HEAPS
16775
16776                 check_and_set_no_gc_oom();
16777
16778 #ifdef MULTIPLE_HEAPS
16779                 gc_t_join.restart();
16780             }
16781 #endif //MULTIPLE_HEAPS
16782         }
16783
16784         if ((current_no_gc_region_info.start_status == start_no_gc_success) &&
16785             !(current_no_gc_region_info.minimal_gc_p) && 
16786             (current_no_gc_region_info.loh_allocation_size != 0))
16787         {
16788             gc_policy = policy_compact;
16789             saved_loh_segment_no_gc = 0;
16790
16791             if (!find_loh_free_for_no_gc())
16792             {
16793                 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16794                 BOOL found_seg_p = FALSE;
16795                 while (seg)
16796                 {
16797                     if ((size_t)(heap_segment_reserved (seg) - heap_segment_allocated (seg)) >= loh_allocation_no_gc)
16798                     {
16799                         found_seg_p = TRUE;
16800                         if (!commit_loh_for_no_gc (seg))
16801                         {
16802                             no_gc_oom_p = true;
16803                             break;
16804                         }
16805                     }
16806                     seg = heap_segment_next (seg);
16807                 }
16808
16809                 if (!found_seg_p)
16810                     gc_policy = policy_expand;
16811             }
16812
16813 #ifdef MULTIPLE_HEAPS
16814             gc_t_join.join(this, gc_join_expand_loh_no_gc);
16815             if (gc_t_join.joined())
16816             {
16817                 check_and_set_no_gc_oom();
16818
16819                 if (current_no_gc_region_info.start_status == start_no_gc_success)
16820                 {
16821                     for (int i = 0; i < n_heaps; i++)
16822                     {
16823                         gc_heap* hp = g_heaps[i];
16824                         if (hp->gc_policy == policy_expand)
16825                         {
16826                             hp->saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc), hp);
16827                             if (!(hp->saved_loh_segment_no_gc))
16828                             {
16829                                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16830                                 break;
16831                             }
16832                         }
16833                     }
16834                 }
16835
16836                 gc_t_join.restart();
16837             }
16838 #else //MULTIPLE_HEAPS
16839             check_and_set_no_gc_oom();
16840
16841             if ((current_no_gc_region_info.start_status == start_no_gc_success) && (gc_policy == policy_expand))
16842             {
16843                 saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc));
16844                 if (!saved_loh_segment_no_gc)
16845                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16846             }
16847 #endif //MULTIPLE_HEAPS
16848
16849             if ((current_no_gc_region_info.start_status == start_no_gc_success) && saved_loh_segment_no_gc)
16850             {
16851                 if (!commit_loh_for_no_gc (saved_loh_segment_no_gc))
16852                 {
16853                     no_gc_oom_p = true;
16854                 }
16855             }
16856         }
16857     }
16858
16859 #ifdef MULTIPLE_HEAPS
16860     gc_t_join.join(this, gc_join_final_no_gc);
16861     if (gc_t_join.joined())
16862     {
16863 #endif //MULTIPLE_HEAPS
16864
16865         check_and_set_no_gc_oom();
16866
16867         if (current_no_gc_region_info.start_status == start_no_gc_success)
16868         {
16869             set_allocations_for_no_gc();
16870             current_no_gc_region_info.started = TRUE;
16871         }
16872
16873 #ifdef MULTIPLE_HEAPS
16874         gc_t_join.restart();
16875     }
16876 #endif //MULTIPLE_HEAPS
16877 }
16878
16879 void gc_heap::init_records()
16880 {
16881     // An option is to move this to be after we figure out which gen to condemn so we don't 
16882     // need to clear some generations' data 'cause we know they don't change, but that also means 
16883     // we can't simply call memset here. 
16884     memset (&gc_data_per_heap, 0, sizeof (gc_data_per_heap));
16885     gc_data_per_heap.heap_index = heap_number;
16886     if (heap_number == 0)
16887         memset (&gc_data_global, 0, sizeof (gc_data_global));
16888
16889 #ifdef GC_CONFIG_DRIVEN
16890     memset (interesting_data_per_gc, 0, sizeof (interesting_data_per_gc));
16891 #endif //GC_CONFIG_DRIVEN
16892     memset (&fgm_result, 0, sizeof (fgm_result));
16893
16894     for (int i = 0; i <= (max_generation + 1); i++)
16895     {
16896         gc_data_per_heap.gen_data[i].size_before = generation_size (i);
16897         generation* gen = generation_of (i);
16898         gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen);
16899         gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen);
16900     }
16901
16902     sufficient_gen0_space_p = FALSE;
16903
16904 #ifdef MULTIPLE_HEAPS
16905     gen0_allocated_after_gc_p = false;
16906 #endif //MULTIPLE_HEAPS
16907
16908 #if defined (_DEBUG) && defined (VERIFY_HEAP)
16909     verify_pinned_queue_p = FALSE;
16910 #endif // _DEBUG && VERIFY_HEAP
16911 }
16912
16913 void gc_heap::pm_full_gc_init_or_clear()
16914 {
16915     // This means the next GC will be a full blocking GC and we need to init.
16916     if (settings.condemned_generation == (max_generation - 1))
16917     {
16918         if (pm_trigger_full_gc)
16919         {
16920 #ifdef MULTIPLE_HEAPS
16921             do_post_gc();
16922 #endif //MULTIPLE_HEAPS
16923             dprintf (GTC_LOG, ("init for PM triggered full GC"));
16924             uint32_t saved_entry_memory_load = settings.entry_memory_load;
16925             settings.init_mechanisms();
16926             settings.reason = reason_pm_full_gc;
16927             settings.condemned_generation = max_generation;
16928             settings.entry_memory_load = saved_entry_memory_load;
16929             // Can't assert this since we only check at the end of gen2 GCs,
16930             // during gen1 the memory load could have already dropped. 
16931             // Although arguably we should just turn off PM then...
16932             //assert (settings.entry_memory_load >= high_memory_load_th);
16933             assert (settings.entry_memory_load > 0);
16934             settings.gc_index += 1;
16935             do_pre_gc();
16936         }
16937     }
16938     // This means we are in the progress of a full blocking GC triggered by
16939     // this PM mode.
16940     else if (settings.reason == reason_pm_full_gc)
16941     {
16942         assert (settings.condemned_generation == max_generation);
16943         assert (pm_trigger_full_gc);
16944         pm_trigger_full_gc = false;
16945
16946         dprintf (GTC_LOG, ("PM triggered full GC done"));
16947     }
16948 }
16949
16950 void gc_heap::garbage_collect_pm_full_gc()
16951 {
16952     assert (settings.condemned_generation == max_generation);
16953     assert (settings.reason == reason_pm_full_gc);
16954     assert (!settings.concurrent);
16955     gc1();
16956 }
16957
16958 void gc_heap::garbage_collect (int n)
16959 {
16960     //reset the number of alloc contexts
16961     alloc_contexts_used = 0;
16962
16963     fix_allocation_contexts (TRUE);
16964 #ifdef MULTIPLE_HEAPS
16965 #ifdef JOIN_STATS
16966     gc_t_join.start_ts(this);
16967 #endif //JOIN_STATS
16968     clear_gen0_bricks();
16969 #endif //MULTIPLE_HEAPS
16970
16971     if ((settings.pause_mode == pause_no_gc) && current_no_gc_region_info.minimal_gc_p)
16972     {
16973 #ifdef MULTIPLE_HEAPS
16974         gc_t_join.join(this, gc_join_minimal_gc);
16975         if (gc_t_join.joined())
16976         {
16977 #endif //MULTIPLE_HEAPS
16978
16979 #ifdef MULTIPLE_HEAPS
16980             // this is serialized because we need to get a segment
16981             for (int i = 0; i < n_heaps; i++)
16982             {
16983                 if (!(g_heaps[i]->expand_soh_with_minimal_gc()))
16984                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16985             }
16986 #else
16987             if (!expand_soh_with_minimal_gc())
16988                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16989 #endif //MULTIPLE_HEAPS
16990
16991             update_collection_counts_for_no_gc();
16992
16993 #ifdef MULTIPLE_HEAPS
16994             gc_t_join.restart();
16995         }
16996 #endif //MULTIPLE_HEAPS
16997
16998         goto done;
16999     }
17000
17001     init_records();
17002
17003     settings.reason = gc_trigger_reason;
17004 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
17005     num_pinned_objects = 0;
17006 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
17007
17008 #ifdef STRESS_HEAP
17009     if (settings.reason == reason_gcstress)
17010     {
17011         settings.reason = reason_induced;
17012         settings.stress_induced = TRUE;
17013     }
17014 #endif // STRESS_HEAP
17015
17016 #ifdef MULTIPLE_HEAPS
17017     //align all heaps on the max generation to condemn
17018     dprintf (3, ("Joining for max generation to condemn"));
17019     condemned_generation_num = generation_to_condemn (n, 
17020                                                     &blocking_collection, 
17021                                                     &elevation_requested, 
17022                                                     FALSE);
17023     gc_t_join.join(this, gc_join_generation_determined);
17024     if (gc_t_join.joined())
17025 #endif //MULTIPLE_HEAPS
17026     {
17027 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
17028         //delete old slots from the segment table
17029         seg_table->delete_old_slots();
17030 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
17031
17032 #ifdef MULTIPLE_HEAPS
17033         for (int i = 0; i < n_heaps; i++)
17034         {
17035             gc_heap* hp = g_heaps[i];
17036             // check for card table growth
17037             if (g_gc_card_table != hp->card_table)
17038                 hp->copy_brick_card_table();
17039
17040             hp->rearrange_large_heap_segments();
17041 #ifdef BACKGROUND_GC
17042             hp->background_delay_delete_loh_segments();
17043             if (!recursive_gc_sync::background_running_p())
17044                 hp->rearrange_small_heap_segments();
17045 #endif //BACKGROUND_GC
17046         }
17047 #else //MULTIPLE_HEAPS
17048         if (g_gc_card_table != card_table)
17049             copy_brick_card_table();
17050
17051         rearrange_large_heap_segments();
17052 #ifdef BACKGROUND_GC
17053         background_delay_delete_loh_segments();
17054         if (!recursive_gc_sync::background_running_p())
17055             rearrange_small_heap_segments();
17056 #endif //BACKGROUND_GC
17057 #endif //MULTIPLE_HEAPS
17058
17059     BOOL should_evaluate_elevation = TRUE;
17060     BOOL should_do_blocking_collection = FALSE;
17061
17062 #ifdef MULTIPLE_HEAPS
17063     int gen_max = condemned_generation_num;
17064     for (int i = 0; i < n_heaps; i++)
17065     {
17066         if (gen_max < g_heaps[i]->condemned_generation_num)
17067             gen_max = g_heaps[i]->condemned_generation_num;
17068         if (should_evaluate_elevation && !(g_heaps[i]->elevation_requested))
17069             should_evaluate_elevation = FALSE;
17070         if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection))
17071             should_do_blocking_collection = TRUE;
17072     }
17073
17074     settings.condemned_generation = gen_max;
17075 #else //MULTIPLE_HEAPS
17076     settings.condemned_generation = generation_to_condemn (n, 
17077                                                         &blocking_collection, 
17078                                                         &elevation_requested, 
17079                                                         FALSE);
17080     should_evaluate_elevation = elevation_requested;
17081     should_do_blocking_collection = blocking_collection;
17082 #endif //MULTIPLE_HEAPS
17083
17084     settings.condemned_generation = joined_generation_to_condemn (
17085                                         should_evaluate_elevation,
17086                                         n,
17087                                         settings.condemned_generation,
17088                                         &should_do_blocking_collection
17089                                         STRESS_HEAP_ARG(n)
17090                                         );
17091
17092     STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10, 
17093             "condemned generation num: %d\n", settings.condemned_generation);
17094
17095     record_gcs_during_no_gc();
17096
17097     if (settings.condemned_generation > 1)
17098         settings.promotion = TRUE;
17099
17100 #ifdef HEAP_ANALYZE
17101     // At this point we've decided what generation is condemned
17102     // See if we've been requested to analyze survivors after the mark phase
17103     if (GCToEEInterface::AnalyzeSurvivorsRequested(settings.condemned_generation))
17104     {
17105         heap_analyze_enabled = TRUE;
17106     }
17107 #endif // HEAP_ANALYZE
17108
17109         GCToEEInterface::DiagGCStart(settings.condemned_generation, settings.reason == reason_induced);
17110
17111 #ifdef BACKGROUND_GC
17112         if ((settings.condemned_generation == max_generation) &&
17113             (recursive_gc_sync::background_running_p()))
17114         {
17115             //TODO BACKGROUND_GC If we just wait for the end of gc, it won't work
17116             // because we have to collect 0 and 1 properly
17117             // in particular, the allocation contexts are gone.
17118             // For now, it is simpler to collect max_generation-1
17119             settings.condemned_generation = max_generation - 1;
17120             dprintf (GTC_LOG, ("bgc - 1 instead of 2"));
17121         }
17122
17123         if ((settings.condemned_generation == max_generation) &&
17124             (should_do_blocking_collection == FALSE) &&
17125             gc_can_use_concurrent &&
17126             !temp_disable_concurrent_p &&                 
17127             ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)))
17128         {
17129             keep_bgc_threads_p = TRUE;
17130             c_write (settings.concurrent,  TRUE);
17131         }
17132 #endif //BACKGROUND_GC
17133
17134         settings.gc_index = (uint32_t)dd_collection_count (dynamic_data_of (0)) + 1;
17135
17136         // Call the EE for start of GC work
17137         // just one thread for MP GC
17138         GCToEEInterface::GcStartWork (settings.condemned_generation,
17139                                 max_generation);            
17140
17141         // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to
17142         // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire
17143         // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be
17144         // fired in gc1.
17145         do_pre_gc();
17146
17147 #ifdef MULTIPLE_HEAPS
17148         gc_start_event.Reset();
17149         //start all threads on the roots.
17150         dprintf(3, ("Starting all gc threads for gc"));
17151         gc_t_join.restart();
17152 #endif //MULTIPLE_HEAPS
17153     }
17154
17155         descr_generations (TRUE);
17156
17157 #ifdef VERIFY_HEAP
17158     if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
17159        !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_POST_GC_ONLY))
17160     {
17161         verify_heap (TRUE);
17162     }
17163     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK)
17164         checkGCWriteBarrier();
17165
17166 #endif // VERIFY_HEAP
17167
17168 #ifdef BACKGROUND_GC
17169     if (settings.concurrent)
17170     {
17171         // We need to save the settings because we'll need to restore it after each FGC.
17172         assert (settings.condemned_generation == max_generation);
17173         settings.compaction = FALSE;
17174         saved_bgc_settings = settings;
17175
17176 #ifdef MULTIPLE_HEAPS
17177         if (heap_number == 0)
17178         {
17179             for (int i = 0; i < n_heaps; i++)
17180             {
17181                 prepare_bgc_thread (g_heaps[i]);
17182             }
17183             dprintf (2, ("setting bgc_threads_sync_event"));
17184             bgc_threads_sync_event.Set();
17185         }
17186         else
17187         {
17188             bgc_threads_sync_event.Wait(INFINITE, FALSE);
17189             dprintf (2, ("bgc_threads_sync_event is signalled"));
17190         }
17191 #else
17192         prepare_bgc_thread(0);
17193 #endif //MULTIPLE_HEAPS
17194
17195 #ifdef MULTIPLE_HEAPS
17196         gc_t_join.join(this, gc_join_start_bgc);
17197         if (gc_t_join.joined())
17198 #endif //MULTIPLE_HEAPS
17199         {
17200             do_concurrent_p = TRUE;
17201             do_ephemeral_gc_p = FALSE;
17202 #ifdef MULTIPLE_HEAPS
17203             dprintf(2, ("Joined to perform a background GC"));
17204
17205             for (int i = 0; i < n_heaps; i++)
17206             {
17207                 gc_heap* hp = g_heaps[i];
17208                 if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array))
17209                 {
17210                     do_concurrent_p = FALSE;
17211                     break;
17212                 }
17213                 else
17214                 {
17215                     hp->background_saved_lowest_address = hp->lowest_address;
17216                     hp->background_saved_highest_address = hp->highest_address;
17217                 }
17218             }
17219 #else
17220             do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array));
17221             if (do_concurrent_p)
17222             {
17223                 background_saved_lowest_address = lowest_address;
17224                 background_saved_highest_address = highest_address;
17225             }
17226 #endif //MULTIPLE_HEAPS
17227
17228             if (do_concurrent_p)
17229             {
17230 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
17231                 SoftwareWriteWatch::EnableForGCHeap();
17232 #endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
17233
17234 #ifdef MULTIPLE_HEAPS
17235                 for (int i = 0; i < n_heaps; i++)
17236                     g_heaps[i]->current_bgc_state = bgc_initialized;
17237 #else
17238                 current_bgc_state = bgc_initialized;
17239 #endif //MULTIPLE_HEAPS
17240
17241                 int gen = check_for_ephemeral_alloc();
17242                 // always do a gen1 GC before we start BGC. 
17243                 // This is temporary for testing purpose.
17244                 //int gen = max_generation - 1;
17245                 dont_restart_ee_p = TRUE;
17246                 if (gen == -1)
17247                 {
17248                     // If we decide to not do a GC before the BGC we need to 
17249                     // restore the gen0 alloc context.
17250 #ifdef MULTIPLE_HEAPS
17251                     for (int i = 0; i < n_heaps; i++)
17252                     {
17253                         generation_allocation_pointer (g_heaps[i]->generation_of (0)) =  0;
17254                         generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0;
17255                     }
17256 #else
17257                     generation_allocation_pointer (youngest_generation) =  0;
17258                     generation_allocation_limit (youngest_generation) = 0;
17259 #endif //MULTIPLE_HEAPS
17260                 }
17261                 else
17262                 {
17263                     do_ephemeral_gc_p = TRUE;
17264
17265                     settings.init_mechanisms();
17266                     settings.condemned_generation = gen;
17267                     settings.gc_index = (size_t)dd_collection_count (dynamic_data_of (0)) + 2;
17268                     do_pre_gc();
17269
17270                     // TODO BACKGROUND_GC need to add the profiling stuff here.
17271                     dprintf (GTC_LOG, ("doing gen%d before doing a bgc", gen));
17272                 }
17273
17274                 //clear the cards so they don't bleed in gen 1 during collection
17275                 // shouldn't this always be done at the beginning of any GC?
17276                 //clear_card_for_addresses (
17277                 //    generation_allocation_start (generation_of (0)),
17278                 //    heap_segment_allocated (ephemeral_heap_segment));
17279
17280                 if (!do_ephemeral_gc_p)
17281                 {
17282                     do_background_gc();
17283                 }
17284             }
17285             else
17286             {
17287                 settings.compaction = TRUE;
17288                 c_write (settings.concurrent, FALSE);
17289             }
17290
17291 #ifdef MULTIPLE_HEAPS
17292             gc_t_join.restart();
17293 #endif //MULTIPLE_HEAPS
17294         }
17295
17296         if (do_concurrent_p)
17297         {
17298             // At this point we are sure we'll be starting a BGC, so save its per heap data here.
17299             // global data is only calculated at the end of the GC so we don't need to worry about
17300             // FGCs overwriting it.
17301             memset (&bgc_data_per_heap, 0, sizeof (bgc_data_per_heap));
17302             memcpy (&bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap));
17303
17304             if (do_ephemeral_gc_p)
17305             {
17306                 dprintf (2, ("GC threads running, doing gen%d GC", settings.condemned_generation));
17307
17308                 gen_to_condemn_reasons.init();
17309                 gen_to_condemn_reasons.set_condition (gen_before_bgc);
17310                 gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons);
17311                 gc1();
17312 #ifdef MULTIPLE_HEAPS
17313                 gc_t_join.join(this, gc_join_bgc_after_ephemeral);
17314                 if (gc_t_join.joined())
17315 #endif //MULTIPLE_HEAPS
17316                 {
17317 #ifdef MULTIPLE_HEAPS
17318                     do_post_gc();
17319 #endif //MULTIPLE_HEAPS
17320                     settings = saved_bgc_settings;
17321                     assert (settings.concurrent);
17322
17323                     do_background_gc();
17324
17325 #ifdef MULTIPLE_HEAPS
17326                     gc_t_join.restart();
17327 #endif //MULTIPLE_HEAPS
17328                 }
17329             }
17330         }
17331         else
17332         {
17333             dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC"));
17334             gc1();
17335         }
17336     }
17337     else
17338 #endif //BACKGROUND_GC
17339     {
17340         gc1();
17341     }
17342 #ifndef MULTIPLE_HEAPS
17343     allocation_running_time = (size_t)GCToOSInterface::GetLowPrecisionTimeStamp();
17344     allocation_running_amount = dd_new_allocation (dynamic_data_of (0));
17345     fgn_last_alloc = dd_new_allocation (dynamic_data_of (0));
17346 #endif //MULTIPLE_HEAPS
17347
17348 done:
17349     if (settings.pause_mode == pause_no_gc)
17350         allocate_for_no_gc_after_gc();
17351
17352 }
17353
17354 #define mark_stack_empty_p() (mark_stack_base == mark_stack_tos)
17355
17356 inline
17357 size_t& gc_heap::promoted_bytes(int thread)
17358 {
17359 #ifdef MULTIPLE_HEAPS
17360     return g_promoted [thread*16];
17361 #else //MULTIPLE_HEAPS
17362     UNREFERENCED_PARAMETER(thread);
17363     return g_promoted;
17364 #endif //MULTIPLE_HEAPS
17365 }
17366
17367 #ifdef INTERIOR_POINTERS
17368 heap_segment* gc_heap::find_segment (uint8_t* interior, BOOL small_segment_only_p)
17369 {
17370 #ifdef SEG_MAPPING_TABLE
17371     heap_segment* seg = seg_mapping_table_segment_of (interior);
17372     if (seg)
17373     {
17374         if (small_segment_only_p && heap_segment_loh_p (seg))
17375             return 0;
17376     }
17377     return seg;
17378 #else //SEG_MAPPING_TABLE
17379 #ifdef MULTIPLE_HEAPS
17380     for (int i = 0; i < gc_heap::n_heaps; i++)
17381     {
17382         gc_heap* h = gc_heap::g_heaps [i];
17383         hs = h->find_segment_per_heap (o, small_segment_only_p);
17384         if (hs)
17385         {
17386             break;
17387         }        
17388     }
17389 #else
17390     {
17391         gc_heap* h = pGenGCHeap;
17392         hs = h->find_segment_per_heap (o, small_segment_only_p);
17393     }
17394 #endif //MULTIPLE_HEAPS
17395 #endif //SEG_MAPPING_TABLE
17396 }
17397
17398 heap_segment* gc_heap::find_segment_per_heap (uint8_t* interior, BOOL small_segment_only_p)
17399 {
17400 #ifdef SEG_MAPPING_TABLE
17401     return find_segment (interior, small_segment_only_p);
17402 #else //SEG_MAPPING_TABLE
17403     if (in_range_for_segment (interior, ephemeral_heap_segment))
17404     {
17405         return ephemeral_heap_segment;
17406     }
17407     else
17408     {
17409         heap_segment* found_seg = 0;
17410
17411         {
17412             heap_segment* seg = generation_start_segment (generation_of (max_generation));
17413             do
17414             {
17415                 if (in_range_for_segment (interior, seg))
17416                 {
17417                     found_seg = seg;
17418                     goto end_find_segment;
17419                 }
17420
17421             } while ((seg = heap_segment_next (seg)) != 0);
17422         }
17423         if (!small_segment_only_p)
17424         {
17425 #ifdef BACKGROUND_GC
17426             {
17427                 ptrdiff_t delta = 0;
17428                 heap_segment* seg = segment_of (interior, delta);
17429                 if (seg && in_range_for_segment (interior, seg))
17430                 {
17431                     found_seg = seg;
17432                 }
17433                 goto end_find_segment;
17434             }
17435 #else //BACKGROUND_GC
17436             heap_segment* seg = generation_start_segment (generation_of (max_generation+1));
17437             do
17438             {
17439                 if (in_range_for_segment(interior, seg))
17440                 {
17441                     found_seg = seg;
17442                     goto end_find_segment;
17443                 }
17444
17445             } while ((seg = heap_segment_next (seg)) != 0);
17446 #endif //BACKGROUND_GC
17447         }
17448 end_find_segment:
17449
17450         return found_seg;
17451     }
17452 #endif //SEG_MAPPING_TABLE
17453 }
17454 #endif //INTERIOR_POINTERS
17455
17456 #if !defined(_DEBUG) && !defined(__GNUC__)
17457 inline // This causes link errors if global optimization is off
17458 #endif //!_DEBUG && !__GNUC__
17459 gc_heap* gc_heap::heap_of (uint8_t* o)
17460 {
17461 #ifdef MULTIPLE_HEAPS
17462     if (o == 0)
17463         return g_heaps [0];
17464 #ifdef SEG_MAPPING_TABLE
17465     gc_heap* hp = seg_mapping_table_heap_of (o);
17466     return (hp ? hp : g_heaps[0]);
17467 #else //SEG_MAPPING_TABLE
17468     ptrdiff_t delta = 0;
17469     heap_segment* seg = segment_of (o, delta);
17470     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17471 #endif //SEG_MAPPING_TABLE
17472 #else //MULTIPLE_HEAPS
17473     UNREFERENCED_PARAMETER(o);
17474     return __this;
17475 #endif //MULTIPLE_HEAPS
17476 }
17477
17478 inline
17479 gc_heap* gc_heap::heap_of_gc (uint8_t* o)
17480 {
17481 #ifdef MULTIPLE_HEAPS
17482     if (o == 0)
17483         return g_heaps [0];
17484 #ifdef SEG_MAPPING_TABLE
17485     gc_heap* hp = seg_mapping_table_heap_of_gc (o);
17486     return (hp ? hp : g_heaps[0]);
17487 #else //SEG_MAPPING_TABLE
17488     ptrdiff_t delta = 0;
17489     heap_segment* seg = segment_of (o, delta);
17490     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17491 #endif //SEG_MAPPING_TABLE
17492 #else //MULTIPLE_HEAPS
17493     UNREFERENCED_PARAMETER(o);
17494     return __this;
17495 #endif //MULTIPLE_HEAPS
17496 }
17497
17498 #ifdef INTERIOR_POINTERS
17499 // will find all heap objects (large and small)
17500 uint8_t* gc_heap::find_object (uint8_t* interior, uint8_t* low)
17501 {
17502     if (!gen0_bricks_cleared)
17503     {
17504 #ifdef MULTIPLE_HEAPS
17505         assert (!"Should have already been done in server GC");
17506 #endif //MULTIPLE_HEAPS
17507         gen0_bricks_cleared = TRUE;
17508         //initialize brick table for gen 0
17509         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
17510              b < brick_of (align_on_brick
17511                            (heap_segment_allocated (ephemeral_heap_segment)));
17512              b++)
17513         {
17514             set_brick (b, -1);
17515         }
17516     }
17517 #ifdef FFIND_OBJECT
17518     //indicate that in the future this needs to be done during allocation
17519 #ifdef MULTIPLE_HEAPS
17520     gen0_must_clear_bricks = FFIND_DECAY*gc_heap::n_heaps;
17521 #else
17522     gen0_must_clear_bricks = FFIND_DECAY;
17523 #endif //MULTIPLE_HEAPS
17524 #endif //FFIND_OBJECT
17525
17526     int brick_entry = get_brick_entry(brick_of (interior));
17527     if (brick_entry == 0)
17528     {
17529         // this is a pointer to a large object
17530         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17531         if (seg
17532 #ifdef FEATURE_CONSERVATIVE_GC
17533             && (GCConfig::GetConservativeGC() || interior <= heap_segment_allocated(seg))
17534 #endif
17535             )
17536         {
17537             // If interior falls within the first free object at the beginning of a generation,
17538             // we don't have brick entry for it, and we may incorrectly treat it as on large object heap.
17539             int align_const = get_alignment_constant (heap_segment_read_only_p (seg)
17540 #ifdef FEATURE_CONSERVATIVE_GC
17541                                                        || (GCConfig::GetConservativeGC() && !heap_segment_loh_p (seg))
17542 #endif
17543                                                       );
17544             //int align_const = get_alignment_constant (heap_segment_read_only_p (seg));
17545             assert (interior < heap_segment_allocated (seg));
17546
17547             uint8_t* o = heap_segment_mem (seg);
17548             while (o < heap_segment_allocated (seg))
17549             {
17550                 uint8_t* next_o = o + Align (size (o), align_const);
17551                 assert (next_o > o);
17552                 if ((o <= interior) && (interior < next_o))
17553                 return o;
17554                 o = next_o;
17555             }
17556             return 0;
17557         }
17558         else
17559         {
17560             return 0;
17561         }
17562     }
17563     else if (interior >= low)
17564     {
17565         heap_segment* seg = find_segment_per_heap (interior, TRUE);
17566         if (seg)
17567         {
17568 #ifdef FEATURE_CONSERVATIVE_GC
17569             if (interior >= heap_segment_allocated (seg))
17570                 return 0;
17571 #else
17572             assert (interior < heap_segment_allocated (seg));
17573 #endif
17574             uint8_t* o = find_first_object (interior, heap_segment_mem (seg));
17575             return o;
17576         }
17577         else
17578             return 0;
17579     }
17580     else
17581         return 0;
17582 }
17583
17584 uint8_t*
17585 gc_heap::find_object_for_relocation (uint8_t* interior, uint8_t* low, uint8_t* high)
17586 {
17587     uint8_t* old_address = interior;
17588     if (!((old_address >= low) && (old_address < high)))
17589         return 0;
17590     uint8_t* plug = 0;
17591     size_t  brick = brick_of (old_address);
17592     int    brick_entry =  brick_table [ brick ];
17593     if (brick_entry != 0)
17594     {
17595     retry:
17596         {
17597             while (brick_entry < 0)
17598             {
17599                 brick = (brick + brick_entry);
17600                 brick_entry =  brick_table [ brick ];
17601             }
17602             uint8_t* old_loc = old_address;
17603             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
17604                                       old_loc);
17605             if (node <= old_loc)
17606                 plug = node;
17607             else
17608             {
17609                 brick = brick - 1;
17610                 brick_entry =  brick_table [ brick ];
17611                 goto retry;
17612             }
17613
17614         }
17615         assert (plug);
17616         //find the object by going along the plug
17617         uint8_t* o = plug;
17618         while (o <= interior)
17619         {
17620             uint8_t* next_o = o + Align (size (o));
17621             assert (next_o > o);
17622             if (next_o > interior)
17623             {
17624                 break;
17625             }
17626             o = next_o;
17627         }
17628         assert ((o <= interior) && ((o + Align (size (o))) > interior));
17629         return o;
17630     }
17631     else
17632     {
17633         // this is a pointer to a large object
17634         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17635         if (seg)
17636         {
17637             assert (interior < heap_segment_allocated (seg));
17638
17639             uint8_t* o = heap_segment_mem (seg);
17640             while (o < heap_segment_allocated (seg))
17641             {
17642                 uint8_t* next_o = o + Align (size (o));
17643                 assert (next_o > o);
17644                 if ((o < interior) && (interior < next_o))
17645                 return o;
17646                 o = next_o;
17647             }
17648             return 0;
17649         }
17650         else
17651             {
17652             return 0;
17653         }
17654     }
17655 }
17656 #else //INTERIOR_POINTERS
17657 inline
17658 uint8_t* gc_heap::find_object (uint8_t* o, uint8_t* low)
17659 {
17660     return o;
17661 }
17662 #endif //INTERIOR_POINTERS
17663
17664 #ifdef MULTIPLE_HEAPS
17665
17666 #ifdef MARK_LIST
17667 #ifdef GC_CONFIG_DRIVEN
17668 #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;} else {mark_list_index++;}}
17669 #else //GC_CONFIG_DRIVEN
17670 #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;}}
17671 #endif //GC_CONFIG_DRIVEN
17672 #else //MARK_LIST
17673 #define m_boundary(o) {}
17674 #endif //MARK_LIST
17675
17676 #define m_boundary_fullgc(o) {}
17677
17678 #else //MULTIPLE_HEAPS
17679
17680 #ifdef MARK_LIST
17681 #ifdef GC_CONFIG_DRIVEN
17682 #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;} else {mark_list_index++;} if (slow > o) slow = o; if (shigh < o) shigh = o;}
17683 #else
17684 #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;}if (slow > o) slow = o; if (shigh < o) shigh = o;}
17685 #endif //GC_CONFIG_DRIVEN
17686 #else //MARK_LIST
17687 #define m_boundary(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17688 #endif //MARK_LIST
17689
17690 #define m_boundary_fullgc(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17691
17692 #endif //MULTIPLE_HEAPS
17693
17694 #define method_table(o) ((CObjectHeader*)(o))->GetMethodTable()
17695
17696 inline
17697 BOOL gc_heap::gc_mark1 (uint8_t* o)
17698 {
17699     BOOL marked = !marked (o);
17700     set_marked (o);
17701     dprintf (3, ("*%Ix*, newly marked: %d", (size_t)o, marked));
17702     return marked;
17703 }
17704
17705 inline
17706 BOOL gc_heap::gc_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17707 {
17708     BOOL marked = FALSE;
17709     if ((o >= low) && (o < high))
17710         marked = gc_mark1 (o);
17711 #ifdef MULTIPLE_HEAPS
17712     else if (o)
17713     {
17714         //find the heap
17715         gc_heap* hp = heap_of_gc (o);
17716         assert (hp);
17717         if ((o >= hp->gc_low) && (o < hp->gc_high))
17718             marked = gc_mark1 (o);
17719     }
17720 #ifdef SNOOP_STATS
17721     snoop_stat.objects_checked_count++;
17722
17723     if (marked)
17724     {
17725         snoop_stat.objects_marked_count++;
17726     }
17727     if (!o)
17728     {
17729         snoop_stat.zero_ref_count++;
17730     }
17731
17732 #endif //SNOOP_STATS
17733 #endif //MULTIPLE_HEAPS
17734     return marked;
17735 }
17736
17737 #ifdef BACKGROUND_GC
17738
17739 inline
17740 BOOL gc_heap::background_marked (uint8_t* o)
17741 {
17742     return mark_array_marked (o);
17743 }
17744 inline
17745 BOOL gc_heap::background_mark1 (uint8_t* o)
17746 {
17747     BOOL to_mark = !mark_array_marked (o);
17748
17749     dprintf (3, ("b*%Ix*b(%d)", (size_t)o, (to_mark ? 1 : 0)));
17750     if (to_mark)
17751     {
17752         mark_array_set_marked (o);
17753         dprintf (4, ("n*%Ix*n", (size_t)o));
17754         return TRUE;
17755     }
17756     else
17757         return FALSE;
17758 }
17759
17760 // TODO: we could consider filtering out NULL's here instead of going to 
17761 // look for it on other heaps
17762 inline
17763 BOOL gc_heap::background_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17764 {
17765     BOOL marked = FALSE;
17766     if ((o >= low) && (o < high))
17767         marked = background_mark1 (o);
17768 #ifdef MULTIPLE_HEAPS
17769     else if (o)
17770     {
17771         //find the heap
17772         gc_heap* hp = heap_of (o);
17773         assert (hp);
17774         if ((o >= hp->background_saved_lowest_address) && (o < hp->background_saved_highest_address))
17775             marked = background_mark1 (o);
17776     }
17777 #endif //MULTIPLE_HEAPS
17778     return marked;
17779 }
17780
17781 #endif //BACKGROUND_GC
17782
17783 inline
17784 uint8_t* gc_heap::next_end (heap_segment* seg, uint8_t* f)
17785 {
17786     if (seg == ephemeral_heap_segment)
17787         return  f;
17788     else
17789         return  heap_segment_allocated (seg);
17790 }
17791
17792 #define new_start() {if (ppstop <= start) {break;} else {parm = start}}
17793 #define ignore_start 0
17794 #define use_start 1
17795
17796 #define go_through_object(mt,o,size,parm,start,start_useful,limit,exp)      \
17797 {                                                                           \
17798     CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt));           \
17799     CGCDescSeries* cur = map->GetHighestSeries();                           \
17800     ptrdiff_t cnt = (ptrdiff_t) map->GetNumSeries();                        \
17801                                                                             \
17802     if (cnt >= 0)                                                           \
17803     {                                                                       \
17804         CGCDescSeries* last = map->GetLowestSeries();                       \
17805         uint8_t** parm = 0;                                                 \
17806         do                                                                  \
17807         {                                                                   \
17808             assert (parm <= (uint8_t**)((o) + cur->GetSeriesOffset()));     \
17809             parm = (uint8_t**)((o) + cur->GetSeriesOffset());               \
17810             uint8_t** ppstop =                                              \
17811                 (uint8_t**)((uint8_t*)parm + cur->GetSeriesSize() + (size));\
17812             if (!start_useful || (uint8_t*)ppstop > (start))                \
17813             {                                                               \
17814                 if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);\
17815                 while (parm < ppstop)                                       \
17816                 {                                                           \
17817                    {exp}                                                    \
17818                    parm++;                                                  \
17819                 }                                                           \
17820             }                                                               \
17821             cur--;                                                          \
17822                                                                             \
17823         } while (cur >= last);                                              \
17824     }                                                                       \
17825     else                                                                    \
17826     {                                                                       \
17827         /* Handle the repeating case - array of valuetypes */               \
17828         uint8_t** parm = (uint8_t**)((o) + cur->startoffset);               \
17829         if (start_useful && start > (uint8_t*)parm)                         \
17830         {                                                                   \
17831             ptrdiff_t cs = mt->RawGetComponentSize();                         \
17832             parm = (uint8_t**)((uint8_t*)parm + (((start) - (uint8_t*)parm)/cs)*cs); \
17833         }                                                                   \
17834         while ((uint8_t*)parm < ((o)+(size)-plug_skew))                     \
17835         {                                                                   \
17836             for (ptrdiff_t __i = 0; __i > cnt; __i--)                         \
17837             {                                                               \
17838                 HALF_SIZE_T skip =  cur->val_serie[__i].skip;               \
17839                 HALF_SIZE_T nptrs = cur->val_serie[__i].nptrs;              \
17840                 uint8_t** ppstop = parm + nptrs;                            \
17841                 if (!start_useful || (uint8_t*)ppstop > (start))            \
17842                 {                                                           \
17843                     if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);      \
17844                     do                                                      \
17845                     {                                                       \
17846                        {exp}                                                \
17847                        parm++;                                              \
17848                     } while (parm < ppstop);                                \
17849                 }                                                           \
17850                 parm = (uint8_t**)((uint8_t*)ppstop + skip);                \
17851             }                                                               \
17852         }                                                                   \
17853     }                                                                       \
17854 }
17855
17856 #define go_through_object_nostart(mt,o,size,parm,exp) {go_through_object(mt,o,size,parm,o,ignore_start,(o + size),exp); }
17857
17858 // 1 thing to note about this macro:
17859 // 1) you can use *parm safely but in general you don't want to use parm 
17860 // because for the collectible types it's not an address on the managed heap.
17861 #ifndef COLLECTIBLE_CLASS
17862 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17863 {                                                                           \
17864     if (header(o)->ContainsPointers())                                      \
17865     {                                                                       \
17866         go_through_object_nostart(mt,o,size,parm,exp);                      \
17867     }                                                                       \
17868 }
17869 #else //COLLECTIBLE_CLASS
17870 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17871 {                                                                           \
17872     if (header(o)->Collectible())                                           \
17873     {                                                                       \
17874         uint8_t* class_obj = get_class_object (o);                             \
17875         uint8_t** parm = &class_obj;                                           \
17876         do {exp} while (false);                                             \
17877     }                                                                       \
17878     if (header(o)->ContainsPointers())                                      \
17879     {                                                                       \
17880         go_through_object_nostart(mt,o,size,parm,exp);                      \
17881     }                                                                       \
17882 }
17883 #endif //COLLECTIBLE_CLASS
17884
17885 // This starts a plug. But mark_stack_tos isn't increased until set_pinned_info is called.
17886 void gc_heap::enque_pinned_plug (uint8_t* plug,
17887                                  BOOL save_pre_plug_info_p, 
17888                                  uint8_t* last_object_in_last_plug)
17889 {
17890     if (mark_stack_array_length <= mark_stack_tos)
17891     {
17892         if (!grow_mark_stack (mark_stack_array, mark_stack_array_length, MARK_STACK_INITIAL_LENGTH))
17893         {
17894             // we don't want to continue here due to security
17895             // risks. This happens very rarely and fixing it in the
17896             // way so that we can continue is a bit involved and will
17897             // not be done in Dev10.
17898             GCToEEInterface::HandleFatalError(CORINFO_EXCEPTION_GC);
17899         }
17900     }
17901
17902     dprintf (3, ("enqueuing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d", 
17903         mark_stack_tos, &mark_stack_array[mark_stack_tos], plug, mark_stack_bos, last_object_in_last_plug, (save_pre_plug_info_p ? 1 : 0)));
17904     mark& m = mark_stack_array[mark_stack_tos];
17905     m.first = plug;
17906     // Must be set now because if we have a short object we'll need the value of saved_pre_p.
17907     m.saved_pre_p = save_pre_plug_info_p;
17908
17909     if (save_pre_plug_info_p)
17910     {
17911 #ifdef SHORT_PLUGS
17912         BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17913         if (is_padded)
17914             clear_plug_padded (last_object_in_last_plug);
17915 #endif //SHORT_PLUGS
17916         memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17917 #ifdef SHORT_PLUGS
17918         if (is_padded)
17919             set_plug_padded (last_object_in_last_plug);
17920 #endif //SHORT_PLUGS
17921
17922         memcpy (&(m.saved_pre_plug_reloc), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17923
17924         // If the last object in the last plug is too short, it requires special handling.
17925         size_t last_obj_size = plug - last_object_in_last_plug;
17926         if (last_obj_size < min_pre_pin_obj_size)
17927         {
17928             record_interesting_data_point (idp_pre_short);
17929 #ifdef SHORT_PLUGS
17930             if (is_padded)
17931                 record_interesting_data_point (idp_pre_short_padded);
17932 #endif //SHORT_PLUGS
17933             dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!", 
17934                          last_object_in_last_plug, plug));
17935             // Need to set the short bit regardless of having refs or not because we need to 
17936             // indicate that this object is not walkable.
17937             m.set_pre_short();
17938
17939 #ifdef COLLECTIBLE_CLASS
17940             if (is_collectible (last_object_in_last_plug))
17941             {
17942                 m.set_pre_short_collectible();
17943             }
17944 #endif //COLLECTIBLE_CLASS
17945
17946             if (contain_pointers (last_object_in_last_plug))
17947             {
17948                 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17949
17950                 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17951                     {
17952                         size_t gap_offset = (((size_t)pval - (size_t)(plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17953                         dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17954                         m.set_pre_short_bit (gap_offset);
17955                     }
17956                 );
17957             }
17958         }
17959     }
17960
17961     m.saved_post_p = FALSE;
17962 }
17963
17964 void gc_heap::save_post_plug_info (uint8_t* last_pinned_plug, uint8_t* last_object_in_last_plug, uint8_t* post_plug)
17965 {
17966     UNREFERENCED_PARAMETER(last_pinned_plug);
17967
17968     mark& m = mark_stack_array[mark_stack_tos - 1];
17969     assert (last_pinned_plug == m.first);
17970     m.saved_post_plug_info_start = (uint8_t*)&(((plug_and_gap*)post_plug)[-1]);
17971
17972 #ifdef SHORT_PLUGS
17973     BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17974     if (is_padded)
17975         clear_plug_padded (last_object_in_last_plug);
17976 #endif //SHORT_PLUGS
17977     memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17978 #ifdef SHORT_PLUGS
17979     if (is_padded)
17980         set_plug_padded (last_object_in_last_plug);
17981 #endif //SHORT_PLUGS
17982
17983     memcpy (&(m.saved_post_plug_reloc), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17984
17985     // This is important - we need to clear all bits here except the last one.
17986     m.saved_post_p = TRUE;
17987
17988 #ifdef _DEBUG
17989     m.saved_post_plug_debug.gap = 1;
17990 #endif //_DEBUG
17991
17992     dprintf (3, ("PP %Ix has NP %Ix right after", last_pinned_plug, post_plug));
17993
17994     size_t last_obj_size = post_plug - last_object_in_last_plug;
17995     if (last_obj_size < min_pre_pin_obj_size)
17996     {
17997         dprintf (3, ("PP %Ix last obj %Ix is too short", last_pinned_plug, last_object_in_last_plug));
17998         record_interesting_data_point (idp_post_short);
17999 #ifdef SHORT_PLUGS
18000         if (is_padded)
18001             record_interesting_data_point (idp_post_short_padded);
18002 #endif //SHORT_PLUGS
18003         m.set_post_short();
18004 #if defined (_DEBUG) && defined (VERIFY_HEAP)
18005         verify_pinned_queue_p = TRUE;
18006 #endif // _DEBUG && VERIFY_HEAP
18007
18008 #ifdef COLLECTIBLE_CLASS
18009         if (is_collectible (last_object_in_last_plug))
18010         {
18011             m.set_post_short_collectible();
18012         }
18013 #endif //COLLECTIBLE_CLASS
18014
18015         if (contain_pointers (last_object_in_last_plug))
18016         {
18017             dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
18018
18019             // TODO: since we won't be able to walk this object in relocation, we still need to
18020             // take care of collectible assemblies here.
18021             go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
18022                 {
18023                     size_t gap_offset = (((size_t)pval - (size_t)(post_plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
18024                     dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
18025                     m.set_post_short_bit (gap_offset);
18026                 }
18027             );
18028         }
18029     }
18030 }
18031
18032 //#define PREFETCH
18033 #ifdef PREFETCH
18034 __declspec(naked) void __fastcall Prefetch(void* addr)
18035 {
18036    __asm {
18037        PREFETCHT0 [ECX]
18038         ret
18039     };
18040 }
18041 #else //PREFETCH
18042 inline void Prefetch (void* addr)
18043 {
18044     UNREFERENCED_PARAMETER(addr);
18045 }
18046 #endif //PREFETCH
18047 #ifdef MH_SC_MARK
18048 inline
18049 VOLATILE(uint8_t*)& gc_heap::ref_mark_stack (gc_heap* hp, int index)
18050 {
18051     return ((VOLATILE(uint8_t*)*)(hp->mark_stack_array))[index];
18052 }
18053
18054 #endif //MH_SC_MARK
18055
18056 #define stolen 2
18057 #define partial 1
18058 #define partial_object 3
18059 inline 
18060 uint8_t* ref_from_slot (uint8_t* r)
18061 {
18062     return (uint8_t*)((size_t)r & ~(stolen | partial));
18063 }
18064 inline
18065 BOOL stolen_p (uint8_t* r)
18066 {
18067     return (((size_t)r&2) && !((size_t)r&1));
18068 }
18069 inline 
18070 BOOL ready_p (uint8_t* r)
18071 {
18072     return ((size_t)r != 1);
18073 }
18074 inline
18075 BOOL partial_p (uint8_t* r)
18076 {
18077     return (((size_t)r&1) && !((size_t)r&2));
18078 }
18079 inline 
18080 BOOL straight_ref_p (uint8_t* r)
18081 {
18082     return (!stolen_p (r) && !partial_p (r));
18083 }
18084 inline 
18085 BOOL partial_object_p (uint8_t* r)
18086 {
18087     return (((size_t)r & partial_object) == partial_object);
18088 }
18089 inline
18090 BOOL ref_p (uint8_t* r)
18091 {
18092     return (straight_ref_p (r) || partial_object_p (r));
18093 }
18094
18095 void gc_heap::mark_object_simple1 (uint8_t* oo, uint8_t* start THREAD_NUMBER_DCL)
18096 {
18097     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_tos = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)mark_stack_array;
18098     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_limit = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)&mark_stack_array[mark_stack_array_length];
18099     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_base = mark_stack_tos;
18100 #ifdef SORT_MARK_STACK
18101     SERVER_SC_MARK_VOLATILE(uint8_t*)* sorted_tos = mark_stack_base;
18102 #endif //SORT_MARK_STACK
18103
18104     // If we are doing a full GC we don't use mark list anyway so use m_boundary_fullgc that doesn't 
18105     // update mark list.
18106     BOOL  full_p = (settings.condemned_generation == max_generation);
18107
18108     assert ((start >= oo) && (start < oo+size(oo)));
18109
18110 #ifndef MH_SC_MARK
18111     *mark_stack_tos = oo;
18112 #endif //!MH_SC_MARK
18113
18114     while (1)
18115     {
18116 #ifdef MULTIPLE_HEAPS
18117 #else  //MULTIPLE_HEAPS
18118         const int thread = 0;
18119 #endif //MULTIPLE_HEAPS
18120
18121         if (oo && ((size_t)oo != 4))
18122         {
18123             size_t s = 0; 
18124             if (stolen_p (oo))
18125             {
18126                 --mark_stack_tos;
18127                 goto next_level;
18128             }
18129             else if (!partial_p (oo) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
18130             {
18131                 BOOL overflow_p = FALSE;
18132
18133                 if (mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit  - 1))
18134                 {
18135                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18136                     if (mark_stack_tos + CGCDesc::GetNumPointers(method_table(oo), s, num_components) >= (mark_stack_limit - 1))
18137                     {
18138                         overflow_p = TRUE;
18139                     }
18140                 }
18141                 
18142                 if (overflow_p == FALSE)
18143                 {
18144                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18145
18146                     go_through_object_cl (method_table(oo), oo, s, ppslot,
18147                                           {
18148                                               uint8_t* o = *ppslot;
18149                                               Prefetch(o);
18150                                               if (gc_mark (o, gc_low, gc_high))
18151                                               {
18152                                                   if (full_p)
18153                                                   {
18154                                                       m_boundary_fullgc (o);
18155                                                   }
18156                                                   else
18157                                                   {
18158                                                       m_boundary (o);
18159                                                   }
18160                                                   size_t obj_size = size (o);
18161                                                   promoted_bytes (thread) += obj_size;
18162                                                   if (contain_pointers_or_collectible (o))
18163                                                   {
18164                                                       *(mark_stack_tos++) = o;
18165                                                   }
18166                                               }
18167                                           }
18168                         );
18169                 }
18170                 else
18171                 {
18172                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
18173                     min_overflow_address = min (min_overflow_address, oo);
18174                     max_overflow_address = max (max_overflow_address, oo);
18175                 }
18176             }
18177             else
18178             {
18179                 if (partial_p (oo))
18180                 {
18181                     start = ref_from_slot (oo);
18182                     oo = ref_from_slot (*(--mark_stack_tos));
18183                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
18184                     assert ((oo < start) && (start < (oo + size (oo))));
18185                 }
18186 #ifdef COLLECTIBLE_CLASS
18187                 else
18188                 {
18189                     // If there's a class object, push it now. We are guaranteed to have the slot since
18190                     // we just popped one object off.
18191                     if (is_collectible (oo))
18192                     {
18193                         uint8_t* class_obj = get_class_object (oo);
18194                         if (gc_mark (class_obj, gc_low, gc_high))
18195                         {
18196                             if (full_p)
18197                             {
18198                                 m_boundary_fullgc (class_obj);
18199                             }
18200                             else
18201                             {
18202                                 m_boundary (class_obj);
18203                             }
18204
18205                             size_t obj_size = size (class_obj);
18206                             promoted_bytes (thread) += obj_size;
18207                             *(mark_stack_tos++) = class_obj;
18208                             // The code below expects that the oo is still stored in the stack slot that was
18209                             // just popped and it "pushes" it back just by incrementing the mark_stack_tos. 
18210                             // But the class_obj has just overwritten that stack slot and so the oo needs to
18211                             // be stored to the new slot that's pointed to by the mark_stack_tos.
18212                             *mark_stack_tos = oo;
18213                         }
18214                     }
18215
18216                     if (!contain_pointers (oo))
18217                     {
18218                         goto next_level;
18219                     }
18220                 }
18221 #endif //COLLECTIBLE_CLASS
18222
18223                 s = size (oo);
18224                 
18225                 BOOL overflow_p = FALSE;
18226             
18227                 if (mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
18228                 {
18229                     overflow_p = TRUE;
18230                 }
18231                 if (overflow_p == FALSE)
18232                 {
18233                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18234
18235                     //push the object and its current 
18236                     SERVER_SC_MARK_VOLATILE(uint8_t*)* place = ++mark_stack_tos;
18237                     mark_stack_tos++;
18238 #ifdef MH_SC_MARK
18239                     *(place-1) = 0;
18240                     *(place) = (uint8_t*)partial;
18241 #endif //MH_SC_MARK
18242                     int i = num_partial_refs; 
18243                     uint8_t* ref_to_continue = 0;
18244
18245                     go_through_object (method_table(oo), oo, s, ppslot,
18246                                        start, use_start, (oo + s),
18247                                        {
18248                                            uint8_t* o = *ppslot;
18249                                            Prefetch(o);
18250                                            if (gc_mark (o, gc_low, gc_high))
18251                                            {
18252                                                 if (full_p)
18253                                                 {
18254                                                     m_boundary_fullgc (o);
18255                                                 }
18256                                                 else
18257                                                 {
18258                                                     m_boundary (o);
18259                                                 }
18260                                                 size_t obj_size = size (o);
18261                                                 promoted_bytes (thread) += obj_size;
18262                                                 if (contain_pointers_or_collectible (o))
18263                                                 {
18264                                                     *(mark_stack_tos++) = o;
18265                                                     if (--i == 0)
18266                                                     {
18267                                                         ref_to_continue = (uint8_t*)((size_t)(ppslot+1) | partial);
18268                                                         goto more_to_do;
18269                                                     }
18270
18271                                                 }
18272                                            }
18273
18274                                        }
18275                         );
18276                     //we are finished with this object
18277                     assert (ref_to_continue == 0);
18278 #ifdef MH_SC_MARK
18279                     assert ((*(place-1)) == (uint8_t*)0);
18280 #else //MH_SC_MARK
18281                     *(place-1) = 0;
18282 #endif //MH_SC_MARK
18283                     *place = 0; 
18284                     // shouldn't we decrease tos by 2 here??
18285
18286 more_to_do:
18287                     if (ref_to_continue)
18288                     {
18289                         //update the start
18290 #ifdef MH_SC_MARK
18291                         assert ((*(place-1)) == (uint8_t*)0);
18292                         *(place-1) = (uint8_t*)((size_t)oo | partial_object);
18293                         assert (((*place) == (uint8_t*)1) || ((*place) == (uint8_t*)2));
18294 #endif //MH_SC_MARK
18295                         *place = ref_to_continue;
18296                     }
18297                 }
18298                 else
18299                 {
18300                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
18301                     min_overflow_address = min (min_overflow_address, oo);
18302                     max_overflow_address = max (max_overflow_address, oo);
18303                 }
18304             }
18305 #ifdef SORT_MARK_STACK
18306             if (mark_stack_tos > sorted_tos + mark_stack_array_length/8)
18307             {
18308                 rqsort1 (sorted_tos, mark_stack_tos-1);
18309                 sorted_tos = mark_stack_tos-1;
18310             }
18311 #endif //SORT_MARK_STACK
18312         }
18313     next_level:
18314         if (!(mark_stack_empty_p()))
18315         {
18316             oo = *(--mark_stack_tos);
18317             start = oo;
18318
18319 #ifdef SORT_MARK_STACK
18320             sorted_tos = min ((size_t)sorted_tos, (size_t)mark_stack_tos);
18321 #endif //SORT_MARK_STACK
18322         }
18323         else
18324             break;
18325     }
18326 }
18327
18328 #ifdef MH_SC_MARK
18329 BOOL same_numa_node_p (int hn1, int hn2)
18330 {
18331     return (heap_select::find_numa_node_from_heap_no (hn1) == heap_select::find_numa_node_from_heap_no (hn2));
18332 }
18333
18334 int find_next_buddy_heap (int this_heap_number, int current_buddy, int n_heaps)
18335 {
18336     int hn = (current_buddy+1)%n_heaps;
18337     while (hn != current_buddy)
18338     {
18339         if ((this_heap_number != hn) && (same_numa_node_p (this_heap_number, hn)))
18340             return hn;
18341         hn = (hn+1)%n_heaps;
18342     }
18343     return current_buddy;
18344 }
18345
18346 void 
18347 gc_heap::mark_steal()
18348 {
18349     mark_stack_busy() = 0;
18350     //clear the mark stack in the snooping range
18351     for (int i = 0; i < max_snoop_level; i++)
18352     {
18353         ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
18354     }
18355
18356     //pick the next heap as our buddy
18357     int thpn = find_next_buddy_heap (heap_number, heap_number, n_heaps);
18358
18359 #ifdef SNOOP_STATS
18360         dprintf (SNOOP_LOG, ("(GC%d)heap%d: start snooping %d", settings.gc_index, heap_number, (heap_number+1)%n_heaps));
18361         uint32_t begin_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
18362 #endif //SNOOP_STATS
18363
18364     int idle_loop_count = 0; 
18365     int first_not_ready_level = 0;
18366
18367     while (1)
18368     {
18369         gc_heap* hp = g_heaps [thpn];
18370         int level = first_not_ready_level;
18371         first_not_ready_level = 0; 
18372
18373         while (check_next_mark_stack (hp) && (level < (max_snoop_level-1)))
18374         {
18375             idle_loop_count = 0; 
18376 #ifdef SNOOP_STATS
18377             snoop_stat.busy_count++;
18378             dprintf (SNOOP_LOG, ("heap%d: looking at next heap level %d stack contents: %Ix", 
18379                                  heap_number, level, (int)((uint8_t**)(hp->mark_stack_array))[level]));
18380 #endif //SNOOP_STATS
18381
18382             uint8_t* o = ref_mark_stack (hp, level);
18383
18384             uint8_t* start = o;
18385             if (ref_p (o))
18386             {
18387                 mark_stack_busy() = 1;
18388
18389                 BOOL success = TRUE;
18390                 uint8_t* next = (ref_mark_stack (hp, level+1));
18391                 if (ref_p (next))
18392                 {
18393                     if (((size_t)o > 4) && !partial_object_p (o))
18394                     {
18395                         //this is a normal object, not a partial mark tuple
18396                         //success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), 0, o)==o);
18397                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), (uint8_t*)4, o)==o);
18398 #ifdef SNOOP_STATS
18399                         snoop_stat.interlocked_count++;
18400                         if (success)
18401                             snoop_stat.normal_count++;
18402 #endif //SNOOP_STATS
18403                     }
18404                     else
18405                     {
18406                         //it is a stolen entry, or beginning/ending of a partial mark
18407                         level++;
18408 #ifdef SNOOP_STATS
18409                         snoop_stat.stolen_or_pm_count++;
18410 #endif //SNOOP_STATS
18411                         success = FALSE;
18412                     }
18413                 }
18414                 else if (stolen_p (next))
18415                 {
18416                     //ignore the stolen guy and go to the next level
18417                     success = FALSE;
18418                     level+=2;
18419 #ifdef SNOOP_STATS
18420                     snoop_stat.stolen_entry_count++;
18421 #endif //SNOOP_STATS
18422                 }
18423                 else
18424                 {
18425                     assert (partial_p (next));
18426                     start = ref_from_slot (next);
18427                     //re-read the object
18428                     o = ref_from_slot (ref_mark_stack (hp, level));
18429                     if (o && start)
18430                     {
18431                         //steal the object
18432                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level+1), (uint8_t*)stolen, next)==next);
18433 #ifdef SNOOP_STATS
18434                         snoop_stat.interlocked_count++;
18435                         if (success)
18436                         {
18437                             snoop_stat.partial_mark_parent_count++;                    
18438                         }
18439 #endif //SNOOP_STATS
18440                     }
18441                     else
18442                     {
18443                         // stack is not ready, or o is completely different from the last time we read from this stack level.
18444                         // go up 2 levels to steal children or totally unrelated objects.
18445                         success = FALSE;
18446                         if (first_not_ready_level == 0)
18447                         {
18448                             first_not_ready_level = level;
18449                         }
18450                         level+=2;
18451 #ifdef SNOOP_STATS
18452                         snoop_stat.pm_not_ready_count++;
18453 #endif //SNOOP_STATS                        
18454                     }
18455                 }
18456                 if (success)
18457                 {
18458
18459 #ifdef SNOOP_STATS
18460                     dprintf (SNOOP_LOG, ("heap%d: marking %Ix from %d [%d] tl:%dms",
18461                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18462                             (GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18463                     uint32_t start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
18464 #endif //SNOOP_STATS
18465
18466                     mark_object_simple1 (o, start, heap_number);
18467
18468 #ifdef SNOOP_STATS
18469                     dprintf (SNOOP_LOG, ("heap%d: done marking %Ix from %d [%d] %dms tl:%dms",
18470                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18471                             (GCToOSInterface::GetLowPrecisionTimeStamp()-start_tick),(GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18472 #endif //SNOOP_STATS
18473
18474                     mark_stack_busy() = 0;
18475
18476                     //clear the mark stack in snooping range
18477                     for (int i = 0; i < max_snoop_level; i++)
18478                     {
18479                         if (((uint8_t**)mark_stack_array)[i] != 0)
18480                         {
18481                             ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
18482 #ifdef SNOOP_STATS
18483                             snoop_stat.stack_bottom_clear_count++;
18484 #endif //SNOOP_STATS
18485                         }
18486                     }
18487
18488                     level = 0; 
18489                 }
18490                 mark_stack_busy() = 0;
18491             }
18492             else
18493             {
18494                 //slot is either partial or stolen
18495                 level++;
18496             }
18497         }
18498         if ((first_not_ready_level != 0) && hp->mark_stack_busy())
18499         {
18500             continue;
18501         } 
18502         if (!hp->mark_stack_busy())
18503         {
18504             first_not_ready_level = 0; 
18505             idle_loop_count++;
18506
18507             if ((idle_loop_count % (6) )==1)
18508             {
18509 #ifdef SNOOP_STATS
18510                 snoop_stat.switch_to_thread_count++;
18511 #endif //SNOOP_STATS
18512                 GCToOSInterface::Sleep(1);
18513             }
18514             int free_count = 1;
18515 #ifdef SNOOP_STATS
18516             snoop_stat.stack_idle_count++;
18517             //dprintf (SNOOP_LOG, ("heap%d: counting idle threads", heap_number));
18518 #endif //SNOOP_STATS
18519             for (int hpn = (heap_number+1)%n_heaps; hpn != heap_number;)
18520             {
18521                 if (!((g_heaps [hpn])->mark_stack_busy()))
18522                 {
18523                     free_count++;
18524 #ifdef SNOOP_STATS
18525                 dprintf (SNOOP_LOG, ("heap%d: %d idle", heap_number, free_count));
18526 #endif //SNOOP_STATS
18527                 }
18528                 else if (same_numa_node_p (hpn, heap_number) || ((idle_loop_count%1000))==999)
18529                 {
18530                     thpn = hpn;
18531                     break;
18532                 }
18533                 hpn = (hpn+1)%n_heaps;
18534                 YieldProcessor();
18535             }
18536             if (free_count == n_heaps)
18537             {
18538                 break;
18539             }
18540         }
18541     }
18542 }
18543
18544 inline
18545 BOOL gc_heap::check_next_mark_stack (gc_heap* next_heap)
18546 {
18547 #ifdef SNOOP_STATS
18548     snoop_stat.check_level_count++;
18549 #endif //SNOOP_STATS
18550     return (next_heap->mark_stack_busy()>=1);
18551 }
18552 #endif //MH_SC_MARK
18553
18554 #ifdef SNOOP_STATS
18555 void gc_heap::print_snoop_stat()
18556 {
18557     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
18558         "heap", "check", "zero", "mark", "stole", "pstack", "nstack", "nonsk"));
18559     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d",
18560         snoop_stat.heap_index,
18561         snoop_stat.objects_checked_count,
18562         snoop_stat.zero_ref_count,
18563         snoop_stat.objects_marked_count,
18564         snoop_stat.stolen_stack_count,
18565         snoop_stat.partial_stack_count,
18566         snoop_stat.normal_stack_count,
18567         snoop_stat.non_stack_count));
18568     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
18569         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "clear"));
18570     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18571         snoop_stat.heap_index,
18572         snoop_stat.check_level_count,
18573         snoop_stat.busy_count,
18574         snoop_stat.interlocked_count,
18575         snoop_stat.partial_mark_parent_count,
18576         snoop_stat.stolen_or_pm_count,
18577         snoop_stat.stolen_entry_count,
18578         snoop_stat.pm_not_ready_count,
18579         snoop_stat.normal_count,
18580         snoop_stat.stack_bottom_clear_count));
18581
18582     printf ("\n%4s | %8s | %8s | %8s | %8s | %8s\n", 
18583         "heap", "check", "zero", "mark", "idle", "switch");
18584     printf ("%4d | %8d | %8d | %8d | %8d | %8d\n",
18585         snoop_stat.heap_index,
18586         snoop_stat.objects_checked_count,
18587         snoop_stat.zero_ref_count,
18588         snoop_stat.objects_marked_count,
18589         snoop_stat.stack_idle_count,
18590         snoop_stat.switch_to_thread_count);
18591     printf ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
18592         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
18593     printf ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18594         snoop_stat.heap_index,
18595         snoop_stat.check_level_count,
18596         snoop_stat.busy_count,
18597         snoop_stat.interlocked_count,
18598         snoop_stat.partial_mark_parent_count,
18599         snoop_stat.stolen_or_pm_count,
18600         snoop_stat.stolen_entry_count,
18601         snoop_stat.pm_not_ready_count,
18602         snoop_stat.normal_count,
18603         snoop_stat.stack_bottom_clear_count);
18604 }
18605 #endif //SNOOP_STATS
18606
18607 #ifdef HEAP_ANALYZE
18608 void
18609 gc_heap::ha_mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18610 {
18611     if (!internal_root_array)
18612     {
18613         internal_root_array = new (nothrow) uint8_t* [internal_root_array_length];
18614         if (!internal_root_array)
18615         {
18616             heap_analyze_success = FALSE;
18617         }
18618     }
18619
18620     if (heap_analyze_success && (internal_root_array_length <= internal_root_array_index))
18621     {
18622         size_t new_size = 2*internal_root_array_length;
18623
18624         uint64_t available_physical = 0;
18625         get_memory_info (NULL, &available_physical);
18626         if (new_size > (size_t)(available_physical / 10))
18627         {
18628             heap_analyze_success = FALSE;
18629         }
18630         else
18631         {
18632             uint8_t** tmp = new (nothrow) uint8_t* [new_size];
18633             if (tmp)
18634             {
18635                 memcpy (tmp, internal_root_array,
18636                         internal_root_array_length*sizeof (uint8_t*));
18637                 delete[] internal_root_array;
18638                 internal_root_array = tmp;
18639                 internal_root_array_length = new_size;
18640             }
18641             else
18642             {
18643                 heap_analyze_success = FALSE;
18644             }
18645         }
18646     }
18647
18648     if (heap_analyze_success)
18649     {
18650         PREFIX_ASSUME(internal_root_array_index < internal_root_array_length);
18651
18652         uint8_t* ref = (uint8_t*)po;
18653         if (!current_obj || 
18654             !((ref >= current_obj) && (ref < (current_obj + current_obj_size))))
18655         {
18656             gc_heap* hp = gc_heap::heap_of (ref);
18657             current_obj = hp->find_object (ref, hp->lowest_address);
18658             current_obj_size = size (current_obj);
18659
18660             internal_root_array[internal_root_array_index] = current_obj;
18661             internal_root_array_index++;
18662         }
18663     }
18664
18665     mark_object_simple (po THREAD_NUMBER_ARG);
18666 }
18667 #endif //HEAP_ANALYZE
18668
18669 //this method assumes that *po is in the [low. high[ range
18670 void
18671 gc_heap::mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18672 {
18673     uint8_t* o = *po;
18674 #ifdef MULTIPLE_HEAPS
18675 #else  //MULTIPLE_HEAPS
18676     const int thread = 0;
18677 #endif //MULTIPLE_HEAPS
18678     {
18679 #ifdef SNOOP_STATS
18680         snoop_stat.objects_checked_count++;
18681 #endif //SNOOP_STATS
18682
18683         if (gc_mark1 (o))
18684         {
18685             m_boundary (o);
18686             size_t s = size (o);
18687             promoted_bytes (thread) += s;
18688             {
18689                 go_through_object_cl (method_table(o), o, s, poo,
18690                                         {
18691                                             uint8_t* oo = *poo;
18692                                             if (gc_mark (oo, gc_low, gc_high))
18693                                             {
18694                                                 m_boundary (oo);
18695                                                 size_t obj_size = size (oo);
18696                                                 promoted_bytes (thread) += obj_size;
18697
18698                                                 if (contain_pointers_or_collectible (oo))
18699                                                     mark_object_simple1 (oo, oo THREAD_NUMBER_ARG);
18700                                             }
18701                                         }
18702                     );
18703             }
18704         }
18705     }
18706 }
18707
18708 inline
18709 uint8_t* gc_heap::mark_object (uint8_t* o THREAD_NUMBER_DCL)
18710 {
18711     if ((o >= gc_low) && (o < gc_high))
18712         mark_object_simple (&o THREAD_NUMBER_ARG);
18713 #ifdef MULTIPLE_HEAPS
18714     else if (o)
18715     {
18716         //find the heap
18717         gc_heap* hp = heap_of (o);
18718         assert (hp);
18719         if ((o >= hp->gc_low) && (o < hp->gc_high))
18720             mark_object_simple (&o THREAD_NUMBER_ARG);
18721     }
18722 #endif //MULTIPLE_HEAPS
18723
18724     return o;
18725 }
18726
18727 #ifdef BACKGROUND_GC
18728
18729 void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL)
18730 {
18731     uint8_t** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length];
18732
18733 #ifdef SORT_MARK_STACK
18734     uint8_t** sorted_tos = background_mark_stack_array;
18735 #endif //SORT_MARK_STACK
18736
18737     background_mark_stack_tos = background_mark_stack_array;
18738
18739     while (1)
18740     {
18741 #ifdef MULTIPLE_HEAPS
18742 #else  //MULTIPLE_HEAPS
18743         const int thread = 0;
18744 #endif //MULTIPLE_HEAPS
18745         if (oo)
18746         {
18747             size_t s = 0; 
18748             if ((((size_t)oo & 1) == 0) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
18749             {
18750                 BOOL overflow_p = FALSE;
18751             
18752                 if (background_mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit - 1))
18753                 {
18754                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18755                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18756                     if (background_mark_stack_tos + num_pointers >= (mark_stack_limit - 1))
18757                     {
18758                         dprintf (2, ("h%d: %Id left, obj (mt: %Ix) %Id ptrs", 
18759                             heap_number,
18760                             (size_t)(mark_stack_limit - 1 - background_mark_stack_tos),
18761                             method_table(oo), 
18762                             num_pointers));
18763
18764                         bgc_overflow_count++;
18765                         overflow_p = TRUE;
18766                     }
18767                 }
18768             
18769                 if (overflow_p == FALSE)
18770                 {
18771                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18772
18773                     go_through_object_cl (method_table(oo), oo, s, ppslot,
18774                     {
18775                         uint8_t* o = *ppslot;
18776                         Prefetch(o);
18777                         if (background_mark (o, 
18778                                              background_saved_lowest_address, 
18779                                              background_saved_highest_address))
18780                         {
18781                             //m_boundary (o);
18782                             size_t obj_size = size (o);
18783                             bpromoted_bytes (thread) += obj_size;
18784                             if (contain_pointers_or_collectible (o))
18785                             {
18786                                 *(background_mark_stack_tos++) = o;
18787
18788                             }
18789                         }
18790                     }
18791                         );
18792                 }
18793                 else
18794                 {
18795                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18796                     background_min_overflow_address = min (background_min_overflow_address, oo);
18797                     background_max_overflow_address = max (background_max_overflow_address, oo);
18798                 }
18799             }
18800             else 
18801             {
18802                 uint8_t* start = oo;
18803                 if ((size_t)oo & 1)
18804                 {
18805                     oo = (uint8_t*)((size_t)oo & ~1);
18806                     start = *(--background_mark_stack_tos);
18807                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
18808                 }
18809 #ifdef COLLECTIBLE_CLASS
18810                 else
18811                 {
18812                     // If there's a class object, push it now. We are guaranteed to have the slot since
18813                     // we just popped one object off.
18814                     if (is_collectible (oo))
18815                     {
18816                         uint8_t* class_obj = get_class_object (oo);
18817                         if (background_mark (class_obj, 
18818                                             background_saved_lowest_address, 
18819                                             background_saved_highest_address))
18820                         {
18821                             size_t obj_size = size (class_obj);
18822                             bpromoted_bytes (thread) += obj_size;
18823
18824                             *(background_mark_stack_tos++) = class_obj;
18825                         }
18826                     }
18827
18828                     if (!contain_pointers (oo))
18829                     {
18830                         goto next_level;
18831                     }                    
18832                 }
18833 #endif //COLLECTIBLE_CLASS
18834
18835                 s = size (oo);
18836                 
18837                 BOOL overflow_p = FALSE;
18838             
18839                 if (background_mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
18840                 {
18841                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18842                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18843
18844                     dprintf (2, ("h%d: PM: %Id left, obj %Ix (mt: %Ix) start: %Ix, total: %Id", 
18845                         heap_number,
18846                         (size_t)(mark_stack_limit - background_mark_stack_tos),
18847                         oo,
18848                         method_table(oo), 
18849                         start,
18850                         num_pointers));
18851
18852                     bgc_overflow_count++;
18853                     overflow_p = TRUE;
18854                 }
18855                 if (overflow_p == FALSE)
18856                 {
18857                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18858
18859                     //push the object and its current 
18860                     uint8_t** place = background_mark_stack_tos++;
18861                     *(place) = start;
18862                     *(background_mark_stack_tos++) = (uint8_t*)((size_t)oo | 1);
18863
18864                     int i = num_partial_refs; 
18865
18866                     go_through_object (method_table(oo), oo, s, ppslot,
18867                                        start, use_start, (oo + s),
18868                     {
18869                         uint8_t* o = *ppslot;
18870                         Prefetch(o);
18871
18872                         if (background_mark (o, 
18873                                             background_saved_lowest_address, 
18874                                             background_saved_highest_address))
18875                         {
18876                             //m_boundary (o);
18877                             size_t obj_size = size (o);
18878                             bpromoted_bytes (thread) += obj_size;
18879                             if (contain_pointers_or_collectible (o))
18880                             {
18881                                 *(background_mark_stack_tos++) = o;
18882                                 if (--i == 0)
18883                                 {
18884                                     //update the start
18885                                     *place = (uint8_t*)(ppslot+1);
18886                                     goto more_to_do;
18887                                 }
18888
18889                             }
18890                         }
18891
18892                     }
18893                         );
18894                     //we are finished with this object
18895                     *place = 0; 
18896                     *(place+1) = 0;
18897
18898                 more_to_do:;
18899                 }
18900                 else
18901                 {
18902                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18903                     background_min_overflow_address = min (background_min_overflow_address, oo);
18904                     background_max_overflow_address = max (background_max_overflow_address, oo);
18905                 }
18906             }
18907         }
18908 #ifdef SORT_MARK_STACK
18909         if (background_mark_stack_tos > sorted_tos + mark_stack_array_length/8)
18910         {
18911             rqsort1 (sorted_tos, background_mark_stack_tos-1);
18912             sorted_tos = background_mark_stack_tos-1;
18913         }
18914 #endif //SORT_MARK_STACK
18915
18916 #ifdef COLLECTIBLE_CLASS
18917 next_level:
18918 #endif // COLLECTIBLE_CLASS
18919         allow_fgc();
18920
18921         if (!(background_mark_stack_tos == background_mark_stack_array))
18922         {
18923             oo = *(--background_mark_stack_tos);
18924
18925 #ifdef SORT_MARK_STACK
18926             sorted_tos = (uint8_t**)min ((size_t)sorted_tos, (size_t)background_mark_stack_tos);
18927 #endif //SORT_MARK_STACK
18928         }
18929         else
18930             break;
18931     }
18932
18933     assert (background_mark_stack_tos == background_mark_stack_array);
18934
18935
18936 }
18937
18938 //this version is different than the foreground GC because
18939 //it can't keep pointers to the inside of an object
18940 //while calling background_mark_simple1. The object could be moved
18941 //by an intervening foreground gc.
18942 //this method assumes that *po is in the [low. high[ range
18943 void
18944 gc_heap::background_mark_simple (uint8_t* o THREAD_NUMBER_DCL)
18945 {
18946 #ifdef MULTIPLE_HEAPS
18947 #else  //MULTIPLE_HEAPS
18948     const int thread = 0;
18949 #endif //MULTIPLE_HEAPS
18950     {
18951         dprintf (3, ("bmarking %Ix", o));
18952         
18953         if (background_mark1 (o))
18954         {
18955             //m_boundary (o);
18956             size_t s = size (o);
18957             bpromoted_bytes (thread) += s;
18958
18959             if (contain_pointers_or_collectible (o))
18960             {
18961                 background_mark_simple1 (o THREAD_NUMBER_ARG);
18962             }
18963         }
18964     }
18965 }
18966
18967 inline
18968 uint8_t* gc_heap::background_mark_object (uint8_t* o THREAD_NUMBER_DCL)
18969 {
18970     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
18971     {
18972         background_mark_simple (o THREAD_NUMBER_ARG);
18973     }
18974     else
18975     {
18976         if (o)
18977         {
18978             dprintf (3, ("or-%Ix", o));
18979         }
18980     }
18981     return o;
18982 }
18983
18984 void gc_heap::background_verify_mark (Object*& object, ScanContext* sc, uint32_t flags)
18985 {
18986     UNREFERENCED_PARAMETER(sc);
18987
18988     assert (settings.concurrent);
18989     uint8_t* o = (uint8_t*)object;
18990
18991     gc_heap* hp = gc_heap::heap_of (o);
18992 #ifdef INTERIOR_POINTERS
18993     if (flags & GC_CALL_INTERIOR)
18994     {
18995         o = hp->find_object (o, background_saved_lowest_address);
18996     }
18997 #endif //INTERIOR_POINTERS
18998
18999     if (!background_object_marked (o, FALSE))
19000     {
19001         FATAL_GC_ERROR();
19002     }
19003 }
19004
19005 void gc_heap::background_promote (Object** ppObject, ScanContext* sc, uint32_t flags)
19006 {
19007     UNREFERENCED_PARAMETER(sc);
19008     //in order to save space on the array, mark the object,
19009     //knowing that it will be visited later
19010     assert (settings.concurrent);
19011
19012     THREAD_NUMBER_FROM_CONTEXT;
19013 #ifndef MULTIPLE_HEAPS
19014     const int thread = 0;
19015 #endif //!MULTIPLE_HEAPS
19016
19017     uint8_t* o = (uint8_t*)*ppObject;
19018
19019     if (o == 0)
19020         return;
19021
19022 #ifdef DEBUG_DestroyedHandleValue
19023     // we can race with destroy handle during concurrent scan
19024     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
19025         return;
19026 #endif //DEBUG_DestroyedHandleValue
19027
19028     HEAP_FROM_THREAD;
19029
19030     gc_heap* hp = gc_heap::heap_of (o);
19031
19032     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
19033     {
19034         return;
19035     }
19036
19037 #ifdef INTERIOR_POINTERS
19038     if (flags & GC_CALL_INTERIOR)
19039     {
19040         o = hp->find_object (o, hp->background_saved_lowest_address);
19041         if (o == 0)
19042             return;
19043     }
19044 #endif //INTERIOR_POINTERS
19045
19046 #ifdef FEATURE_CONSERVATIVE_GC
19047     // For conservative GC, a value on stack may point to middle of a free object.
19048     // In this case, we don't need to promote the pointer.
19049     if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
19050     {
19051         return;
19052     }
19053 #endif //FEATURE_CONSERVATIVE_GC
19054
19055 #ifdef _DEBUG
19056     ((CObjectHeader*)o)->Validate();
19057 #endif //_DEBUG
19058
19059     dprintf (BGC_LOG, ("Background Promote %Ix", (size_t)o));
19060
19061     //needs to be called before the marking because it is possible for a foreground
19062     //gc to take place during the mark and move the object
19063     STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, "    GCHeap::Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL);
19064
19065     hpt->background_mark_simple (o THREAD_NUMBER_ARG);
19066 }
19067
19068 //used by the ephemeral collection to scan the local background structures
19069 //containing references.
19070 void
19071 gc_heap::scan_background_roots (promote_func* fn, int hn, ScanContext *pSC)
19072 {
19073     ScanContext sc;
19074     if (pSC == 0)
19075         pSC = &sc;
19076
19077     pSC->thread_number = hn;
19078
19079 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
19080     pSC->pCurrentDomain = 0;
19081 #endif
19082
19083     BOOL relocate_p = (fn == &GCHeap::Relocate);
19084
19085     dprintf (3, ("Scanning background mark list"));
19086
19087     //scan mark_list
19088     size_t mark_list_finger = 0;
19089     while (mark_list_finger < c_mark_list_index)
19090     {
19091         uint8_t** o = &c_mark_list [mark_list_finger];
19092         if (!relocate_p)
19093         {
19094             // We may not be able to calculate the size during relocate as POPO
19095             // may have written over the object.
19096             size_t s = size (*o);
19097             assert (Align (s) >= Align (min_obj_size));
19098             dprintf(3,("background root %Ix", (size_t)*o));
19099         }
19100         (*fn) ((Object**)o, pSC, 0);
19101         mark_list_finger++;
19102     }
19103
19104     //scan the mark stack
19105     dprintf (3, ("Scanning background mark stack"));
19106
19107     uint8_t** finger = background_mark_stack_array;
19108     while (finger < background_mark_stack_tos)
19109     {
19110         if ((finger + 1) < background_mark_stack_tos)
19111         {
19112             // We need to check for the partial mark case here.
19113             uint8_t* parent_obj = *(finger + 1);
19114             if ((size_t)parent_obj & 1)
19115             {
19116                 uint8_t* place = *finger;
19117                 size_t place_offset = 0;
19118                 uint8_t* real_parent_obj = (uint8_t*)((size_t)parent_obj & ~1);
19119
19120                 if (relocate_p)
19121                 {
19122                     *(finger + 1) = real_parent_obj;
19123                     place_offset = place - real_parent_obj;
19124                     dprintf(3,("relocating background root %Ix", (size_t)real_parent_obj));
19125                     (*fn) ((Object**)(finger + 1), pSC, 0);
19126                     real_parent_obj = *(finger + 1);
19127                     *finger = real_parent_obj + place_offset;
19128                     *(finger + 1) = (uint8_t*)((size_t)real_parent_obj | 1);
19129                     dprintf(3,("roots changed to %Ix, %Ix", *finger, *(finger + 1)));
19130                 }
19131                 else
19132                 {
19133                     uint8_t** temp = &real_parent_obj;
19134                     dprintf(3,("marking background root %Ix", (size_t)real_parent_obj));
19135                     (*fn) ((Object**)temp, pSC, 0);
19136                 }
19137
19138                 finger += 2;
19139                 continue;
19140             }
19141         }
19142         dprintf(3,("background root %Ix", (size_t)*finger));
19143         (*fn) ((Object**)finger, pSC, 0);
19144         finger++;
19145     }
19146 }
19147
19148 inline
19149 void gc_heap::background_mark_through_object (uint8_t* oo THREAD_NUMBER_DCL)
19150 {
19151     if (contain_pointers (oo))
19152     {
19153         size_t total_refs = 0;
19154         size_t s = size (oo);
19155         go_through_object_nostart (method_table(oo), oo, s, po,
19156                           {
19157                             uint8_t* o = *po;
19158                             total_refs++;
19159                             background_mark_object (o THREAD_NUMBER_ARG);
19160                           }
19161             );
19162
19163         dprintf (3,("Background marking through %Ix went through %Id refs", 
19164                           (size_t)oo,
19165                            total_refs));
19166     }
19167 }
19168
19169 uint8_t* gc_heap::background_seg_end (heap_segment* seg, BOOL concurrent_p)
19170 {
19171     if (concurrent_p && (seg == saved_overflow_ephemeral_seg))
19172     {
19173         // for now we stop at where gen1 started when we started processing 
19174         return background_min_soh_overflow_address;
19175     }
19176     else
19177     {
19178         return heap_segment_allocated (seg);
19179     }
19180 }
19181
19182 uint8_t* gc_heap::background_first_overflow (uint8_t* min_add,
19183                                           heap_segment* seg,
19184                                           BOOL concurrent_p, 
19185                                           BOOL small_object_p)
19186 {
19187     uint8_t* o = 0;
19188
19189     if (small_object_p)
19190     {
19191         if (in_range_for_segment (min_add, seg))
19192         {
19193             // min_add was the beginning of gen1 when we did the concurrent
19194             // overflow. Now we could be in a situation where min_add is
19195             // actually the same as allocated for that segment (because
19196             // we expanded heap), in which case we can not call 
19197             // find first on this address or we will AV.
19198             if (min_add >= heap_segment_allocated (seg))
19199             {
19200                 return min_add;
19201             }
19202             else
19203             {
19204                 if (concurrent_p && 
19205                     ((seg == saved_overflow_ephemeral_seg) && (min_add >= background_min_soh_overflow_address)))
19206                 {
19207                     return background_min_soh_overflow_address;
19208                 }
19209                 else
19210                 {
19211                     o = find_first_object (min_add, heap_segment_mem (seg));
19212                     return o;
19213                 }
19214             }
19215         }
19216     }
19217
19218     o = max (heap_segment_mem (seg), min_add);
19219     return o;
19220 }
19221
19222 void gc_heap::background_process_mark_overflow_internal (int condemned_gen_number,
19223                                                          uint8_t* min_add, uint8_t* max_add,
19224                                                          BOOL concurrent_p)
19225 {
19226     if (concurrent_p)
19227     {
19228         current_bgc_state = bgc_overflow_soh;
19229     }
19230
19231     size_t total_marked_objects = 0;
19232
19233 #ifdef MULTIPLE_HEAPS
19234     int thread = heap_number;
19235 #endif //MULTIPLE_HEAPS
19236
19237     exclusive_sync* loh_alloc_lock = 0;
19238
19239     dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
19240 #ifdef MULTIPLE_HEAPS
19241     // We don't have each heap scan all heaps concurrently because we are worried about
19242     // multiple threads calling things like find_first_object.
19243     int h_start = (concurrent_p ? heap_number : 0);
19244     int h_end = (concurrent_p ? (heap_number + 1) : n_heaps);
19245     for (int hi = h_start; hi < h_end; hi++)
19246     {
19247         gc_heap*  hp = (concurrent_p ? this : g_heaps [(heap_number + hi) % n_heaps]);
19248
19249 #else
19250     {
19251         gc_heap*  hp = 0;
19252
19253 #endif //MULTIPLE_HEAPS
19254         BOOL small_object_segments = TRUE;
19255         int align_const = get_alignment_constant (small_object_segments);
19256         generation* gen = hp->generation_of (condemned_gen_number);
19257         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
19258         PREFIX_ASSUME(seg != NULL);
19259         loh_alloc_lock = hp->bgc_alloc_lock;
19260
19261         uint8_t* o = hp->background_first_overflow (min_add,
19262                                                     seg, 
19263                                                     concurrent_p, 
19264                                                     small_object_segments);
19265
19266         while (1)
19267         {
19268             while ((o < hp->background_seg_end (seg, concurrent_p)) && (o <= max_add))
19269             {
19270                 dprintf (3, ("considering %Ix", (size_t)o));
19271
19272                 size_t s;
19273
19274                 if (concurrent_p && !small_object_segments)
19275                 {
19276                     loh_alloc_lock->bgc_mark_set (o);
19277
19278                     if (((CObjectHeader*)o)->IsFree())
19279                     {
19280                         s = unused_array_size (o);
19281                     }
19282                     else
19283                     {
19284                         s = size (o);
19285                     }
19286                 }
19287                 else
19288                 {
19289                     s = size (o);
19290                 }
19291
19292                 if (background_object_marked (o, FALSE) && contain_pointers_or_collectible (o))
19293                 {
19294                     total_marked_objects++;
19295                     go_through_object_cl (method_table(o), o, s, poo,
19296                                           uint8_t* oo = *poo;
19297                                           background_mark_object (oo THREAD_NUMBER_ARG);
19298                                          );
19299                 }
19300
19301                 if (concurrent_p && !small_object_segments)
19302                 {
19303                     loh_alloc_lock->bgc_mark_done ();
19304                 }
19305
19306                 o = o + Align (s, align_const);
19307
19308                 if (concurrent_p)
19309                 {
19310                     allow_fgc();
19311                 }
19312             }
19313
19314             dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)", 
19315                 heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects));
19316
19317             if ((concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) ||
19318                 (seg = heap_segment_next_in_range (seg)) == 0)
19319             {
19320                 if (small_object_segments)
19321                 {
19322                     if (concurrent_p)
19323                     {
19324                         current_bgc_state = bgc_overflow_loh;
19325                     }
19326
19327                     dprintf (2, ("h%d: SOH: ov-mo: %Id", heap_number, total_marked_objects));
19328                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
19329                     concurrent_print_time_delta (concurrent_p ? "Cov SOH" : "Nov SOH");
19330                     total_marked_objects = 0;
19331                     small_object_segments = FALSE;
19332                     align_const = get_alignment_constant (small_object_segments);
19333                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
19334
19335                     PREFIX_ASSUME(seg != NULL);
19336
19337                     o = max (heap_segment_mem (seg), min_add);
19338                     continue;
19339                 }
19340                 else
19341                 {
19342                     dprintf (GTC_LOG, ("h%d: LOH: ov-mo: %Id", heap_number, total_marked_objects));
19343                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
19344                     break;
19345                 }
19346             } 
19347             else
19348             {
19349                 o = hp->background_first_overflow (min_add, 
19350                                                    seg, 
19351                                                    concurrent_p, 
19352                                                    small_object_segments);
19353                 continue;
19354             }
19355         }
19356     }
19357 }
19358
19359 BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p)
19360 {
19361     BOOL grow_mark_array_p = TRUE;
19362
19363     if (concurrent_p)
19364     {
19365         assert (!processed_soh_overflow_p);
19366
19367         if ((background_max_overflow_address != 0) &&
19368             (background_min_overflow_address != MAX_PTR))
19369         {
19370             // We have overflow to process but we know we can't process the ephemeral generations
19371             // now (we actually could process till the current gen1 start but since we are going to 
19372             // make overflow per segment, for now I'll just stop at the saved gen1 start.
19373             saved_overflow_ephemeral_seg = ephemeral_heap_segment;
19374             background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg);
19375             background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation-1));
19376         }
19377     }
19378     else
19379     {
19380         assert ((saved_overflow_ephemeral_seg == 0) || 
19381                 ((background_max_soh_overflow_address != 0) &&
19382                  (background_min_soh_overflow_address != MAX_PTR)));
19383         
19384         if (!processed_soh_overflow_p)
19385         {
19386             // if there was no more overflow we just need to process what we didn't process 
19387             // on the saved ephemeral segment.
19388             if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR))
19389             {
19390                 dprintf (2, ("final processing mark overflow - no more overflow since last time"));
19391                 grow_mark_array_p = FALSE;
19392             }
19393
19394             background_min_overflow_address = min (background_min_overflow_address, 
19395                                                 background_min_soh_overflow_address);
19396             background_max_overflow_address = max (background_max_overflow_address,
19397                                                 background_max_soh_overflow_address);
19398             processed_soh_overflow_p = TRUE;
19399         }
19400     }
19401
19402     BOOL  overflow_p = FALSE;
19403 recheck:
19404     if ((! ((background_max_overflow_address == 0)) ||
19405          ! ((background_min_overflow_address == MAX_PTR))))
19406     {
19407         overflow_p = TRUE;
19408
19409         if (grow_mark_array_p)
19410         {
19411             // Try to grow the array.
19412             size_t new_size = max (MARK_STACK_INITIAL_LENGTH, 2*background_mark_stack_array_length);
19413
19414             if ((new_size * sizeof(mark)) > 100*1024)
19415             {
19416                 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19417
19418                 new_size = min(new_max_size, new_size);
19419             }
19420
19421             if ((background_mark_stack_array_length < new_size) && 
19422                 ((new_size - background_mark_stack_array_length) > (background_mark_stack_array_length / 2)))
19423             {
19424                 dprintf (2, ("h%d: ov grow to %Id", heap_number, new_size));
19425
19426                 uint8_t** tmp = new (nothrow) uint8_t* [new_size];
19427                 if (tmp)
19428                 {
19429                     delete background_mark_stack_array;
19430                     background_mark_stack_array = tmp;
19431                     background_mark_stack_array_length = new_size;
19432                     background_mark_stack_tos = background_mark_stack_array;
19433                 }
19434             }
19435         }
19436         else
19437         {
19438             grow_mark_array_p = TRUE;
19439         }
19440
19441         uint8_t*  min_add = background_min_overflow_address;
19442         uint8_t*  max_add = background_max_overflow_address;
19443
19444         background_max_overflow_address = 0;
19445         background_min_overflow_address = MAX_PTR;
19446
19447         background_process_mark_overflow_internal (max_generation, min_add, max_add, concurrent_p);
19448         if (!concurrent_p)
19449         {        
19450             goto recheck;
19451         }
19452     }
19453
19454     return overflow_p;
19455 }
19456
19457 #endif //BACKGROUND_GC
19458
19459 inline
19460 void gc_heap::mark_through_object (uint8_t* oo, BOOL mark_class_object_p THREAD_NUMBER_DCL)
19461 {
19462 #ifndef COLLECTIBLE_CLASS
19463     UNREFERENCED_PARAMETER(mark_class_object_p);
19464     BOOL to_mark_class_object = FALSE;
19465 #else //COLLECTIBLE_CLASS
19466     BOOL to_mark_class_object = (mark_class_object_p && (is_collectible(oo)));
19467 #endif //COLLECTIBLE_CLASS
19468     if (contain_pointers (oo) || to_mark_class_object)
19469     {
19470         dprintf(3,( "Marking through %Ix", (size_t)oo));
19471         size_t s = size (oo);
19472
19473 #ifdef COLLECTIBLE_CLASS
19474         if (to_mark_class_object)
19475         {
19476             uint8_t* class_obj = get_class_object (oo);
19477             mark_object (class_obj THREAD_NUMBER_ARG);
19478         }
19479 #endif //COLLECTIBLE_CLASS
19480
19481         if (contain_pointers (oo))
19482         {
19483             go_through_object_nostart (method_table(oo), oo, s, po,
19484                                 uint8_t* o = *po;
19485                                 mark_object (o THREAD_NUMBER_ARG);
19486                                 );
19487         }
19488     }
19489 }
19490
19491 size_t gc_heap::get_total_heap_size()
19492 {
19493     size_t total_heap_size = 0;
19494
19495 #ifdef MULTIPLE_HEAPS
19496     int hn = 0;
19497
19498     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19499     {
19500         gc_heap* hp2 = gc_heap::g_heaps [hn];
19501         total_heap_size += hp2->generation_size (max_generation + 1) + hp2->generation_sizes (hp2->generation_of (max_generation));
19502     }
19503 #else
19504     total_heap_size = generation_size (max_generation + 1) + generation_sizes (generation_of (max_generation));
19505 #endif //MULTIPLE_HEAPS
19506
19507     return total_heap_size;
19508 }
19509
19510 size_t gc_heap::get_total_fragmentation()
19511 {
19512     size_t total_fragmentation = 0;
19513
19514 #ifdef MULTIPLE_HEAPS
19515     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
19516     {
19517         gc_heap* hp = gc_heap::g_heaps[hn];
19518 #else //MULTIPLE_HEAPS
19519     {
19520         gc_heap* hp = pGenGCHeap;
19521 #endif //MULTIPLE_HEAPS
19522         for (int i = 0; i <= (max_generation + 1); i++)
19523         {
19524             generation* gen = hp->generation_of (i);
19525             total_fragmentation += (generation_free_list_space (gen) + generation_free_obj_space (gen));
19526         }
19527     }
19528
19529     return total_fragmentation;
19530 }
19531
19532 size_t gc_heap::get_total_gen_fragmentation (int gen_number)
19533 {
19534     size_t total_fragmentation = 0;
19535
19536 #ifdef MULTIPLE_HEAPS
19537     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
19538     {
19539         gc_heap* hp = gc_heap::g_heaps[hn];
19540 #else //MULTIPLE_HEAPS
19541     {
19542         gc_heap* hp = pGenGCHeap;
19543 #endif //MULTIPLE_HEAPS
19544         generation* gen = hp->generation_of (gen_number);
19545         total_fragmentation += (generation_free_list_space (gen) + generation_free_obj_space (gen));
19546     }
19547
19548     return total_fragmentation;
19549 }
19550
19551 size_t gc_heap::get_total_gen_estimated_reclaim (int gen_number)
19552 {
19553     size_t total_estimated_reclaim = 0;
19554
19555 #ifdef MULTIPLE_HEAPS
19556     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
19557     {
19558         gc_heap* hp = gc_heap::g_heaps[hn];
19559 #else //MULTIPLE_HEAPS
19560     {
19561         gc_heap* hp = pGenGCHeap;
19562 #endif //MULTIPLE_HEAPS
19563         total_estimated_reclaim += hp->estimated_reclaim (gen_number);
19564     }
19565
19566     return total_estimated_reclaim;
19567 }
19568
19569 size_t gc_heap::committed_size()
19570 {
19571     generation* gen = generation_of (max_generation);
19572     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19573     size_t total_committed = 0;
19574
19575     while (1)
19576     {
19577         total_committed += heap_segment_committed (seg) - (uint8_t*)seg;
19578
19579         seg = heap_segment_next (seg);
19580         if (!seg)
19581         {
19582             if (gen != large_object_generation)
19583             {
19584                 gen = generation_of (max_generation + 1);
19585                 seg = generation_start_segment (gen);
19586             }
19587             else
19588                 break;
19589         }
19590     }
19591
19592     return total_committed;
19593 }
19594
19595 size_t gc_heap::get_total_committed_size()
19596 {
19597     size_t total_committed = 0;
19598
19599 #ifdef MULTIPLE_HEAPS
19600     int hn = 0;
19601
19602     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19603     {
19604         gc_heap* hp = gc_heap::g_heaps [hn];
19605         total_committed += hp->committed_size();
19606     }
19607 #else
19608     total_committed = committed_size();
19609 #endif //MULTIPLE_HEAPS
19610
19611     return total_committed;
19612 }
19613
19614 size_t gc_heap::committed_size (bool loh_p, size_t* allocated)
19615 {
19616     int gen_number = (loh_p ? (max_generation + 1) : max_generation);
19617     generation* gen = generation_of (gen_number);
19618     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19619     size_t total_committed = 0;
19620     size_t total_allocated = 0;
19621
19622     while (seg)
19623     {
19624         total_committed += heap_segment_committed (seg) - (uint8_t*)seg;
19625         total_allocated += heap_segment_allocated (seg) - (uint8_t*)seg;
19626         seg = heap_segment_next (seg);
19627     }
19628
19629     *allocated = total_allocated;
19630     return total_committed;
19631 }
19632
19633 void gc_heap::get_memory_info (uint32_t* memory_load, 
19634                                uint64_t* available_physical,
19635                                uint64_t* available_page_file)
19636 {
19637     GCToOSInterface::GetMemoryStatus(memory_load, available_physical, available_page_file);
19638 }
19639
19640 void fire_mark_event (int heap_num, int root_type, size_t bytes_marked)
19641 {
19642     dprintf (DT_LOG_0, ("-----------[%d]mark %d: %Id", heap_num, root_type, bytes_marked));
19643     FIRE_EVENT(GCMarkWithType, heap_num, root_type, bytes_marked);
19644 }
19645
19646 //returns TRUE is an overflow happened.
19647 BOOL gc_heap::process_mark_overflow(int condemned_gen_number)
19648 {
19649     size_t last_promoted_bytes = promoted_bytes (heap_number);
19650     BOOL  overflow_p = FALSE;
19651 recheck:
19652     if ((! (max_overflow_address == 0) ||
19653          ! (min_overflow_address == MAX_PTR)))
19654     {
19655         overflow_p = TRUE;
19656         // Try to grow the array.
19657         size_t new_size =
19658             max (MARK_STACK_INITIAL_LENGTH, 2*mark_stack_array_length);
19659
19660         if ((new_size * sizeof(mark)) > 100*1024)
19661         {
19662             size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19663
19664             new_size = min(new_max_size, new_size);
19665         }
19666
19667         if ((mark_stack_array_length < new_size) && 
19668             ((new_size - mark_stack_array_length) > (mark_stack_array_length / 2)))
19669         {
19670             mark* tmp = new (nothrow) mark [new_size];
19671             if (tmp)
19672             {
19673                 delete mark_stack_array;
19674                 mark_stack_array = tmp;
19675                 mark_stack_array_length = new_size;
19676             }
19677         }
19678
19679         uint8_t*  min_add = min_overflow_address;
19680         uint8_t*  max_add = max_overflow_address;
19681         max_overflow_address = 0;
19682         min_overflow_address = MAX_PTR;
19683         process_mark_overflow_internal (condemned_gen_number, min_add, max_add);
19684         goto recheck;
19685     }
19686
19687     size_t current_promoted_bytes = promoted_bytes (heap_number);
19688
19689     if (current_promoted_bytes != last_promoted_bytes)
19690         fire_mark_event (heap_number, ETW::GC_ROOT_OVERFLOW, (current_promoted_bytes - last_promoted_bytes));
19691     return overflow_p;
19692 }
19693
19694 void gc_heap::process_mark_overflow_internal (int condemned_gen_number,
19695                                               uint8_t* min_add, uint8_t* max_add)
19696 {
19697 #ifdef MULTIPLE_HEAPS
19698     int thread = heap_number;
19699 #endif //MULTIPLE_HEAPS
19700     BOOL  full_p = (condemned_gen_number == max_generation);
19701
19702         dprintf(3,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
19703 #ifdef MULTIPLE_HEAPS
19704             for (int hi = 0; hi < n_heaps; hi++)
19705             {
19706                 gc_heap*  hp = g_heaps [(heap_number + hi) % n_heaps];
19707
19708 #else
19709             {
19710                 gc_heap*  hp = 0;
19711
19712 #endif //MULTIPLE_HEAPS
19713         BOOL small_object_segments = TRUE;
19714         int align_const = get_alignment_constant (small_object_segments);
19715         generation* gen = hp->generation_of (condemned_gen_number);
19716         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
19717         
19718         PREFIX_ASSUME(seg != NULL);
19719         uint8_t*  o = max (heap_segment_mem (seg), min_add);
19720         while (1)
19721         {
19722             uint8_t*  end = heap_segment_allocated (seg);
19723
19724             while ((o < end) && (o <= max_add))
19725             {
19726                 assert ((min_add <= o) && (max_add >= o));
19727                 dprintf (3, ("considering %Ix", (size_t)o));
19728                 if (marked (o))
19729                 {
19730                     mark_through_object (o, TRUE THREAD_NUMBER_ARG);
19731                 }
19732
19733                 o = o + Align (size (o), align_const);
19734             }
19735
19736             if (( seg = heap_segment_next_in_range (seg)) == 0)
19737             {
19738                 if (small_object_segments && full_p)
19739                 {
19740                     small_object_segments = FALSE;
19741                     align_const = get_alignment_constant (small_object_segments);
19742                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
19743
19744                     PREFIX_ASSUME(seg != NULL);
19745
19746                     o = max (heap_segment_mem (seg), min_add);
19747                     continue;
19748                 }
19749                 else
19750                 {
19751                     break;
19752                 } 
19753             } 
19754             else
19755             {
19756                 o = max (heap_segment_mem (seg), min_add);
19757                 continue;
19758             }
19759         }
19760     }
19761 }
19762
19763 // Scanning for promotion for dependent handles need special handling. Because the primary holds a strong
19764 // reference to the secondary (when the primary itself is reachable) and this can cause a cascading series of
19765 // promotions (the secondary of one handle is or promotes the primary of another) we might need to perform the
19766 // promotion scan multiple times.
19767 // This helper encapsulates the logic to complete all dependent handle promotions when running a server GC. It
19768 // also has the effect of processing any mark stack overflow.
19769
19770 #ifdef MULTIPLE_HEAPS
19771 // When multiple heaps are enabled we have must utilize a more complex algorithm in order to keep all the GC
19772 // worker threads synchronized. The algorithms are sufficiently divergent that we have different
19773 // implementations based on whether MULTIPLE_HEAPS is defined or not.
19774 //
19775 // Define some static variables used for synchronization in the method below. These should really be defined
19776 // locally but MSVC complains when the VOLATILE macro is expanded into an instantiation of the Volatile class.
19777 //
19778 // A note about the synchronization used within this method. Communication between the worker threads is
19779 // achieved via two shared booleans (defined below). These both act as latches that are transitioned only from
19780 // false -> true by unsynchronized code. They are only read or reset to false by a single thread under the
19781 // protection of a join.
19782 static VOLATILE(BOOL) s_fUnpromotedHandles = FALSE;
19783 static VOLATILE(BOOL) s_fUnscannedPromotions = FALSE;
19784 static VOLATILE(BOOL) s_fScanRequired;
19785 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19786 {
19787     // Whenever we call this method there may have been preceding object promotions. So set
19788     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19789     // based on the how the scanning proceeded).
19790     s_fUnscannedPromotions = TRUE;
19791
19792     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
19793     // the state of this thread's portion of the dependent handle table. That's because promotions on other
19794     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
19795     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
19796     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
19797     // as all the others or they'll get out of step).
19798     while (true)
19799     {
19800         // The various worker threads are all currently racing in this code. We need to work out if at least
19801         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
19802         // dependent handle table when both of the following conditions apply:
19803         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
19804         //     object happens to correspond to a primary in one of our handles we might potentially have to
19805         //     promote the associated secondary).
19806         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
19807         //
19808         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
19809         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
19810         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
19811         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
19812         // follows below. Note that we can't read this outside of the join since on any iteration apart from
19813         // the first threads will be racing between reading this value and completing their previous
19814         // iteration's table scan.
19815         //
19816         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
19817         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
19818         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
19819         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
19820         // we're safely joined.
19821         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19822             s_fUnpromotedHandles = TRUE;
19823
19824         // Synchronize all the threads so we can read our state variables safely. The shared variable
19825         // s_fScanRequired, indicating whether we should scan the tables or terminate the loop, will be set by
19826         // a single thread inside the join.
19827         gc_t_join.join(this, gc_join_scan_dependent_handles);
19828         if (gc_t_join.joined())
19829         {
19830             // We're synchronized so it's safe to read our shared state variables. We update another shared
19831             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
19832             // the loop. We scan if there has been at least one object promotion since last time and at least
19833             // one thread has a dependent handle table with a potential handle promotion possible.
19834             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
19835
19836             // Reset our shared state variables (ready to be set again on this scan or with a good initial
19837             // value for the next call if we're terminating the loop).
19838             s_fUnscannedPromotions = FALSE;
19839             s_fUnpromotedHandles = FALSE;
19840
19841             if (!s_fScanRequired)
19842             {
19843                 // We're terminating the loop. Perform any last operations that require single threaded access.
19844                 if (!initial_scan_p)
19845                 {
19846                     // On the second invocation we reconcile all mark overflow ranges across the heaps. This can help
19847                     // load balance if some of the heaps have an abnormally large workload.
19848                     uint8_t* all_heaps_max = 0;
19849                     uint8_t* all_heaps_min = MAX_PTR;
19850                     int i;
19851                     for (i = 0; i < n_heaps; i++)
19852                     {
19853                         if (all_heaps_max < g_heaps[i]->max_overflow_address)
19854                             all_heaps_max = g_heaps[i]->max_overflow_address;
19855                         if (all_heaps_min > g_heaps[i]->min_overflow_address)
19856                             all_heaps_min = g_heaps[i]->min_overflow_address;
19857                     }
19858                     for (i = 0; i < n_heaps; i++)
19859                     {
19860                         g_heaps[i]->max_overflow_address = all_heaps_max;
19861                         g_heaps[i]->min_overflow_address = all_heaps_min;
19862                     }
19863                 }
19864             }
19865
19866             // Restart all the workers.
19867             dprintf(3, ("Starting all gc thread mark stack overflow processing"));
19868             gc_t_join.restart();
19869         }
19870
19871         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19872         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
19873         // global flag indicating that at least one object promotion may have occurred (the usual comment
19874         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
19875         // exit the method since we unconditionally set this variable on method entry anyway).
19876         if (process_mark_overflow(condemned_gen_number))
19877             s_fUnscannedPromotions = TRUE;
19878
19879         // If we decided that no scan was required we can terminate the loop now.
19880         if (!s_fScanRequired)
19881             break;
19882
19883         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
19884         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
19885         // could miss noting the promotion of some primary objects).
19886         gc_t_join.join(this, gc_join_rescan_dependent_handles);
19887         if (gc_t_join.joined())
19888         {
19889             // Restart all the workers.
19890             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
19891             gc_t_join.restart();
19892         }
19893
19894         // If the portion of the dependent handle table managed by this worker has handles that could still be
19895         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
19896         // could require a rescan of handles on this or other workers.
19897         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19898             if (GCScan::GcDhReScan(sc))
19899                 s_fUnscannedPromotions = TRUE;
19900     }
19901 }
19902 #else //MULTIPLE_HEAPS
19903 // Non-multiple heap version of scan_dependent_handles: much simpler without the need to keep multiple worker
19904 // threads synchronized.
19905 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19906 {
19907     UNREFERENCED_PARAMETER(initial_scan_p);
19908
19909     // Whenever we call this method there may have been preceding object promotions. So set
19910     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19911     // based on the how the scanning proceeded).
19912     bool fUnscannedPromotions = true;
19913
19914     // Loop until there are either no more dependent handles that can have their secondary promoted or we've
19915     // managed to perform a scan without promoting anything new.
19916     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
19917     {
19918         // On each iteration of the loop start with the assumption that no further objects have been promoted.
19919         fUnscannedPromotions = false;
19920
19921         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19922         // being visible. If there was an overflow (process_mark_overflow returned true) then additional
19923         // objects now appear to be promoted and we should set the flag.
19924         if (process_mark_overflow(condemned_gen_number))
19925             fUnscannedPromotions = true;
19926
19927         // Perform the scan and set the flag if any promotions resulted.
19928         if (GCScan::GcDhReScan(sc))
19929             fUnscannedPromotions = true;
19930     }
19931
19932     // Process any mark stack overflow that may have resulted from scanning handles (or if we didn't need to
19933     // scan any handles at all this is the processing of overflows that may have occurred prior to this method
19934     // invocation).
19935     process_mark_overflow(condemned_gen_number);
19936 }
19937 #endif //MULTIPLE_HEAPS
19938
19939 void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
19940 {
19941     assert (settings.concurrent == FALSE);
19942
19943     ScanContext sc;
19944     sc.thread_number = heap_number;
19945     sc.promotion = TRUE;
19946     sc.concurrent = FALSE;
19947
19948     dprintf(2,("---- Mark Phase condemning %d ----", condemned_gen_number));
19949     BOOL  full_p = (condemned_gen_number == max_generation);
19950
19951 #ifdef TIME_GC
19952     unsigned start;
19953     unsigned finish;
19954     start = GetCycleCount32();
19955 #endif //TIME_GC
19956
19957     int gen_to_init = condemned_gen_number;
19958     if (condemned_gen_number == max_generation)
19959     {
19960         gen_to_init = max_generation + 1;
19961     }
19962     for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++)
19963     {
19964         dynamic_data* dd = dynamic_data_of (gen_idx);
19965         dd_begin_data_size (dd) = generation_size (gen_idx) - 
19966                                    dd_fragmentation (dd) -
19967                                    Align (size (generation_allocation_start (generation_of (gen_idx))));
19968         dprintf (2, ("begin data size for gen%d is %Id", gen_idx, dd_begin_data_size (dd)));
19969         dd_survived_size (dd) = 0;
19970         dd_pinned_survived_size (dd) = 0;
19971         dd_artificial_pinned_survived_size (dd) = 0;
19972         dd_added_pinned_size (dd) = 0;
19973 #ifdef SHORT_PLUGS
19974         dd_padding_size (dd) = 0;
19975 #endif //SHORT_PLUGS
19976 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
19977         dd_num_npinned_plugs (dd) = 0;
19978 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
19979     }
19980
19981 #ifdef FFIND_OBJECT
19982     if (gen0_must_clear_bricks > 0)
19983         gen0_must_clear_bricks--;
19984 #endif //FFIND_OBJECT
19985
19986     size_t last_promoted_bytes = 0;
19987
19988     promoted_bytes (heap_number) = 0;
19989     reset_mark_stack();
19990
19991 #ifdef SNOOP_STATS
19992     memset (&snoop_stat, 0, sizeof(snoop_stat));
19993     snoop_stat.heap_index = heap_number;
19994 #endif //SNOOP_STATS
19995
19996 #ifdef MH_SC_MARK
19997     if (full_p)
19998     {
19999         //initialize the mark stack
20000         for (int i = 0; i < max_snoop_level; i++)
20001         {
20002             ((uint8_t**)(mark_stack_array))[i] = 0;
20003         }
20004
20005         mark_stack_busy() = 1;
20006     }
20007 #endif //MH_SC_MARK
20008
20009     static uint32_t num_sizedrefs = 0;
20010
20011 #ifdef MH_SC_MARK
20012     static BOOL do_mark_steal_p = FALSE;
20013 #endif //MH_SC_MARK
20014
20015 #ifdef MULTIPLE_HEAPS
20016     gc_t_join.join(this, gc_join_begin_mark_phase);
20017     if (gc_t_join.joined())
20018     {
20019 #endif //MULTIPLE_HEAPS
20020
20021         maxgen_size_inc_p = false;
20022
20023         num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
20024
20025 #ifdef MULTIPLE_HEAPS
20026
20027 #ifdef MH_SC_MARK
20028         if (full_p)
20029         {
20030             size_t total_heap_size = get_total_heap_size();
20031
20032             if (total_heap_size > (100 * 1024 * 1024))
20033             {
20034                 do_mark_steal_p = TRUE;
20035             }
20036             else
20037             {
20038                 do_mark_steal_p = FALSE;
20039             }
20040         }
20041         else
20042         {
20043             do_mark_steal_p = FALSE;
20044         }
20045 #endif //MH_SC_MARK
20046
20047         gc_t_join.restart();
20048     }
20049 #endif //MULTIPLE_HEAPS
20050
20051     {
20052
20053 #ifdef MARK_LIST
20054         //set up the mark lists from g_mark_list
20055         assert (g_mark_list);
20056 #ifdef MULTIPLE_HEAPS
20057         mark_list = &g_mark_list [heap_number*mark_list_size];
20058 #else
20059         mark_list = g_mark_list;
20060 #endif //MULTIPLE_HEAPS
20061         //dont use the mark list for full gc
20062         //because multiple segments are more complex to handle and the list
20063         //is likely to overflow
20064         if (condemned_gen_number != max_generation)
20065             mark_list_end = &mark_list [mark_list_size-1];
20066         else
20067             mark_list_end = &mark_list [0];
20068         mark_list_index = &mark_list [0];
20069 #endif //MARK_LIST
20070
20071 #ifndef MULTIPLE_HEAPS
20072         shigh = (uint8_t*) 0;
20073         slow  = MAX_PTR;
20074 #endif //MULTIPLE_HEAPS
20075
20076         //%type%  category = quote (mark);
20077
20078         if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0))
20079         {
20080             GCScan::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
20081             fire_mark_event (heap_number, ETW::GC_ROOT_SIZEDREF, (promoted_bytes (heap_number) - last_promoted_bytes));
20082             last_promoted_bytes = promoted_bytes (heap_number);
20083
20084 #ifdef MULTIPLE_HEAPS
20085             gc_t_join.join(this, gc_join_scan_sizedref_done);
20086             if (gc_t_join.joined())
20087             {
20088                 dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots"));
20089                 gc_t_join.restart();
20090             }
20091 #endif //MULTIPLE_HEAPS
20092         }
20093     
20094         dprintf(3,("Marking Roots"));
20095
20096         GCScan::GcScanRoots(GCHeap::Promote,
20097                                 condemned_gen_number, max_generation,
20098                                 &sc);
20099
20100         fire_mark_event (heap_number, ETW::GC_ROOT_STACK, (promoted_bytes (heap_number) - last_promoted_bytes));
20101         last_promoted_bytes = promoted_bytes (heap_number);
20102
20103 #ifdef BACKGROUND_GC
20104         if (recursive_gc_sync::background_running_p())
20105         {
20106             scan_background_roots (GCHeap::Promote, heap_number, &sc);
20107         }
20108 #endif //BACKGROUND_GC
20109
20110 #ifdef FEATURE_PREMORTEM_FINALIZATION
20111         dprintf(3, ("Marking finalization data"));
20112         finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0);
20113 #endif // FEATURE_PREMORTEM_FINALIZATION
20114
20115         fire_mark_event (heap_number, ETW::GC_ROOT_FQ, (promoted_bytes (heap_number) - last_promoted_bytes));
20116         last_promoted_bytes = promoted_bytes (heap_number);
20117
20118 // MTHTS
20119         {
20120
20121             dprintf(3,("Marking handle table"));
20122             GCScan::GcScanHandles(GCHeap::Promote,
20123                                       condemned_gen_number, max_generation,
20124                                       &sc);
20125             fire_mark_event (heap_number, ETW::GC_ROOT_HANDLES, (promoted_bytes (heap_number) - last_promoted_bytes));
20126             last_promoted_bytes = promoted_bytes (heap_number);
20127         }
20128
20129 #ifdef TRACE_GC
20130         size_t promoted_before_cards = promoted_bytes (heap_number);
20131 #endif //TRACE_GC
20132
20133         dprintf (3, ("before cards: %Id", promoted_before_cards));
20134         if (!full_p)
20135         {
20136 #ifdef CARD_BUNDLE
20137 #ifdef MULTIPLE_HEAPS
20138             if (gc_t_join.r_join(this, gc_r_join_update_card_bundle))
20139             {
20140 #endif //MULTIPLE_HEAPS
20141
20142 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
20143                 // If we are manually managing card bundles, every write to the card table should already be
20144                 // accounted for in the card bundle table so there's nothing to update here.
20145                 update_card_table_bundle();
20146 #endif
20147                 if (card_bundles_enabled())
20148                 {
20149                     verify_card_bundles();
20150                 }
20151
20152 #ifdef MULTIPLE_HEAPS
20153                 gc_t_join.r_restart();
20154             }
20155 #endif //MULTIPLE_HEAPS
20156 #endif //CARD_BUNDLE
20157
20158             card_fn mark_object_fn = &gc_heap::mark_object_simple;
20159 #ifdef HEAP_ANALYZE
20160             heap_analyze_success = TRUE;
20161             if (heap_analyze_enabled)
20162             {
20163                 internal_root_array_index = 0;
20164                 current_obj = 0;
20165                 current_obj_size = 0;
20166                 mark_object_fn = &gc_heap::ha_mark_object_simple;
20167             }
20168 #endif //HEAP_ANALYZE
20169
20170             dprintf(3,("Marking cross generation pointers"));
20171             mark_through_cards_for_segments (mark_object_fn, FALSE);
20172
20173             dprintf(3,("Marking cross generation pointers for large objects"));
20174             mark_through_cards_for_large_objects (mark_object_fn, FALSE);
20175
20176             dprintf (3, ("marked by cards: %Id", 
20177                 (promoted_bytes (heap_number) - promoted_before_cards)));
20178             fire_mark_event (heap_number, ETW::GC_ROOT_OLDER, (promoted_bytes (heap_number) - last_promoted_bytes));
20179             last_promoted_bytes = promoted_bytes (heap_number);
20180         }
20181     }
20182
20183 #ifdef MH_SC_MARK
20184     if (do_mark_steal_p)
20185     {
20186         mark_steal();
20187     }
20188 #endif //MH_SC_MARK
20189
20190     // Dependent handles need to be scanned with a special algorithm (see the header comment on
20191     // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other
20192     // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation
20193     // but in a common case (where there are no dependent handles that are due to be collected) it allows us
20194     // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more
20195     // iterations if required and will also perform processing of any mark stack overflow once the dependent
20196     // handle table has been fully promoted.
20197     GCScan::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
20198     scan_dependent_handles(condemned_gen_number, &sc, true);
20199
20200 #ifdef MULTIPLE_HEAPS
20201     dprintf(3, ("Joining for short weak handle scan"));
20202     gc_t_join.join(this, gc_join_null_dead_short_weak);
20203     if (gc_t_join.joined())
20204 #endif //MULTIPLE_HEAPS
20205     {
20206 #ifdef HEAP_ANALYZE
20207         heap_analyze_enabled = FALSE;
20208         GCToEEInterface::AnalyzeSurvivorsFinished(condemned_gen_number);
20209 #endif // HEAP_ANALYZE
20210         GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);
20211
20212 #ifdef MULTIPLE_HEAPS
20213         if (!full_p)
20214         {
20215             // we used r_join and need to reinitialize states for it here.
20216             gc_t_join.r_init();
20217         }
20218
20219         //start all threads on the roots.
20220         dprintf(3, ("Starting all gc thread for short weak handle scan"));
20221         gc_t_join.restart();
20222 #endif //MULTIPLE_HEAPS
20223
20224     }
20225
20226     // null out the target of short weakref that were not promoted.
20227     GCScan::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc);
20228
20229 // MTHTS: keep by single thread
20230 #ifdef MULTIPLE_HEAPS
20231     dprintf(3, ("Joining for finalization"));
20232     gc_t_join.join(this, gc_join_scan_finalization);
20233     if (gc_t_join.joined())
20234 #endif //MULTIPLE_HEAPS
20235
20236     {
20237 #ifdef MULTIPLE_HEAPS
20238         //start all threads on the roots.
20239         dprintf(3, ("Starting all gc thread for Finalization"));
20240         gc_t_join.restart();
20241 #endif //MULTIPLE_HEAPS
20242     }
20243
20244     //Handle finalization.
20245     size_t promoted_bytes_live = promoted_bytes (heap_number);
20246
20247 #ifdef FEATURE_PREMORTEM_FINALIZATION
20248     dprintf (3, ("Finalize marking"));
20249     finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this);
20250
20251     GCToEEInterface::DiagWalkFReachableObjects(__this);
20252 #endif // FEATURE_PREMORTEM_FINALIZATION
20253
20254     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
20255     // for finalization. As before scan_dependent_handles will also process any mark stack overflow.
20256     scan_dependent_handles(condemned_gen_number, &sc, false);
20257
20258 #ifdef MULTIPLE_HEAPS
20259     dprintf(3, ("Joining for weak pointer deletion"));
20260     gc_t_join.join(this, gc_join_null_dead_long_weak);
20261     if (gc_t_join.joined())
20262     {
20263         //start all threads on the roots.
20264         dprintf(3, ("Starting all gc thread for weak pointer deletion"));
20265         gc_t_join.restart();
20266     }
20267 #endif //MULTIPLE_HEAPS
20268
20269     // null out the target of long weakref that were not promoted.
20270     GCScan::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc);
20271
20272 // MTHTS: keep by single thread
20273 #ifdef MULTIPLE_HEAPS
20274 #ifdef MARK_LIST
20275 #ifdef PARALLEL_MARK_LIST_SORT
20276 //    unsigned long start = GetCycleCount32();
20277     sort_mark_list();
20278 //    printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start);
20279 #endif //PARALLEL_MARK_LIST_SORT
20280 #endif //MARK_LIST
20281
20282     dprintf (3, ("Joining for sync block cache entry scanning"));
20283     gc_t_join.join(this, gc_join_null_dead_syncblk);
20284     if (gc_t_join.joined())
20285 #endif //MULTIPLE_HEAPS
20286     {
20287         // scan for deleted entries in the syncblk cache
20288         GCScan::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc);
20289
20290 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
20291         if (g_fEnableAppDomainMonitoring)
20292         {
20293             size_t promoted_all_heaps = 0;
20294 #ifdef MULTIPLE_HEAPS
20295             for (int i = 0; i < n_heaps; i++)
20296             {
20297                 promoted_all_heaps += promoted_bytes (i);
20298             }
20299 #else
20300             promoted_all_heaps = promoted_bytes (heap_number);
20301 #endif //MULTIPLE_HEAPS
20302             GCToEEInterface::RecordTotalSurvivedBytes(promoted_all_heaps);
20303         }
20304 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
20305
20306 #ifdef MULTIPLE_HEAPS
20307
20308 #ifdef MARK_LIST
20309 #ifndef PARALLEL_MARK_LIST_SORT
20310         //compact g_mark_list and sort it.
20311         combine_mark_lists();
20312 #endif //PARALLEL_MARK_LIST_SORT
20313 #endif //MARK_LIST
20314
20315         //decide on promotion
20316         if (!settings.promotion)
20317         {
20318             size_t m = 0;
20319             for (int n = 0; n <= condemned_gen_number;n++)
20320             {
20321                 m +=  (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.1);
20322             }
20323
20324             for (int i = 0; i < n_heaps; i++)
20325             {
20326                 dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1,
20327                                                                      max_generation));
20328                 size_t older_gen_size = (dd_current_size (dd) +
20329                                          (dd_desired_allocation (dd) -
20330                                          dd_new_allocation (dd)));
20331
20332                 if ((m > (older_gen_size)) ||
20333                     (promoted_bytes (i) > m))
20334                 {
20335                     settings.promotion = TRUE;
20336                 }
20337             }
20338         }
20339
20340 #ifdef SNOOP_STATS
20341         if (do_mark_steal_p)
20342         {
20343             size_t objects_checked_count = 0;
20344             size_t zero_ref_count = 0;
20345             size_t objects_marked_count = 0;
20346             size_t check_level_count = 0;
20347             size_t busy_count = 0;
20348             size_t interlocked_count = 0;
20349             size_t partial_mark_parent_count = 0;
20350             size_t stolen_or_pm_count = 0; 
20351             size_t stolen_entry_count = 0; 
20352             size_t pm_not_ready_count = 0; 
20353             size_t normal_count = 0;
20354             size_t stack_bottom_clear_count = 0;
20355
20356             for (int i = 0; i < n_heaps; i++)
20357             {
20358                 gc_heap* hp = g_heaps[i];
20359                 hp->print_snoop_stat();
20360                 objects_checked_count += hp->snoop_stat.objects_checked_count;
20361                 zero_ref_count += hp->snoop_stat.zero_ref_count;
20362                 objects_marked_count += hp->snoop_stat.objects_marked_count;
20363                 check_level_count += hp->snoop_stat.check_level_count;
20364                 busy_count += hp->snoop_stat.busy_count;
20365                 interlocked_count += hp->snoop_stat.interlocked_count;
20366                 partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count;
20367                 stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count;
20368                 stolen_entry_count += hp->snoop_stat.stolen_entry_count;
20369                 pm_not_ready_count += hp->snoop_stat.pm_not_ready_count;
20370                 normal_count += hp->snoop_stat.normal_count;
20371                 stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count;
20372             }
20373
20374             fflush (stdout);
20375
20376             printf ("-------total stats-------\n");
20377             printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
20378                 "checked", "zero", "marked", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
20379             printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
20380                 objects_checked_count,
20381                 zero_ref_count,
20382                 objects_marked_count,
20383                 check_level_count,
20384                 busy_count,
20385                 interlocked_count,
20386                 partial_mark_parent_count,
20387                 stolen_or_pm_count,
20388                 stolen_entry_count,
20389                 pm_not_ready_count,
20390                 normal_count,
20391                 stack_bottom_clear_count);
20392         }
20393 #endif //SNOOP_STATS
20394
20395         //start all threads.
20396         dprintf(3, ("Starting all threads for end of mark phase"));
20397         gc_t_join.restart();
20398 #else //MULTIPLE_HEAPS
20399
20400         //decide on promotion
20401         if (!settings.promotion)
20402         {
20403             size_t m = 0;
20404             for (int n = 0; n <= condemned_gen_number;n++)
20405             {
20406                 m +=  (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.06);
20407             }
20408             dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1,
20409                                                      max_generation));
20410             size_t older_gen_size = (dd_current_size (dd) +
20411                                      (dd_desired_allocation (dd) -
20412                                      dd_new_allocation (dd)));
20413
20414             dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id",
20415                          m, promoted_bytes (heap_number), older_gen_size));
20416
20417             if ((m > older_gen_size) ||
20418                     (promoted_bytes (heap_number) > m))
20419             {
20420                 settings.promotion = TRUE;
20421             }
20422         }
20423
20424 #endif //MULTIPLE_HEAPS
20425     }
20426
20427 #ifdef MULTIPLE_HEAPS
20428 #ifdef MARK_LIST
20429 #ifdef PARALLEL_MARK_LIST_SORT
20430 //    start = GetCycleCount32();
20431     merge_mark_lists();
20432 //    printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start);
20433 #endif //PARALLEL_MARK_LIST_SORT
20434 #endif //MARK_LIST
20435 #endif //MULTIPLE_HEAPS
20436
20437 #ifdef BACKGROUND_GC
20438     total_promoted_bytes = promoted_bytes (heap_number);
20439 #endif //BACKGROUND_GC
20440
20441     promoted_bytes (heap_number) -= promoted_bytes_live;
20442
20443 #ifdef TIME_GC
20444         finish = GetCycleCount32();
20445         mark_time = finish - start;
20446 #endif //TIME_GC
20447
20448     dprintf(2,("---- End of mark phase ----"));
20449 }
20450
20451 inline
20452 void gc_heap::pin_object (uint8_t* o, uint8_t** ppObject, uint8_t* low, uint8_t* high)
20453 {
20454     dprintf (3, ("Pinning %Ix", (size_t)o));
20455     if ((o >= low) && (o < high))
20456     {
20457         dprintf(3,("^%Ix^", (size_t)o));
20458         set_pinned (o);
20459
20460 #ifdef FEATURE_EVENT_TRACE        
20461         if(EVENT_ENABLED(PinObjectAtGCTime))
20462         {
20463             fire_etw_pin_object_event(o, ppObject);
20464         }
20465 #endif // FEATURE_EVENT_TRACE
20466
20467 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
20468         num_pinned_objects++;
20469 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
20470     }
20471 }
20472
20473 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
20474 size_t gc_heap::get_total_pinned_objects()
20475 {
20476 #ifdef MULTIPLE_HEAPS
20477     size_t total_num_pinned_objects = 0;
20478     for (int i = 0; i < gc_heap::n_heaps; i++)
20479     {
20480         gc_heap* hp = gc_heap::g_heaps[i];
20481         total_num_pinned_objects += hp->num_pinned_objects;
20482     }
20483     return total_num_pinned_objects;
20484 #else //MULTIPLE_HEAPS
20485     return num_pinned_objects;
20486 #endif //MULTIPLE_HEAPS
20487 }
20488 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
20489
20490 void gc_heap::reset_mark_stack ()
20491 {
20492     reset_pinned_queue();
20493     max_overflow_address = 0;
20494     min_overflow_address = MAX_PTR;
20495 }
20496
20497 #ifdef FEATURE_STRUCTALIGN
20498 //
20499 // The word with left child, right child, and align info is laid out as follows:
20500 //
20501 //      |   upper short word   |   lower short word   |
20502 //      |<------------> <----->|<------------> <----->|
20503 //      |  left child   info hi| right child   info lo|
20504 // x86: |    10 bits     6 bits|   10 bits      6 bits|
20505 //
20506 // where left/right child are signed values and concat(info hi, info lo) is unsigned.
20507 //
20508 // The "align info" encodes two numbers: the required alignment (a power of two)
20509 // and the misalignment (the number of machine words the destination address needs
20510 // to be adjusted by to provide alignment - so this number is always smaller than
20511 // the required alignment).  Thus, the two can be represented as the "logical or"
20512 // of the two numbers.  Note that the actual pad is computed from the misalignment
20513 // by adding the alignment iff the misalignment is non-zero and less than min_obj_size.
20514 //
20515
20516 // The number of bits in a brick.
20517 #if defined (_TARGET_AMD64_)
20518 #define brick_bits (12)
20519 #else
20520 #define brick_bits (11)
20521 #endif //_TARGET_AMD64_
20522 C_ASSERT(brick_size == (1 << brick_bits));
20523
20524 // The number of bits needed to represent the offset to a child node.
20525 // "brick_bits + 1" allows us to represent a signed offset within a brick.
20526 #define child_bits (brick_bits + 1 - LOG2_PTRSIZE)
20527
20528 // The number of bits in each of the pad hi, pad lo fields.
20529 #define pad_bits (sizeof(short) * 8 - child_bits)
20530
20531 #define child_from_short(w) (((signed short)(w) / (1 << (pad_bits - LOG2_PTRSIZE))) & ~((1 << LOG2_PTRSIZE) - 1))
20532 #define pad_mask ((1 << pad_bits) - 1)
20533 #define pad_from_short(w) ((size_t)(w) & pad_mask)
20534 #else // FEATURE_STRUCTALIGN
20535 #define child_from_short(w) (w)
20536 #endif // FEATURE_STRUCTALIGN
20537
20538 inline
20539 short node_left_child(uint8_t* node)
20540 {
20541     return child_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20542 }
20543
20544 inline
20545 void set_node_left_child(uint8_t* node, ptrdiff_t val)
20546 {
20547     assert (val > -(ptrdiff_t)brick_size);
20548     assert (val < (ptrdiff_t)brick_size);
20549     assert (Aligned (val));
20550 #ifdef FEATURE_STRUCTALIGN
20551     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20552     ((plug_and_pair*)node)[-1].m_pair.left = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20553 #else // FEATURE_STRUCTALIGN
20554     ((plug_and_pair*)node)[-1].m_pair.left = (short)val;
20555 #endif // FEATURE_STRUCTALIGN
20556     assert (node_left_child (node) == val);
20557 }
20558
20559 inline
20560 short node_right_child(uint8_t* node)
20561 {
20562     return child_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20563 }
20564
20565 inline
20566 void set_node_right_child(uint8_t* node, ptrdiff_t val)
20567 {
20568     assert (val > -(ptrdiff_t)brick_size);
20569     assert (val < (ptrdiff_t)brick_size);
20570     assert (Aligned (val));
20571 #ifdef FEATURE_STRUCTALIGN
20572     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20573     ((plug_and_pair*)node)[-1].m_pair.right = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20574 #else // FEATURE_STRUCTALIGN
20575     ((plug_and_pair*)node)[-1].m_pair.right = (short)val;
20576 #endif // FEATURE_STRUCTALIGN
20577     assert (node_right_child (node) == val);
20578 }
20579
20580 #ifdef FEATURE_STRUCTALIGN
20581 void node_aligninfo (uint8_t* node, int& requiredAlignment, ptrdiff_t& pad)
20582 {
20583     // Extract the single-number aligninfo from the fields.
20584     short left = ((plug_and_pair*)node)[-1].m_pair.left;
20585     short right = ((plug_and_pair*)node)[-1].m_pair.right;
20586     ptrdiff_t pad_shifted = (pad_from_short(left) << pad_bits) | pad_from_short(right);
20587     ptrdiff_t aligninfo = pad_shifted * DATA_ALIGNMENT;
20588
20589     // Replicate the topmost bit into all lower bits.
20590     ptrdiff_t x = aligninfo;
20591     x |= x >> 8;
20592     x |= x >> 4;
20593     x |= x >> 2;
20594     x |= x >> 1;
20595
20596     // Clear all bits but the highest.
20597     requiredAlignment = (int)(x ^ (x >> 1));
20598     pad = aligninfo - requiredAlignment;
20599     pad += AdjustmentForMinPadSize(pad, requiredAlignment);
20600 }
20601
20602 inline
20603 ptrdiff_t node_alignpad (uint8_t* node)
20604 {
20605     int requiredAlignment;
20606     ptrdiff_t alignpad;
20607     node_aligninfo (node, requiredAlignment, alignpad);
20608     return alignpad;
20609 }
20610
20611 void clear_node_aligninfo (uint8_t* node)
20612 {
20613     ((plug_and_pair*)node)[-1].m_pair.left &= ~0 << pad_bits;
20614     ((plug_and_pair*)node)[-1].m_pair.right &= ~0 << pad_bits;
20615 }
20616
20617 void set_node_aligninfo (uint8_t* node, int requiredAlignment, ptrdiff_t pad)
20618 {
20619     // Encode the alignment requirement and alignment offset as a single number
20620     // as described above.
20621     ptrdiff_t aligninfo = (size_t)requiredAlignment + (pad & (requiredAlignment-1));
20622     assert (Aligned (aligninfo));
20623     ptrdiff_t aligninfo_shifted = aligninfo / DATA_ALIGNMENT;
20624     assert (aligninfo_shifted < (1 << (pad_bits + pad_bits)));
20625
20626     ptrdiff_t hi = aligninfo_shifted >> pad_bits;
20627     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.left) == 0);
20628     ((plug_and_pair*)node)[-1].m_pair.left |= hi;
20629
20630     ptrdiff_t lo = aligninfo_shifted & pad_mask;
20631     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.right) == 0);
20632     ((plug_and_pair*)node)[-1].m_pair.right |= lo;
20633
20634 #ifdef _DEBUG
20635     int requiredAlignment2;
20636     ptrdiff_t pad2;
20637     node_aligninfo (node, requiredAlignment2, pad2);
20638     assert (requiredAlignment == requiredAlignment2);
20639     assert (pad == pad2);
20640 #endif // _DEBUG
20641 }
20642 #endif // FEATURE_STRUCTALIGN
20643
20644 inline
20645 void loh_set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20646 {
20647     ptrdiff_t* place = &(((loh_obj_and_pad*)node)[-1].reloc);
20648     *place = val;
20649 }
20650
20651 inline
20652 ptrdiff_t loh_node_relocation_distance(uint8_t* node)
20653 {
20654     return (((loh_obj_and_pad*)node)[-1].reloc);
20655 }
20656
20657 inline
20658 ptrdiff_t node_relocation_distance (uint8_t* node)
20659 {
20660     return (((plug_and_reloc*)(node))[-1].reloc & ~3);
20661 }
20662
20663 inline
20664 void set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20665 {
20666     assert (val == (val & ~3));
20667     ptrdiff_t* place = &(((plug_and_reloc*)node)[-1].reloc);
20668     //clear the left bit and the relocation field
20669     *place &= 1;
20670     // store the value
20671     *place |= val;
20672 }
20673
20674 #define node_left_p(node) (((plug_and_reloc*)(node))[-1].reloc & 2)
20675
20676 #define set_node_left(node) ((plug_and_reloc*)(node))[-1].reloc |= 2;
20677
20678 #ifndef FEATURE_STRUCTALIGN
20679 void set_node_realigned(uint8_t* node)
20680 {
20681     ((plug_and_reloc*)(node))[-1].reloc |= 1;
20682 }
20683
20684 void clear_node_realigned(uint8_t* node)
20685 {
20686 #ifdef RESPECT_LARGE_ALIGNMENT
20687     ((plug_and_reloc*)(node))[-1].reloc &= ~1;
20688 #else
20689     UNREFERENCED_PARAMETER(node);
20690 #endif //RESPECT_LARGE_ALIGNMENT
20691 }
20692 #endif // FEATURE_STRUCTALIGN
20693
20694 inline
20695 size_t  node_gap_size (uint8_t* node)
20696 {
20697     return ((plug_and_gap *)node)[-1].gap;
20698 }
20699
20700 void set_gap_size (uint8_t* node, size_t size)
20701 {
20702     assert (Aligned (size));
20703
20704     // clear the 2 uint32_t used by the node.
20705     ((plug_and_gap *)node)[-1].reloc = 0;
20706     ((plug_and_gap *)node)[-1].lr =0;
20707     ((plug_and_gap *)node)[-1].gap = size;
20708
20709     assert ((size == 0 )||(size >= sizeof(plug_and_reloc)));
20710
20711 }
20712
20713 uint8_t* gc_heap::insert_node (uint8_t* new_node, size_t sequence_number,
20714                    uint8_t* tree, uint8_t* last_node)
20715 {
20716     dprintf (3, ("IN: %Ix(%Ix), T: %Ix(%Ix), L: %Ix(%Ix) [%Ix]",
20717                  (size_t)new_node, brick_of(new_node), 
20718                  (size_t)tree, brick_of(tree), 
20719                  (size_t)last_node, brick_of(last_node),
20720                  sequence_number));
20721     if (power_of_two_p (sequence_number))
20722     {
20723         set_node_left_child (new_node, (tree - new_node));
20724         dprintf (3, ("NT: %Ix, LC->%Ix", (size_t)new_node, (tree - new_node)));
20725         tree = new_node;
20726     }
20727     else
20728     {
20729         if (oddp (sequence_number))
20730         {
20731             set_node_right_child (last_node, (new_node - last_node));
20732             dprintf (3, ("%Ix RC->%Ix", last_node, (new_node - last_node)));
20733         }
20734         else
20735         {
20736             uint8_t*  earlier_node = tree;
20737             size_t imax = logcount(sequence_number) - 2;
20738             for (size_t i = 0; i != imax; i++)
20739             {
20740                 earlier_node = earlier_node + node_right_child (earlier_node);
20741             }
20742             int tmp_offset = node_right_child (earlier_node);
20743             assert (tmp_offset); // should never be empty
20744             set_node_left_child (new_node, ((earlier_node + tmp_offset ) - new_node));
20745             set_node_right_child (earlier_node, (new_node - earlier_node));
20746
20747             dprintf (3, ("%Ix LC->%Ix, %Ix RC->%Ix", 
20748                 new_node, ((earlier_node + tmp_offset ) - new_node),
20749                 earlier_node, (new_node - earlier_node)));
20750         }
20751     }
20752     return tree;
20753 }
20754
20755 size_t gc_heap::update_brick_table (uint8_t* tree, size_t current_brick,
20756                                     uint8_t* x, uint8_t* plug_end)
20757 {
20758     dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix",
20759         tree, current_brick, x, plug_end));
20760
20761     if (tree != NULL)
20762     {
20763         dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix", 
20764             current_brick, (size_t)(tree - brick_address (current_brick)), tree));
20765         set_brick (current_brick, (tree - brick_address (current_brick)));
20766     }
20767     else
20768     {
20769         dprintf (3, ("b- %Ix->-1", current_brick));
20770         set_brick (current_brick, -1);
20771     }
20772     size_t  b = 1 + current_brick;
20773     ptrdiff_t  offset = 0;
20774     size_t last_br = brick_of (plug_end-1);
20775     current_brick = brick_of (x-1);
20776     dprintf (3, ("ubt: %Ix->%Ix]->%Ix]", b, last_br, current_brick));
20777     while (b <= current_brick)
20778     {
20779         if (b <= last_br)
20780         {
20781             set_brick (b, --offset);
20782         }
20783         else
20784         {
20785             set_brick (b,-1);
20786         }
20787         b++;
20788     }
20789     return brick_of (x);
20790 }
20791
20792 void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, uint8_t* next_plug_to_allocate)
20793 {
20794 #ifdef BIT64
20795     // We should never demote big plugs to gen0.
20796     if (gen == youngest_generation)
20797     {
20798         heap_segment* seg = ephemeral_heap_segment;
20799         size_t mark_stack_large_bos = mark_stack_bos;
20800         size_t large_plug_pos = 0;
20801         while (mark_stack_large_bos < mark_stack_tos)
20802         {
20803             if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th)
20804             {
20805                 while (mark_stack_bos <= mark_stack_large_bos)
20806                 {
20807                     size_t entry = deque_pinned_plug();
20808                     size_t len = pinned_len (pinned_plug_of (entry));
20809                     uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20810                     if (len > demotion_plug_len_th)
20811                     {
20812                         dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)", gen->gen_num, plug, len, (plug+len)));
20813                     }
20814                     pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen);
20815                     assert(mark_stack_array[entry].len == 0 ||
20816                             mark_stack_array[entry].len >= Align(min_obj_size));
20817                     generation_allocation_pointer (consing_gen) = plug + len;
20818                     generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg);
20819                     set_allocator_next_pin (consing_gen);
20820                 }
20821             }
20822
20823             mark_stack_large_bos++;
20824         }
20825     }
20826 #endif // BIT64
20827
20828     generation_plan_allocation_start (gen) =
20829         allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1);
20830     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20831     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20832     if (next_plug_to_allocate)
20833     {
20834         size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen));
20835         if (allocation_left > dist_to_next_plug)
20836         {
20837             allocation_left = dist_to_next_plug;
20838         }
20839     }
20840     if (allocation_left < Align (min_obj_size))
20841     {
20842         generation_plan_allocation_start_size (gen) += allocation_left;
20843         generation_allocation_pointer (consing_gen) += allocation_left;
20844     }
20845
20846     dprintf (2, ("plan alloc gen%d(%Ix) start at %Ix (ptr: %Ix, limit: %Ix, next: %Ix)", gen->gen_num, 
20847         generation_plan_allocation_start (gen),
20848         generation_plan_allocation_start_size (gen),
20849         generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen),
20850         next_plug_to_allocate));
20851 }
20852
20853 void gc_heap::realloc_plan_generation_start (generation* gen, generation* consing_gen)
20854 {
20855     BOOL adjacentp = FALSE;
20856
20857     generation_plan_allocation_start (gen) =  
20858         allocate_in_expanded_heap (consing_gen, Align(min_obj_size), adjacentp, 0, 
20859 #ifdef SHORT_PLUGS
20860                                    FALSE, NULL, 
20861 #endif //SHORT_PLUGS
20862                                    FALSE, -1 REQD_ALIGN_AND_OFFSET_ARG);
20863
20864     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20865     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20866     if ((allocation_left < Align (min_obj_size)) && 
20867          (generation_allocation_limit (consing_gen)!=heap_segment_plan_allocated (generation_allocation_segment (consing_gen))))
20868     {
20869         generation_plan_allocation_start_size (gen) += allocation_left;
20870         generation_allocation_pointer (consing_gen) += allocation_left;
20871     }
20872
20873     dprintf (1, ("plan re-alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num, 
20874         generation_plan_allocation_start (consing_gen),
20875         generation_allocation_pointer (consing_gen), 
20876         generation_allocation_limit (consing_gen))); 
20877 }
20878
20879 void gc_heap::plan_generation_starts (generation*& consing_gen)
20880 {
20881     //make sure that every generation has a planned allocation start
20882     int  gen_number = settings.condemned_generation;
20883     while (gen_number >= 0)
20884     {
20885         if (gen_number < max_generation)
20886         {
20887             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20888         }
20889         generation* gen = generation_of (gen_number);
20890         if (0 == generation_plan_allocation_start (gen))
20891         {
20892             plan_generation_start (gen, consing_gen, 0);
20893             assert (generation_plan_allocation_start (gen));
20894         }
20895         gen_number--;
20896     }
20897     // now we know the planned allocation size
20898     heap_segment_plan_allocated (ephemeral_heap_segment) =
20899         generation_allocation_pointer (consing_gen);
20900 }
20901
20902 void gc_heap::advance_pins_for_demotion (generation* gen)
20903 {
20904     uint8_t* original_youngest_start = generation_allocation_start (youngest_generation);
20905     heap_segment* seg = ephemeral_heap_segment;
20906
20907     if ((!(pinned_plug_que_empty_p())))
20908     {
20909         size_t gen1_pinned_promoted = generation_pinned_allocation_compact_size (generation_of (max_generation));
20910         size_t gen1_pins_left = dd_pinned_survived_size (dynamic_data_of (max_generation - 1)) - gen1_pinned_promoted;
20911         size_t total_space_to_skip = last_gen1_pin_end - generation_allocation_pointer (gen);
20912         float pin_frag_ratio = (float)gen1_pins_left / (float)total_space_to_skip;
20913         float pin_surv_ratio = (float)gen1_pins_left / (float)(dd_survived_size (dynamic_data_of (max_generation - 1)));
20914         if ((pin_frag_ratio > 0.15) && (pin_surv_ratio > 0.30))
20915         {
20916             while (!pinned_plug_que_empty_p() &&
20917                     (pinned_plug (oldest_pin()) < original_youngest_start))
20918             {
20919                 size_t entry = deque_pinned_plug();
20920                 size_t len = pinned_len (pinned_plug_of (entry));
20921                 uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20922                 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (gen);
20923                 assert(mark_stack_array[entry].len == 0 ||
20924                         mark_stack_array[entry].len >= Align(min_obj_size));
20925                 generation_allocation_pointer (gen) = plug + len;
20926                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20927                 set_allocator_next_pin (gen);
20928
20929                 //Add the size of the pinned plug to the right pinned allocations
20930                 //find out which gen this pinned plug came from 
20931                 int frgn = object_gennum (plug);
20932                 if ((frgn != (int)max_generation) && settings.promotion)
20933                 {
20934                     int togn = object_gennum_plan (plug);
20935                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
20936                     if (frgn < togn)
20937                     {
20938                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
20939                     }
20940                 }
20941
20942                 dprintf (2, ("skipping gap %d, pin %Ix (%Id)", 
20943                     pinned_len (pinned_plug_of (entry)), plug, len));
20944             }
20945         }
20946         dprintf (2, ("ad_p_d: PL: %Id, SL: %Id, pfr: %d, psr: %d", 
20947             gen1_pins_left, total_space_to_skip, (int)(pin_frag_ratio*100), (int)(pin_surv_ratio*100)));
20948     }
20949 }
20950
20951 void gc_heap::process_ephemeral_boundaries (uint8_t* x,
20952                                             int& active_new_gen_number,
20953                                             int& active_old_gen_number,
20954                                             generation*& consing_gen,
20955                                             BOOL& allocate_in_condemned)
20956 {
20957 retry:
20958     if ((active_old_gen_number > 0) &&
20959         (x >= generation_allocation_start (generation_of (active_old_gen_number - 1))))
20960     {
20961         dprintf (2, ("crossing gen%d, x is %Ix", active_old_gen_number - 1, x));
20962
20963         if (!pinned_plug_que_empty_p())
20964         {
20965             dprintf (2, ("oldest pin: %Ix(%Id)",
20966                 pinned_plug (oldest_pin()), 
20967                 (x - pinned_plug (oldest_pin()))));
20968         }
20969
20970         if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation))
20971         {
20972             active_new_gen_number--;
20973         }
20974
20975         active_old_gen_number--;
20976         assert ((!settings.promotion) || (active_new_gen_number>0));
20977
20978         if (active_new_gen_number == (max_generation - 1))
20979         {
20980 #ifdef FREE_USAGE_STATS
20981             if (settings.condemned_generation == max_generation)
20982             {
20983                 // We need to do this before we skip the rest of the pinned plugs.
20984                 generation* gen_2 = generation_of (max_generation);
20985                 generation* gen_1 = generation_of (max_generation - 1);
20986
20987                 size_t total_num_pinned_free_spaces_left = 0;
20988
20989                 // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is.
20990                 for (int j = 0; j < NUM_GEN_POWER2; j++)
20991                 {
20992                     dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)", 
20993                         heap_number, 
20994                         settings.gc_index,
20995                         (j + 10), 
20996                         gen_2->gen_current_pinned_free_spaces[j],
20997                         gen_2->gen_plugs[j], gen_1->gen_plugs[j],
20998                         (gen_2->gen_plugs[j] + gen_1->gen_plugs[j])));
20999
21000                     total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j];
21001                 }
21002
21003                 float pinned_free_list_efficiency = 0;
21004                 size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2);
21005                 if (total_pinned_free_space != 0)
21006                 {
21007                     pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space;
21008                 }
21009
21010                 dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left",
21011                             heap_number,
21012                             generation_allocated_in_pinned_free (gen_2),
21013                             total_pinned_free_space, 
21014                             (int)(pinned_free_list_efficiency * 100),
21015                             generation_pinned_free_obj_space (gen_2),
21016                             total_num_pinned_free_spaces_left));
21017             }
21018 #endif //FREE_USAGE_STATS
21019
21020             //Go past all of the pinned plugs for this generation.
21021             while (!pinned_plug_que_empty_p() &&
21022                    (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment)))
21023             {
21024                 size_t  entry = deque_pinned_plug();
21025                 mark*  m = pinned_plug_of (entry);
21026                 uint8_t*  plug = pinned_plug (m);
21027                 size_t  len = pinned_len (m);
21028                 // detect pinned block in different segment (later) than
21029                 // allocation segment, skip those until the oldest pin is in the ephemeral seg.
21030                 // adjust the allocation segment along the way (at the end it will
21031                 // be the ephemeral segment.
21032                 heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen));
21033
21034                 PREFIX_ASSUME(nseg != NULL);
21035
21036                 while (!((plug >= generation_allocation_pointer (consing_gen))&&
21037                         (plug < heap_segment_allocated (nseg))))
21038                 {
21039                     //adjust the end of the segment to be the end of the plug
21040                     assert (generation_allocation_pointer (consing_gen)>=
21041                             heap_segment_mem (nseg));
21042                     assert (generation_allocation_pointer (consing_gen)<=
21043                             heap_segment_committed (nseg));
21044
21045                     heap_segment_plan_allocated (nseg) =
21046                         generation_allocation_pointer (consing_gen);
21047                     //switch allocation segment
21048                     nseg = heap_segment_next_rw (nseg);
21049                     generation_allocation_segment (consing_gen) = nseg;
21050                     //reset the allocation pointer and limits
21051                     generation_allocation_pointer (consing_gen) =
21052                         heap_segment_mem (nseg);
21053                 }
21054                 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
21055                 assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size));
21056                 generation_allocation_pointer (consing_gen) = plug + len;
21057                 generation_allocation_limit (consing_gen) =
21058                     generation_allocation_pointer (consing_gen);
21059             }
21060             allocate_in_condemned = TRUE;
21061             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
21062         }
21063
21064         if (active_new_gen_number != max_generation)
21065         {
21066             if (active_new_gen_number == (max_generation - 1))
21067             {
21068                 maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
21069                 if (!demote_gen1_p)
21070                     advance_pins_for_demotion (consing_gen);
21071             }
21072
21073             plan_generation_start (generation_of (active_new_gen_number), consing_gen, x);
21074                 
21075             dprintf (2, ("process eph: allocated gen%d start at %Ix", 
21076                 active_new_gen_number,
21077                 generation_plan_allocation_start (generation_of (active_new_gen_number))));
21078
21079             if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p())
21080             {
21081                 uint8_t* pplug = pinned_plug (oldest_pin());
21082                 if (object_gennum (pplug) > 0)
21083                 {
21084                     demotion_low = pplug;
21085                     dprintf (3, ("process eph: dlow->%Ix", demotion_low));
21086                 }
21087             }
21088
21089             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
21090         }
21091
21092         goto retry;
21093     }
21094 }
21095
21096 inline
21097 void gc_heap::seg_clear_mark_bits (heap_segment* seg)
21098 {
21099     uint8_t* o = heap_segment_mem (seg);
21100     while (o < heap_segment_allocated (seg))
21101     {
21102         if (marked (o))
21103         {
21104             clear_marked (o);
21105         }
21106         o = o  + Align (size (o));
21107     }
21108 }
21109
21110 #ifdef FEATURE_BASICFREEZE
21111 void gc_heap::sweep_ro_segments (heap_segment* start_seg)
21112 {
21113     //go through all of the segment in range and reset the mark bit
21114     //TODO works only on small object segments
21115
21116     heap_segment* seg = start_seg;
21117
21118     while (seg)
21119     {
21120         if (heap_segment_read_only_p (seg) &&
21121             heap_segment_in_range_p (seg))
21122         {
21123 #ifdef BACKGROUND_GC
21124             if (settings.concurrent)
21125             {
21126                 seg_clear_mark_array_bits_soh (seg);
21127             }
21128             else
21129             {
21130                 seg_clear_mark_bits (seg);
21131             }
21132 #else //BACKGROUND_GC
21133
21134 #ifdef MARK_ARRAY
21135             if(gc_can_use_concurrent)
21136             {
21137                 clear_mark_array (max (heap_segment_mem (seg), lowest_address),
21138                               min (heap_segment_allocated (seg), highest_address),
21139                               FALSE); // read_only segments need the mark clear
21140             }
21141 #else //MARK_ARRAY
21142             seg_clear_mark_bits (seg);
21143 #endif //MARK_ARRAY
21144
21145 #endif //BACKGROUND_GC
21146         }
21147         seg = heap_segment_next (seg);
21148     }
21149 }
21150 #endif // FEATURE_BASICFREEZE
21151
21152 #ifdef FEATURE_LOH_COMPACTION
21153 inline
21154 BOOL gc_heap::loh_pinned_plug_que_empty_p()
21155 {
21156     return (loh_pinned_queue_bos == loh_pinned_queue_tos);
21157 }
21158
21159 void gc_heap::loh_set_allocator_next_pin()
21160 {
21161     if (!(loh_pinned_plug_que_empty_p()))
21162     {
21163         mark*  oldest_entry = loh_oldest_pin();
21164         uint8_t* plug = pinned_plug (oldest_entry);
21165         generation* gen = large_object_generation;
21166         if ((plug >= generation_allocation_pointer (gen)) &&
21167             (plug <  generation_allocation_limit (gen)))
21168         {
21169             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
21170         }
21171         else
21172             assert (!((plug < generation_allocation_pointer (gen)) &&
21173                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
21174     }
21175 }
21176
21177 size_t gc_heap::loh_deque_pinned_plug ()
21178 {
21179     size_t m = loh_pinned_queue_bos;
21180     loh_pinned_queue_bos++;
21181     return m;
21182 }
21183
21184 inline
21185 mark* gc_heap::loh_pinned_plug_of (size_t bos)
21186 {
21187     return &loh_pinned_queue[bos];
21188 }
21189
21190 inline
21191 mark* gc_heap::loh_oldest_pin()
21192 {
21193     return loh_pinned_plug_of (loh_pinned_queue_bos);
21194 }
21195
21196 // If we can't grow the queue, then don't compact.
21197 BOOL gc_heap::loh_enque_pinned_plug (uint8_t* plug, size_t len)
21198 {
21199     assert(len >= Align(min_obj_size, get_alignment_constant (FALSE)));
21200
21201     if (loh_pinned_queue_length <= loh_pinned_queue_tos)
21202     {
21203         if (!grow_mark_stack (loh_pinned_queue, loh_pinned_queue_length, LOH_PIN_QUEUE_LENGTH))
21204         {
21205             return FALSE;
21206         }
21207     }
21208     dprintf (3, (" P: %Ix(%Id)", plug, len));
21209     mark& m = loh_pinned_queue[loh_pinned_queue_tos];
21210     m.first = plug;
21211     m.len = len;
21212     loh_pinned_queue_tos++;
21213     loh_set_allocator_next_pin();
21214     return TRUE;
21215 }
21216
21217 inline
21218 BOOL gc_heap::loh_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit)
21219 {
21220     dprintf (1235, ("trying to fit %Id(%Id) between %Ix and %Ix (%Id)", 
21221         size, 
21222         (2* AlignQword (loh_padding_obj_size) +  size),
21223         alloc_pointer,
21224         alloc_limit,
21225         (alloc_limit - alloc_pointer)));
21226
21227     return ((alloc_pointer + 2* AlignQword (loh_padding_obj_size) +  size) <= alloc_limit);
21228 }
21229
21230 uint8_t* gc_heap::loh_allocate_in_condemned (uint8_t* old_loc, size_t size)
21231 {
21232     UNREFERENCED_PARAMETER(old_loc);
21233
21234     generation* gen = large_object_generation;
21235     dprintf (1235, ("E: p:%Ix, l:%Ix, s: %Id", 
21236         generation_allocation_pointer (gen),
21237         generation_allocation_limit (gen),
21238         size));
21239
21240 retry:
21241     {
21242         heap_segment* seg = generation_allocation_segment (gen);
21243         if (!(loh_size_fit_p (size, generation_allocation_pointer (gen), generation_allocation_limit (gen))))
21244         {
21245             if ((!(loh_pinned_plug_que_empty_p()) &&
21246                  (generation_allocation_limit (gen) ==
21247                   pinned_plug (loh_oldest_pin()))))
21248             {
21249                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21250                 size_t len = pinned_len (m);
21251                 uint8_t* plug = pinned_plug (m);
21252                 dprintf (1235, ("AIC: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
21253                 pinned_len (m) = plug - generation_allocation_pointer (gen);
21254                 generation_allocation_pointer (gen) = plug + len;
21255                 
21256                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21257                 loh_set_allocator_next_pin();
21258                 dprintf (1235, ("s: p: %Ix, l: %Ix (%Id)", 
21259                     generation_allocation_pointer (gen), 
21260                     generation_allocation_limit (gen),
21261                     (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21262
21263                 goto retry;
21264             }
21265
21266             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
21267             {
21268                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21269                 dprintf (1235, ("l->pa(%Ix)", generation_allocation_limit (gen)));
21270             }
21271             else
21272             {
21273                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
21274                 {
21275                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
21276                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21277                     dprintf (1235, ("l->c(%Ix)", generation_allocation_limit (gen)));
21278                 }
21279                 else
21280                 {
21281                     if (loh_size_fit_p (size, generation_allocation_pointer (gen), heap_segment_reserved (seg)) &&
21282                         (grow_heap_segment (seg, (generation_allocation_pointer (gen) + size + 2* AlignQword (loh_padding_obj_size)))))
21283                     {
21284                         dprintf (1235, ("growing seg from %Ix to %Ix\n", heap_segment_committed (seg),
21285                                          (generation_allocation_pointer (gen) + size)));
21286
21287                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
21288                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21289
21290                         dprintf (1235, ("g: p: %Ix, l: %Ix (%Id)", 
21291                             generation_allocation_pointer (gen), 
21292                             generation_allocation_limit (gen),
21293                             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21294                     }
21295                     else
21296                     {
21297                         heap_segment* next_seg = heap_segment_next (seg);
21298                         assert (generation_allocation_pointer (gen)>=
21299                                 heap_segment_mem (seg));
21300                         // Verify that all pinned plugs for this segment are consumed
21301                         if (!loh_pinned_plug_que_empty_p() &&
21302                             ((pinned_plug (loh_oldest_pin()) <
21303                               heap_segment_allocated (seg)) &&
21304                              (pinned_plug (loh_oldest_pin()) >=
21305                               generation_allocation_pointer (gen))))
21306                         {
21307                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
21308                                          pinned_plug (loh_oldest_pin())));
21309                             dprintf (1236, ("queue empty: %d", loh_pinned_plug_que_empty_p()));
21310                             FATAL_GC_ERROR();
21311                         }
21312                         assert (generation_allocation_pointer (gen)>=
21313                                 heap_segment_mem (seg));
21314                         assert (generation_allocation_pointer (gen)<=
21315                                 heap_segment_committed (seg));
21316                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
21317
21318                         if (next_seg)
21319                         {
21320                             // for LOH do we want to try starting from the first LOH every time though?
21321                             generation_allocation_segment (gen) = next_seg;
21322                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
21323                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
21324
21325                             dprintf (1235, ("n: p: %Ix, l: %Ix (%Id)", 
21326                                 generation_allocation_pointer (gen), 
21327                                 generation_allocation_limit (gen),
21328                                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21329                         }
21330                         else
21331                         {
21332                             dprintf (1, ("We ran out of space compacting, shouldn't happen"));
21333                             FATAL_GC_ERROR();
21334                         }
21335                     }
21336                 }
21337             }
21338             loh_set_allocator_next_pin();
21339
21340             dprintf (1235, ("r: p: %Ix, l: %Ix (%Id)", 
21341                 generation_allocation_pointer (gen), 
21342                 generation_allocation_limit (gen),
21343                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21344
21345             goto retry;
21346         }
21347     }
21348
21349     {
21350         assert (generation_allocation_pointer (gen)>=
21351                 heap_segment_mem (generation_allocation_segment (gen)));
21352         uint8_t* result = generation_allocation_pointer (gen);
21353         size_t loh_pad = AlignQword (loh_padding_obj_size);
21354
21355         generation_allocation_pointer (gen) += size + loh_pad;
21356         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
21357
21358         dprintf (1235, ("p: %Ix, l: %Ix (%Id)", 
21359             generation_allocation_pointer (gen), 
21360             generation_allocation_limit (gen),
21361             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21362
21363         assert (result + loh_pad);
21364         return result + loh_pad;
21365     }
21366 }
21367
21368 BOOL gc_heap::should_compact_loh()
21369 {
21370     // If hard limit is specified GC will automatically decide if LOH needs to be compacted.
21371     return (heap_hard_limit || loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
21372 }
21373
21374 inline
21375 void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p)
21376 {
21377     if (settings.loh_compaction && (loh_compaction_mode == loh_compaction_once))
21378     {
21379         if (all_heaps_compacted_p)
21380         {
21381             // If the compaction mode says to compact once and we are going to compact LOH, 
21382             // we need to revert it back to no compaction.
21383             loh_compaction_mode = loh_compaction_default;
21384         }
21385     }
21386 }
21387
21388 BOOL gc_heap::plan_loh()
21389 {
21390     if (!loh_pinned_queue)
21391     {
21392         loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]);
21393         if (!loh_pinned_queue)
21394         {
21395             dprintf (1, ("Cannot allocate the LOH pinned queue (%Id bytes), no compaction", 
21396                          LOH_PIN_QUEUE_LENGTH * sizeof (mark)));
21397             return FALSE;
21398         }
21399
21400         loh_pinned_queue_length = LOH_PIN_QUEUE_LENGTH;
21401     }
21402
21403     if (heap_number == 0)
21404         loh_pinned_queue_decay = LOH_PIN_DECAY;
21405
21406     loh_pinned_queue_tos = 0;
21407     loh_pinned_queue_bos = 0;
21408     
21409     generation* gen        = large_object_generation;
21410     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21411     PREFIX_ASSUME(start_seg != NULL);
21412     heap_segment* seg      = start_seg;
21413     uint8_t* o             = generation_allocation_start (gen);
21414
21415     dprintf (1235, ("before GC LOH size: %Id, free list: %Id, free obj: %Id\n", 
21416         generation_size (max_generation + 1), 
21417         generation_free_list_space (gen),
21418         generation_free_obj_space (gen)));
21419
21420     while (seg)
21421     {
21422         heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
21423         seg = heap_segment_next (seg);
21424     }
21425
21426     seg = start_seg;
21427
21428     //Skip the generation gap object
21429     o = o + AlignQword (size (o));
21430     // We don't need to ever realloc gen3 start so don't touch it.
21431     heap_segment_plan_allocated (seg) = o;
21432     generation_allocation_pointer (gen) = o;
21433     generation_allocation_limit (gen) = generation_allocation_pointer (gen);
21434     generation_allocation_segment (gen) = start_seg;
21435
21436     uint8_t* free_space_start = o;
21437     uint8_t* free_space_end = o;
21438     uint8_t* new_address = 0;
21439
21440     while (1)
21441     {
21442         if (o >= heap_segment_allocated (seg))
21443         {
21444             seg = heap_segment_next (seg);
21445             if (seg == 0)
21446             {
21447                 break;
21448             }
21449
21450             o = heap_segment_mem (seg);
21451         }
21452
21453         if (marked (o))
21454         {
21455             free_space_end = o;
21456             size_t size = AlignQword (size (o));
21457             dprintf (1235, ("%Ix(%Id) M", o, size));
21458
21459             if (pinned (o))
21460             {
21461                 // We don't clear the pinned bit yet so we can check in 
21462                 // compact phase how big a free object we should allocate
21463                 // in front of the pinned object. We use the reloc address
21464                 // field to store this.
21465                 if (!loh_enque_pinned_plug (o, size))
21466                 {
21467                     return FALSE;
21468                 }
21469                 new_address = o;
21470             }
21471             else
21472             {
21473                 new_address = loh_allocate_in_condemned (o, size);
21474             }
21475
21476             loh_set_node_relocation_distance (o, (new_address - o));
21477             dprintf (1235, ("lobj %Ix-%Ix -> %Ix-%Ix (%Id)", o, (o + size), new_address, (new_address + size), (new_address - o)));
21478
21479             o = o + size;
21480             free_space_start = o;
21481             if (o < heap_segment_allocated (seg))
21482             {
21483                 assert (!marked (o));
21484             }
21485         }
21486         else
21487         {
21488             while (o < heap_segment_allocated (seg) && !marked (o))
21489             {
21490                 dprintf (1235, ("%Ix(%Id) F (%d)", o, AlignQword (size (o)), ((method_table (o) == g_gc_pFreeObjectMethodTable) ? 1 : 0)));
21491                 o = o + AlignQword (size (o));
21492             }
21493         }
21494     }
21495
21496     while (!loh_pinned_plug_que_empty_p())
21497     {
21498         mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21499         size_t len = pinned_len (m);
21500         uint8_t* plug = pinned_plug (m);
21501
21502         // detect pinned block in different segment (later) than
21503         // allocation segment
21504         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (gen));
21505
21506         while ((plug < generation_allocation_pointer (gen)) ||
21507                (plug >= heap_segment_allocated (nseg)))
21508         {
21509             assert ((plug < heap_segment_mem (nseg)) ||
21510                     (plug > heap_segment_reserved (nseg)));
21511             //adjust the end of the segment to be the end of the plug
21512             assert (generation_allocation_pointer (gen)>=
21513                     heap_segment_mem (nseg));
21514             assert (generation_allocation_pointer (gen)<=
21515                     heap_segment_committed (nseg));
21516
21517             heap_segment_plan_allocated (nseg) =
21518                 generation_allocation_pointer (gen);
21519             //switch allocation segment
21520             nseg = heap_segment_next_rw (nseg);
21521             generation_allocation_segment (gen) = nseg;
21522             //reset the allocation pointer and limits
21523             generation_allocation_pointer (gen) =
21524                 heap_segment_mem (nseg);
21525         }
21526
21527         dprintf (1235, ("SP: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
21528         pinned_len (m) = plug - generation_allocation_pointer (gen);
21529         generation_allocation_pointer (gen) = plug + len;
21530     }
21531
21532     heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
21533     generation_allocation_pointer (gen) = 0;
21534     generation_allocation_limit (gen) = 0;
21535
21536     return TRUE;
21537 }
21538
21539 void gc_heap::compact_loh()
21540 {
21541     assert (should_compact_loh());
21542
21543     generation* gen        = large_object_generation;
21544     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21545     PREFIX_ASSUME(start_seg != NULL);
21546     heap_segment* seg      = start_seg;
21547     heap_segment* prev_seg = 0;
21548     uint8_t* o             = generation_allocation_start (gen);
21549
21550     //Skip the generation gap object
21551     o = o + AlignQword (size (o));
21552     // We don't need to ever realloc gen3 start so don't touch it.
21553     uint8_t* free_space_start = o;
21554     uint8_t* free_space_end = o;
21555     generation_allocator (gen)->clear();
21556     generation_free_list_space (gen) = 0;
21557     generation_free_obj_space (gen) = 0;
21558
21559     loh_pinned_queue_bos = 0;
21560
21561     while (1)
21562     {
21563         if (o >= heap_segment_allocated (seg))
21564         {
21565             heap_segment* next_seg = heap_segment_next (seg);
21566
21567             if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) &&
21568                 (seg != start_seg) && !heap_segment_read_only_p (seg))
21569             {
21570                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
21571                 assert (prev_seg);
21572                 heap_segment_next (prev_seg) = next_seg;
21573                 heap_segment_next (seg) = freeable_large_heap_segment;
21574                 freeable_large_heap_segment = seg;
21575             }
21576             else
21577             {
21578                 if (!heap_segment_read_only_p (seg))
21579                 {
21580                     // We grew the segment to accommodate allocations.
21581                     if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
21582                     {
21583                         if ((heap_segment_plan_allocated (seg) - plug_skew)  > heap_segment_used (seg))
21584                         {
21585                             heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew;
21586                         }
21587                     }
21588
21589                     heap_segment_allocated (seg) = heap_segment_plan_allocated (seg);
21590                     dprintf (3, ("Trimming seg to %Ix[", heap_segment_allocated (seg)));
21591                     decommit_heap_segment_pages (seg, 0);
21592                     dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix",
21593                         seg, 
21594                         heap_segment_allocated (seg),
21595                         heap_segment_used (seg),
21596                         heap_segment_committed (seg)));
21597                     //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew;
21598                     dprintf (1236, ("CLOH: used is set to %Ix", heap_segment_used (seg)));
21599                 }
21600                 prev_seg = seg;
21601             }
21602
21603             seg = next_seg;
21604             if (seg == 0)
21605                 break;
21606             else
21607             {
21608                 o = heap_segment_mem (seg);
21609             }
21610         }
21611
21612         if (marked (o))
21613         {
21614             free_space_end = o;
21615             size_t size = AlignQword (size (o));
21616
21617             size_t loh_pad;
21618             uint8_t* reloc = o;
21619             clear_marked (o);
21620
21621             if (pinned (o))
21622             {
21623                 // We are relying on the fact the pinned objects are always looked at in the same order 
21624                 // in plan phase and in compact phase.
21625                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21626                 uint8_t* plug = pinned_plug (m);
21627                 assert (plug == o);
21628
21629                 loh_pad = pinned_len (m);
21630                 clear_pinned (o);
21631             }
21632             else
21633             {
21634                 loh_pad = AlignQword (loh_padding_obj_size);
21635
21636                 reloc += loh_node_relocation_distance (o);
21637                 gcmemcopy (reloc, o, size, TRUE);
21638             }
21639
21640             thread_gap ((reloc - loh_pad), loh_pad, gen);
21641
21642             o = o + size;
21643             free_space_start = o;
21644             if (o < heap_segment_allocated (seg))
21645             {
21646                 assert (!marked (o));
21647             }
21648         }
21649         else
21650         {
21651             while (o < heap_segment_allocated (seg) && !marked (o))
21652             {
21653                 o = o + AlignQword (size (o));
21654             }
21655         }
21656     }
21657
21658     assert (loh_pinned_plug_que_empty_p());
21659
21660     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
21661         generation_size (max_generation + 1), 
21662         generation_free_list_space (gen),
21663         generation_free_obj_space (gen)));
21664 }
21665
21666 void gc_heap::relocate_in_loh_compact()
21667 {
21668     generation* gen        = large_object_generation;
21669     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21670     uint8_t* o             = generation_allocation_start (gen);
21671
21672     //Skip the generation gap object
21673     o = o + AlignQword (size (o));
21674
21675     relocate_args args;
21676     args.low = gc_low;
21677     args.high = gc_high;
21678     args.last_plug = 0;
21679
21680     while (1)
21681     {
21682         if (o >= heap_segment_allocated (seg))
21683         {
21684             seg = heap_segment_next (seg);
21685             if (seg == 0)
21686             {
21687                 break;
21688             }
21689
21690             o = heap_segment_mem (seg);
21691         }
21692
21693         if (marked (o))
21694         {
21695             size_t size = AlignQword (size (o));
21696
21697             check_class_object_demotion (o);
21698             if (contain_pointers (o))
21699             {
21700                 go_through_object_nostart (method_table (o), o, size(o), pval,
21701                 {
21702                     reloc_survivor_helper (pval);
21703                 });
21704             }
21705
21706             o = o + size;
21707             if (o < heap_segment_allocated (seg))
21708             {
21709                 assert (!marked (o));
21710             }
21711         }
21712         else
21713         {
21714             while (o < heap_segment_allocated (seg) && !marked (o))
21715             {
21716                 o = o + AlignQword (size (o));
21717             }
21718         }
21719     }
21720
21721     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
21722         generation_size (max_generation + 1), 
21723         generation_free_list_space (gen),
21724         generation_free_obj_space (gen)));
21725 }
21726
21727 void gc_heap::walk_relocation_for_loh (void* profiling_context, record_surv_fn fn)
21728 {
21729     generation* gen        = large_object_generation;
21730     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21731     uint8_t* o             = generation_allocation_start (gen);
21732
21733     //Skip the generation gap object
21734     o = o + AlignQword (size (o));
21735
21736     while (1)
21737     {
21738         if (o >= heap_segment_allocated (seg))
21739         {
21740             seg = heap_segment_next (seg);
21741             if (seg == 0)
21742             {
21743                 break;
21744             }
21745
21746             o = heap_segment_mem (seg);
21747         }
21748
21749         if (marked (o))
21750         {
21751             size_t size = AlignQword (size (o));
21752
21753             ptrdiff_t reloc = loh_node_relocation_distance (o);
21754
21755             STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc);
21756
21757             fn (o, (o + size), reloc, profiling_context, !!settings.compaction, false);
21758
21759             o = o + size;
21760             if (o < heap_segment_allocated (seg))
21761             {
21762                 assert (!marked (o));
21763             }
21764         }
21765         else
21766         {
21767             while (o < heap_segment_allocated (seg) && !marked (o))
21768             {
21769                 o = o + AlignQword (size (o));
21770             }
21771         }
21772     }
21773 }
21774
21775 BOOL gc_heap::loh_object_p (uint8_t* o)
21776 {
21777 #ifdef MULTIPLE_HEAPS
21778     gc_heap* hp = gc_heap::g_heaps [0];
21779     int brick_entry = hp->brick_table[hp->brick_of (o)];
21780 #else //MULTIPLE_HEAPS
21781     int brick_entry = brick_table[brick_of (o)];
21782 #endif //MULTIPLE_HEAPS
21783
21784     return (brick_entry == 0);
21785 }
21786 #endif //FEATURE_LOH_COMPACTION
21787
21788 void gc_heap::convert_to_pinned_plug (BOOL& last_npinned_plug_p, 
21789                                       BOOL& last_pinned_plug_p, 
21790                                       BOOL& pinned_plug_p,
21791                                       size_t ps,
21792                                       size_t& artificial_pinned_size)
21793 {
21794     last_npinned_plug_p = FALSE;
21795     last_pinned_plug_p = TRUE;
21796     pinned_plug_p = TRUE;
21797     artificial_pinned_size = ps;
21798 }
21799
21800 // Because we have the artificial pinning, we can't guarantee that pinned and npinned
21801 // plugs are always interleaved.
21802 void gc_heap::store_plug_gap_info (uint8_t* plug_start,
21803                                    uint8_t* plug_end,
21804                                    BOOL& last_npinned_plug_p, 
21805                                    BOOL& last_pinned_plug_p, 
21806                                    uint8_t*& last_pinned_plug,
21807                                    BOOL& pinned_plug_p,
21808                                    uint8_t* last_object_in_last_plug,
21809                                    BOOL& merge_with_last_pin_p,
21810                                    // this is only for verification purpose
21811                                    size_t last_plug_len)
21812 {
21813     UNREFERENCED_PARAMETER(last_plug_len);
21814
21815     if (!last_npinned_plug_p && !last_pinned_plug_p)
21816     {
21817         //dprintf (3, ("last full plug end: %Ix, full plug start: %Ix", plug_end, plug_start));
21818         dprintf (3, ("Free: %Ix", (plug_start - plug_end)));
21819         assert ((plug_start == plug_end) || ((size_t)(plug_start - plug_end) >= Align (min_obj_size)));
21820         set_gap_size (plug_start, plug_start - plug_end);
21821     }
21822
21823     if (pinned (plug_start))
21824     {
21825         BOOL save_pre_plug_info_p = FALSE;
21826
21827         if (last_npinned_plug_p || last_pinned_plug_p)
21828         {
21829             //if (last_plug_len == Align (min_obj_size))
21830             //{
21831             //    dprintf (3, ("debugging only - last npinned plug is min, check to see if it's correct"));
21832             //    GCToOSInterface::DebugBreak();
21833             //}
21834             save_pre_plug_info_p = TRUE;
21835         }
21836
21837         pinned_plug_p = TRUE;
21838         last_npinned_plug_p = FALSE;
21839
21840         if (last_pinned_plug_p)
21841         {
21842             dprintf (3, ("last plug %Ix was also pinned, should merge", last_pinned_plug));
21843             merge_with_last_pin_p = TRUE;
21844         }
21845         else
21846         {
21847             last_pinned_plug_p = TRUE;
21848             last_pinned_plug = plug_start;
21849                 
21850             enque_pinned_plug (last_pinned_plug, save_pre_plug_info_p, last_object_in_last_plug);
21851
21852             if (save_pre_plug_info_p)
21853             {
21854                 set_gap_size (plug_start, sizeof (gap_reloc_pair));
21855             }
21856         }
21857     }
21858     else
21859     {
21860         if (last_pinned_plug_p)
21861         {
21862             //if (Align (last_plug_len) < min_pre_pin_obj_size)
21863             //{
21864             //    dprintf (3, ("debugging only - last pinned plug is min, check to see if it's correct"));
21865             //    GCToOSInterface::DebugBreak();
21866             //}
21867
21868             save_post_plug_info (last_pinned_plug, last_object_in_last_plug, plug_start);
21869             set_gap_size (plug_start, sizeof (gap_reloc_pair));
21870
21871             verify_pins_with_post_plug_info("after saving post plug info");
21872         }
21873         last_npinned_plug_p = TRUE;
21874         last_pinned_plug_p = FALSE;
21875     }
21876 }
21877
21878 void gc_heap::record_interesting_data_point (interesting_data_point idp)
21879 {
21880 #ifdef GC_CONFIG_DRIVEN
21881     (interesting_data_per_gc[idp])++;
21882 #else
21883     UNREFERENCED_PARAMETER(idp);
21884 #endif //GC_CONFIG_DRIVEN
21885 }
21886
21887 #ifdef _PREFAST_
21888 #pragma warning(push)
21889 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
21890 #endif //_PREFAST_
21891 void gc_heap::plan_phase (int condemned_gen_number)
21892 {
21893     size_t old_gen2_allocated = 0;
21894     size_t old_gen2_size = 0;
21895
21896     if (condemned_gen_number == (max_generation - 1))
21897     {
21898         old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation));
21899         old_gen2_size = generation_size (max_generation);
21900     }
21901
21902     assert (settings.concurrent == FALSE);
21903
21904     // %type%  category = quote (plan);
21905 #ifdef TIME_GC
21906     unsigned start;
21907     unsigned finish;
21908     start = GetCycleCount32();
21909 #endif //TIME_GC
21910
21911     dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d",
21912                 condemned_gen_number, settings.promotion ? 1 : 0));
21913
21914     generation*  condemned_gen1 = generation_of (condemned_gen_number);
21915
21916 #ifdef MARK_LIST
21917     BOOL use_mark_list = FALSE;
21918     uint8_t** mark_list_next = &mark_list[0];
21919 #ifdef GC_CONFIG_DRIVEN
21920     dprintf (3, ("total number of marked objects: %Id (%Id)",
21921                  (mark_list_index - &mark_list[0]), ((mark_list_end - &mark_list[0]))));
21922     
21923     if (mark_list_index >= (mark_list_end + 1))
21924         mark_list_index = mark_list_end + 1;
21925 #else
21926     dprintf (3, ("mark_list length: %Id",
21927                  (mark_list_index - &mark_list[0])));
21928 #endif //GC_CONFIG_DRIVEN
21929
21930     if ((condemned_gen_number < max_generation) &&
21931         (mark_list_index <= mark_list_end) 
21932 #ifdef BACKGROUND_GC        
21933         && (!recursive_gc_sync::background_running_p())
21934 #endif //BACKGROUND_GC
21935         )
21936     {
21937 #ifndef MULTIPLE_HEAPS
21938         _sort (&mark_list[0], mark_list_index-1, 0);
21939         //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0)));
21940         //verify_qsort_array (&mark_list[0], mark_list_index-1);
21941 #endif //!MULTIPLE_HEAPS
21942         use_mark_list = TRUE;
21943         get_gc_data_per_heap()->set_mechanism_bit (gc_mark_list_bit);
21944     }
21945     else
21946     {
21947         dprintf (3, ("mark_list not used"));
21948     }
21949
21950 #endif //MARK_LIST
21951
21952 #ifdef FEATURE_BASICFREEZE
21953     if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) &&
21954         ro_segments_in_range)
21955     {
21956         sweep_ro_segments (generation_start_segment (condemned_gen1));
21957     }
21958 #endif // FEATURE_BASICFREEZE
21959
21960 #ifndef MULTIPLE_HEAPS
21961     if (shigh != (uint8_t*)0)
21962     {
21963         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21964
21965         PREFIX_ASSUME(seg != NULL);
21966
21967         heap_segment* fseg = seg;
21968         do
21969         {
21970             if (slow > heap_segment_mem (seg) &&
21971                 slow < heap_segment_reserved (seg))
21972             {
21973                 if (seg == fseg)
21974                 {
21975                     uint8_t* o = generation_allocation_start (condemned_gen1) +
21976                         Align (size (generation_allocation_start (condemned_gen1)));
21977                     if (slow > o)
21978                     {
21979                         assert ((slow - o) >= (int)Align (min_obj_size));
21980 #ifdef BACKGROUND_GC
21981                         if (current_c_gc_state == c_gc_state_marking)
21982                         {
21983                             bgc_clear_batch_mark_array_bits (o, slow);
21984                         }
21985 #endif //BACKGROUND_GC
21986                         make_unused_array (o, slow - o);
21987                     }
21988                 } 
21989                 else
21990                 {
21991                     assert (condemned_gen_number == max_generation);
21992                     make_unused_array (heap_segment_mem (seg),
21993                                        slow - heap_segment_mem (seg));
21994                 }
21995             }
21996             if (in_range_for_segment (shigh, seg))
21997             {
21998 #ifdef BACKGROUND_GC
21999                 if (current_c_gc_state == c_gc_state_marking)
22000                 {
22001                     bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg));
22002                 }
22003 #endif //BACKGROUND_GC
22004                 heap_segment_allocated (seg) = shigh + Align (size (shigh));
22005             }
22006             // test if the segment is in the range of [slow, shigh]
22007             if (!((heap_segment_reserved (seg) >= slow) &&
22008                   (heap_segment_mem (seg) <= shigh)))
22009             {
22010                 // shorten it to minimum
22011                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
22012             }
22013             seg = heap_segment_next_rw (seg);
22014         } while (seg);
22015     }
22016     else
22017     {
22018         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
22019
22020         PREFIX_ASSUME(seg != NULL);
22021
22022         heap_segment* sseg = seg;
22023         do
22024         {
22025             // shorten it to minimum
22026             if (seg == sseg)
22027             {
22028                 // no survivors make all generations look empty
22029                 uint8_t* o = generation_allocation_start (condemned_gen1) +
22030                     Align (size (generation_allocation_start (condemned_gen1)));
22031 #ifdef BACKGROUND_GC
22032                 if (current_c_gc_state == c_gc_state_marking)
22033                 {
22034                     bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg));
22035                 }
22036 #endif //BACKGROUND_GC
22037                 heap_segment_allocated (seg) = o;
22038             }
22039             else
22040             {
22041                 assert (condemned_gen_number == max_generation);
22042 #ifdef BACKGROUND_GC
22043                 if (current_c_gc_state == c_gc_state_marking)
22044                 {
22045                     bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg));
22046                 }
22047 #endif //BACKGROUND_GC
22048                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
22049             }
22050             seg = heap_segment_next_rw (seg);
22051         } while (seg);
22052     }
22053
22054 #endif //MULTIPLE_HEAPS
22055
22056     heap_segment*  seg1 = heap_segment_rw (generation_start_segment (condemned_gen1));
22057
22058     PREFIX_ASSUME(seg1 != NULL);
22059
22060     uint8_t*  end = heap_segment_allocated (seg1);
22061     uint8_t*  first_condemned_address = generation_allocation_start (condemned_gen1);
22062     uint8_t*  x = first_condemned_address;
22063
22064     assert (!marked (x));
22065     uint8_t*  plug_end = x;
22066     uint8_t*  tree = 0;
22067     size_t  sequence_number = 0;
22068     uint8_t*  last_node = 0;
22069     size_t  current_brick = brick_of (x);
22070     BOOL  allocate_in_condemned = ((condemned_gen_number == max_generation)||
22071                                    (settings.promotion == FALSE));
22072     int  active_old_gen_number = condemned_gen_number;
22073     int  active_new_gen_number = (allocate_in_condemned ? condemned_gen_number:
22074                                   (1 + condemned_gen_number));
22075     generation*  older_gen = 0;
22076     generation* consing_gen = condemned_gen1;
22077     alloc_list  r_free_list [MAX_BUCKET_COUNT];
22078
22079     size_t r_free_list_space = 0;
22080     size_t r_free_obj_space = 0;
22081     size_t r_older_gen_free_list_allocated = 0;
22082     size_t r_older_gen_condemned_allocated = 0;
22083     size_t r_older_gen_end_seg_allocated = 0;
22084     uint8_t*  r_allocation_pointer = 0;
22085     uint8_t*  r_allocation_limit = 0;
22086     uint8_t* r_allocation_start_region = 0;
22087     heap_segment*  r_allocation_segment = 0;
22088 #ifdef FREE_USAGE_STATS
22089     size_t r_older_gen_free_space[NUM_GEN_POWER2];
22090 #endif //FREE_USAGE_STATS
22091
22092     if ((condemned_gen_number < max_generation))
22093     {
22094         older_gen = generation_of (min (max_generation, 1 + condemned_gen_number));
22095         generation_allocator (older_gen)->copy_to_alloc_list (r_free_list);
22096
22097         r_free_list_space = generation_free_list_space (older_gen);
22098         r_free_obj_space = generation_free_obj_space (older_gen);
22099 #ifdef FREE_USAGE_STATS
22100         memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space));
22101 #endif //FREE_USAGE_STATS
22102         generation_allocate_end_seg_p (older_gen) = FALSE;
22103         r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen);
22104         r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen);
22105         r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen);
22106         r_allocation_limit = generation_allocation_limit (older_gen);
22107         r_allocation_pointer = generation_allocation_pointer (older_gen);
22108         r_allocation_start_region = generation_allocation_context_start_region (older_gen);
22109         r_allocation_segment = generation_allocation_segment (older_gen);
22110         heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen));
22111
22112         PREFIX_ASSUME(start_seg != NULL);
22113
22114         if (start_seg != ephemeral_heap_segment)
22115         {
22116             assert (condemned_gen_number == (max_generation - 1));
22117             while (start_seg && (start_seg != ephemeral_heap_segment))
22118             {
22119                 assert (heap_segment_allocated (start_seg) >=
22120                         heap_segment_mem (start_seg));
22121                 assert (heap_segment_allocated (start_seg) <=
22122                         heap_segment_reserved (start_seg));
22123                 heap_segment_plan_allocated (start_seg) =
22124                     heap_segment_allocated (start_seg);
22125                 start_seg = heap_segment_next_rw (start_seg);
22126             }
22127         }
22128     }
22129
22130     //reset all of the segment allocated sizes
22131     {
22132         heap_segment*  seg2 = heap_segment_rw (generation_start_segment (condemned_gen1));
22133
22134         PREFIX_ASSUME(seg2 != NULL);
22135
22136         while (seg2)
22137         {
22138             heap_segment_plan_allocated (seg2) =
22139                 heap_segment_mem (seg2);
22140             seg2 = heap_segment_next_rw (seg2);
22141         }
22142     }
22143     int  condemned_gn = condemned_gen_number;
22144
22145     int bottom_gen = 0;
22146     init_free_and_plug();
22147
22148     while (condemned_gn >= bottom_gen)
22149     {
22150         generation*  condemned_gen2 = generation_of (condemned_gn);
22151         generation_allocator (condemned_gen2)->clear();
22152         generation_free_list_space (condemned_gen2) = 0;
22153         generation_free_obj_space (condemned_gen2) = 0;
22154         generation_allocation_size (condemned_gen2) = 0;
22155         generation_condemned_allocated (condemned_gen2) = 0; 
22156         generation_pinned_allocated (condemned_gen2) = 0; 
22157         generation_free_list_allocated(condemned_gen2) = 0; 
22158         generation_end_seg_allocated (condemned_gen2) = 0; 
22159         generation_pinned_allocation_sweep_size (condemned_gen2) = 0;
22160         generation_pinned_allocation_compact_size (condemned_gen2) = 0;
22161 #ifdef FREE_USAGE_STATS
22162         generation_pinned_free_obj_space (condemned_gen2) = 0;
22163         generation_allocated_in_pinned_free (condemned_gen2) = 0;
22164         generation_allocated_since_last_pin (condemned_gen2) = 0;
22165 #endif //FREE_USAGE_STATS
22166         generation_plan_allocation_start (condemned_gen2) = 0;
22167         generation_allocation_segment (condemned_gen2) =
22168             heap_segment_rw (generation_start_segment (condemned_gen2));
22169
22170         PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL);
22171
22172         if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment)
22173         {
22174             generation_allocation_pointer (condemned_gen2) =
22175                 heap_segment_mem (generation_allocation_segment (condemned_gen2));
22176         }
22177         else
22178         {
22179             generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2);
22180         }
22181
22182         generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
22183         generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
22184
22185         condemned_gn--;
22186     }
22187
22188     BOOL allocate_first_generation_start = FALSE;
22189     
22190     if (allocate_in_condemned)
22191     {
22192         allocate_first_generation_start = TRUE;
22193     }
22194
22195     dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
22196
22197     demotion_low = MAX_PTR;
22198     demotion_high = heap_segment_allocated (ephemeral_heap_segment);
22199
22200     // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs
22201     // from gen1. They should get promoted to gen2.
22202     demote_gen1_p = !(settings.promotion && 
22203                       (settings.condemned_generation == (max_generation - 1)) && 
22204                       gen_to_condemn_reasons.is_only_condition (gen_low_card_p));
22205
22206     total_ephemeral_size = 0;
22207
22208     print_free_and_plug ("BP");
22209
22210     for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
22211     {
22212         generation* temp_gen = generation_of (gen_idx);
22213
22214         dprintf (2, ("gen%d start %Ix, plan start %Ix",
22215             gen_idx, 
22216             generation_allocation_start (temp_gen),
22217             generation_plan_allocation_start (temp_gen)));
22218     }
22219
22220     BOOL fire_pinned_plug_events_p = EVENT_ENABLED(PinPlugAtGCTime);
22221     size_t last_plug_len = 0;
22222
22223     while (1)
22224     {
22225         if (x >= end)
22226         {
22227             assert (x == end);
22228             assert (heap_segment_allocated (seg1) == end);
22229             heap_segment_allocated (seg1) = plug_end;
22230
22231             current_brick = update_brick_table (tree, current_brick, x, plug_end);
22232             dprintf (3, ("end of seg: new tree, sequence# 0"));
22233             sequence_number = 0;
22234             tree = 0;
22235
22236             if (heap_segment_next_rw (seg1))
22237             {
22238                 seg1 = heap_segment_next_rw (seg1);
22239                 end = heap_segment_allocated (seg1);
22240                 plug_end = x = heap_segment_mem (seg1);
22241                 current_brick = brick_of (x);
22242                 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
22243                 continue;
22244             }
22245             else
22246             {
22247                 break;
22248             }
22249         }
22250
22251         BOOL last_npinned_plug_p = FALSE;
22252         BOOL last_pinned_plug_p = FALSE;
22253
22254         // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned
22255         // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs -
22256         // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it.
22257         uint8_t* last_pinned_plug = 0;
22258         size_t num_pinned_plugs_in_plug = 0;
22259
22260         uint8_t* last_object_in_plug = 0;
22261
22262         while ((x < end) && marked (x))
22263         {
22264             uint8_t*  plug_start = x;
22265             uint8_t*  saved_plug_end = plug_end;
22266             BOOL   pinned_plug_p = FALSE;
22267             BOOL   npin_before_pin_p = FALSE;
22268             BOOL   saved_last_npinned_plug_p = last_npinned_plug_p;
22269             uint8_t*  saved_last_object_in_plug = last_object_in_plug;
22270             BOOL   merge_with_last_pin_p = FALSE;
22271
22272             size_t added_pinning_size = 0;
22273             size_t artificial_pinned_size = 0;
22274
22275             store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p, 
22276                                  last_pinned_plug, pinned_plug_p, last_object_in_plug, 
22277                                  merge_with_last_pin_p, last_plug_len);
22278
22279 #ifdef FEATURE_STRUCTALIGN
22280             int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment();
22281             size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET;
22282 #endif // FEATURE_STRUCTALIGN
22283
22284             {
22285                 uint8_t* xl = x;
22286                 while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p))
22287                 {
22288                     assert (xl < end);
22289                     if (pinned(xl))
22290                     {
22291                         clear_pinned (xl);
22292                     }
22293 #ifdef FEATURE_STRUCTALIGN
22294                     else
22295                     {
22296                         int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment();
22297                         if (obj_requiredAlignment > requiredAlignment)
22298                         {
22299                             requiredAlignment = obj_requiredAlignment;
22300                             alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET;
22301                         }
22302                     }
22303 #endif // FEATURE_STRUCTALIGN
22304
22305                     clear_marked (xl);
22306
22307                     dprintf(4, ("+%Ix+", (size_t)xl));
22308                     assert ((size (xl) > 0));
22309                     assert ((size (xl) <= loh_size_threshold));
22310
22311                     last_object_in_plug = xl;
22312
22313                     xl = xl + Align (size (xl));
22314                     Prefetch (xl);
22315                 }
22316
22317                 BOOL next_object_marked_p = ((xl < end) && marked (xl));
22318
22319                 if (pinned_plug_p)
22320                 {
22321                     // If it is pinned we need to extend to the next marked object as we can't use part of
22322                     // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all
22323                     // references but for now I am just using the next non pinned object for that).
22324                     if (next_object_marked_p) 
22325                     {
22326                         clear_marked (xl);
22327                         last_object_in_plug = xl;
22328                         size_t extra_size = Align (size (xl));
22329                         xl = xl + extra_size;
22330                         added_pinning_size = extra_size;
22331                     }
22332                 }
22333                 else
22334                 {
22335                     if (next_object_marked_p)
22336                         npin_before_pin_p = TRUE;
22337                 }
22338
22339                 assert (xl <= end);
22340                 x = xl;
22341             }
22342             dprintf (3, ( "%Ix[", (size_t)x));
22343             plug_end = x;
22344             size_t ps = plug_end - plug_start;
22345             last_plug_len = ps;
22346             dprintf (3, ( "%Ix[(%Ix)", (size_t)x, ps));
22347             uint8_t*  new_address = 0;
22348
22349             if (!pinned_plug_p)
22350             {
22351                 if (allocate_in_condemned &&
22352                     (settings.condemned_generation == max_generation) &&
22353                     (ps > OS_PAGE_SIZE))
22354                 {
22355                     ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen);
22356                     //reloc should >=0 except when we relocate
22357                     //across segments and the dest seg is higher then the src
22358
22359                     if ((ps > (8*OS_PAGE_SIZE)) &&
22360                         (reloc > 0) &&
22361                         ((size_t)reloc < (ps/16)))
22362                     {
22363                         dprintf (3, ("Pinning %Ix; reloc would have been: %Ix",
22364                                      (size_t)plug_start, reloc));
22365                         // The last plug couldn't have been a npinned plug or it would have
22366                         // included this plug.
22367                         assert (!saved_last_npinned_plug_p);
22368
22369                         if (last_pinned_plug)
22370                         {
22371                             dprintf (3, ("artificially pinned plug merged with last pinned plug"));
22372                             merge_with_last_pin_p = TRUE;
22373                         }
22374                         else
22375                         {
22376                             enque_pinned_plug (plug_start, FALSE, 0);
22377                             last_pinned_plug = plug_start;
22378                         }
22379
22380                         convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
22381                                                 ps, artificial_pinned_size);
22382                     }
22383                 }
22384             }
22385
22386             if (allocate_first_generation_start)
22387             {
22388                 allocate_first_generation_start = FALSE;
22389                 plan_generation_start (condemned_gen1, consing_gen, plug_start);
22390                 assert (generation_plan_allocation_start (condemned_gen1));
22391             }
22392
22393             if (seg1 == ephemeral_heap_segment)
22394             {
22395                 process_ephemeral_boundaries (plug_start, active_new_gen_number,
22396                                               active_old_gen_number,
22397                                               consing_gen,
22398                                               allocate_in_condemned);
22399             }
22400
22401             dprintf (3, ("adding %Id to gen%d surv", ps, active_old_gen_number));
22402
22403             dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number);
22404             dd_survived_size (dd_active_old) += ps;
22405
22406             BOOL convert_to_pinned_p = FALSE;
22407
22408             if (!pinned_plug_p)
22409             {
22410 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
22411                 dd_num_npinned_plugs (dd_active_old)++;
22412 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
22413
22414                 add_gen_plug (active_old_gen_number, ps);
22415
22416                 if (allocate_in_condemned)
22417                 {
22418                     verify_pins_with_post_plug_info("before aic");
22419
22420                     new_address =
22421                         allocate_in_condemned_generations (consing_gen,
22422                                                            ps,
22423                                                            active_old_gen_number,
22424 #ifdef SHORT_PLUGS
22425                                                            &convert_to_pinned_p,
22426                                                            (npin_before_pin_p ? plug_end : 0),
22427                                                            seg1,
22428 #endif //SHORT_PLUGS
22429                                                            plug_start REQD_ALIGN_AND_OFFSET_ARG);
22430                     verify_pins_with_post_plug_info("after aic");
22431                 }
22432                 else
22433                 {
22434                     new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG);
22435
22436                     if (new_address != 0)
22437                     {
22438                         if (settings.condemned_generation == (max_generation - 1))
22439                         {
22440                             dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)",
22441                                 plug_start, plug_end,
22442                                 (size_t)new_address, (size_t)new_address + (plug_end - plug_start),
22443                                 (size_t)(plug_end - plug_start)));
22444                         }
22445                     }
22446                     else
22447                     {
22448                         if (generation_allocator(older_gen)->discard_if_no_fit_p())
22449                         {
22450                             allocate_in_condemned = TRUE;
22451                         }
22452
22453                         new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number, 
22454 #ifdef SHORT_PLUGS
22455                                                                          &convert_to_pinned_p,
22456                                                                          (npin_before_pin_p ? plug_end : 0),
22457                                                                          seg1,
22458 #endif //SHORT_PLUGS
22459                                                                          plug_start REQD_ALIGN_AND_OFFSET_ARG);
22460                     }
22461                 }
22462
22463                 if (convert_to_pinned_p)
22464                 {
22465                     assert (last_npinned_plug_p != FALSE);
22466                     assert (last_pinned_plug_p == FALSE);
22467                     convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
22468                                             ps, artificial_pinned_size);
22469                     enque_pinned_plug (plug_start, FALSE, 0);
22470                     last_pinned_plug = plug_start;
22471                 }
22472                 else
22473                 {
22474                     if (!new_address)
22475                     {
22476                         //verify that we are at then end of the ephemeral segment
22477                         assert (generation_allocation_segment (consing_gen) ==
22478                                 ephemeral_heap_segment);
22479                         //verify that we are near the end
22480                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) <
22481                                 heap_segment_allocated (ephemeral_heap_segment));
22482                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) >
22483                                 (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size)));
22484                     }
22485                     else
22486                     {
22487 #ifdef SIMPLE_DPRINTF
22488                         dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix(%d)",
22489                             (size_t)(node_gap_size (plug_start)), 
22490                             plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address),
22491                                 (size_t)new_address + ps, ps, 
22492                                 (is_plug_padded (plug_start) ? 1 : 0)));
22493 #endif //SIMPLE_DPRINTF
22494
22495 #ifdef SHORT_PLUGS
22496                         if (is_plug_padded (plug_start))
22497                         {
22498                             dprintf (3, ("%Ix was padded", plug_start));
22499                             dd_padding_size (dd_active_old) += Align (min_obj_size);
22500                         }
22501 #endif //SHORT_PLUGS
22502                     }
22503                 }
22504             }
22505
22506             if (pinned_plug_p)
22507             {
22508                 if (fire_pinned_plug_events_p)
22509                 {
22510                     FIRE_EVENT(PinPlugAtGCTime, plug_start, plug_end, 
22511                                (merge_with_last_pin_p ? 0 : (uint8_t*)node_gap_size (plug_start)));
22512                 }
22513
22514                 if (merge_with_last_pin_p)
22515                 {
22516                     merge_with_last_pinned_plug (last_pinned_plug, ps);
22517                 }
22518                 else
22519                 {
22520                     assert (last_pinned_plug == plug_start);
22521                     set_pinned_info (plug_start, ps, consing_gen);
22522                 }
22523
22524                 new_address = plug_start;
22525
22526                 dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix](m:%d)",
22527                             (size_t)(node_gap_size (plug_start)), (size_t)plug_start,
22528                             (size_t)plug_end, ps,
22529                             (merge_with_last_pin_p ? 1 : 0)));
22530
22531                 dprintf (3, ("adding %Id to gen%d pinned surv", plug_end - plug_start, active_old_gen_number));
22532                 dd_pinned_survived_size (dd_active_old) += plug_end - plug_start;
22533                 dd_added_pinned_size (dd_active_old) += added_pinning_size;
22534                 dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size;
22535
22536                 if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1)))
22537                 {
22538                     last_gen1_pin_end = plug_end;
22539                 }
22540             }
22541
22542 #ifdef _DEBUG
22543             // detect forward allocation in the same segment
22544             assert (!((new_address > plug_start) &&
22545                 (new_address < heap_segment_reserved (seg1))));
22546 #endif //_DEBUG
22547
22548             if (!merge_with_last_pin_p)
22549             {
22550                 if (current_brick != brick_of (plug_start))
22551                 {
22552                     current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end);
22553                     sequence_number = 0;
22554                     tree = 0;
22555                 }
22556
22557                 set_node_relocation_distance (plug_start, (new_address - plug_start));
22558                 if (last_node && (node_relocation_distance (last_node) ==
22559                                   (node_relocation_distance (plug_start) +
22560                                    (ptrdiff_t)node_gap_size (plug_start))))
22561                 {
22562                     //dprintf(3,( " Lb"));
22563                     dprintf (3, ("%Ix Lb", plug_start));
22564                     set_node_left (plug_start);
22565                 }
22566                 if (0 == sequence_number)
22567                 {
22568                     dprintf (2, ("sn: 0, tree is set to %Ix", plug_start));
22569                     tree = plug_start;
22570                 }
22571
22572                 verify_pins_with_post_plug_info("before insert node");
22573
22574                 tree = insert_node (plug_start, ++sequence_number, tree, last_node);
22575                 dprintf (3, ("tree is %Ix (b: %Ix) after insert_node", tree, brick_of (tree)));
22576                 last_node = plug_start;
22577
22578 #ifdef _DEBUG
22579                 // If we detect if the last plug is pinned plug right before us, we should save this gap info
22580                 if (!pinned_plug_p)
22581                 {
22582                     if (mark_stack_tos > 0)
22583                     {
22584                         mark& m = mark_stack_array[mark_stack_tos - 1];
22585                         if (m.has_post_plug_info())
22586                         {
22587                             uint8_t* post_plug_info_start = m.saved_post_plug_info_start;
22588                             size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap));
22589                             if ((uint8_t*)current_plug_gap_start == post_plug_info_start)
22590                             {
22591                                 dprintf (3, ("Ginfo: %Ix, %Ix, %Ix",
22592                                     *current_plug_gap_start, *(current_plug_gap_start + 1),
22593                                     *(current_plug_gap_start + 2)));
22594                                 memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair));
22595                             }
22596                         }
22597                     }
22598                 }
22599 #endif //_DEBUG
22600
22601                 verify_pins_with_post_plug_info("after insert node");
22602             }
22603         }
22604         
22605         if (num_pinned_plugs_in_plug > 1)
22606         {
22607             dprintf (3, ("more than %Id pinned plugs in this plug", num_pinned_plugs_in_plug));
22608         }
22609
22610         {
22611 #ifdef MARK_LIST
22612             if (use_mark_list)
22613             {
22614                while ((mark_list_next < mark_list_index) &&
22615                       (*mark_list_next <= x))
22616                {
22617                    mark_list_next++;
22618                }
22619                if ((mark_list_next < mark_list_index)
22620 #ifdef MULTIPLE_HEAPS
22621                    && (*mark_list_next < end) //for multiple segments
22622 #endif //MULTIPLE_HEAPS
22623                    )
22624                    x = *mark_list_next;
22625                else
22626                    x = end;
22627             }
22628             else
22629 #endif //MARK_LIST
22630             {
22631                 uint8_t* xl = x;
22632 #ifdef BACKGROUND_GC
22633                 if (current_c_gc_state == c_gc_state_marking)
22634                 {
22635                     assert (recursive_gc_sync::background_running_p());
22636                     while ((xl < end) && !marked (xl))
22637                     {
22638                         dprintf (4, ("-%Ix-", (size_t)xl));
22639                         assert ((size (xl) > 0));
22640                         background_object_marked (xl, TRUE);
22641                         xl = xl + Align (size (xl));
22642                         Prefetch (xl);
22643                     }
22644                 }
22645                 else
22646 #endif //BACKGROUND_GC
22647                 {
22648                     while ((xl < end) && !marked (xl))
22649                     {
22650                         dprintf (4, ("-%Ix-", (size_t)xl));
22651                         assert ((size (xl) > 0));
22652                         xl = xl + Align (size (xl));
22653                         Prefetch (xl);
22654                     }
22655                 }
22656                 assert (xl <= end);
22657                 x = xl;
22658             }
22659         }
22660     }
22661
22662     while (!pinned_plug_que_empty_p())
22663     {
22664         if (settings.promotion)
22665         {
22666             uint8_t* pplug = pinned_plug (oldest_pin());
22667             if (in_range_for_segment (pplug, ephemeral_heap_segment))
22668             {
22669                 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
22670                 //allocate all of the generation gaps
22671                 while (active_new_gen_number > 0)
22672                 {
22673                     active_new_gen_number--;
22674
22675                     if (active_new_gen_number == (max_generation - 1))
22676                     {
22677                         maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
22678                         if (!demote_gen1_p)
22679                             advance_pins_for_demotion (consing_gen);
22680                     }
22681
22682                     generation* gen = generation_of (active_new_gen_number);
22683                     plan_generation_start (gen, consing_gen, 0);
22684
22685                     if (demotion_low == MAX_PTR)
22686                     {
22687                         demotion_low = pplug;
22688                         dprintf (3, ("end plan: dlow->%Ix", demotion_low));
22689                     }
22690
22691                     dprintf (2, ("(%d)gen%d plan start: %Ix", 
22692                                   heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen)));
22693                     assert (generation_plan_allocation_start (gen));
22694                 }
22695             }
22696         }
22697
22698         if (pinned_plug_que_empty_p())
22699             break;
22700
22701         size_t  entry = deque_pinned_plug();
22702         mark*  m = pinned_plug_of (entry);
22703         uint8_t*  plug = pinned_plug (m);
22704         size_t  len = pinned_len (m);
22705
22706         // detect pinned block in different segment (later) than
22707         // allocation segment
22708         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen));
22709
22710         while ((plug < generation_allocation_pointer (consing_gen)) ||
22711                (plug >= heap_segment_allocated (nseg)))
22712         {
22713             assert ((plug < heap_segment_mem (nseg)) ||
22714                     (plug > heap_segment_reserved (nseg)));
22715             //adjust the end of the segment to be the end of the plug
22716             assert (generation_allocation_pointer (consing_gen)>=
22717                     heap_segment_mem (nseg));
22718             assert (generation_allocation_pointer (consing_gen)<=
22719                     heap_segment_committed (nseg));
22720
22721             heap_segment_plan_allocated (nseg) =
22722                 generation_allocation_pointer (consing_gen);
22723             //switch allocation segment
22724             nseg = heap_segment_next_rw (nseg);
22725             generation_allocation_segment (consing_gen) = nseg;
22726             //reset the allocation pointer and limits
22727             generation_allocation_pointer (consing_gen) =
22728                 heap_segment_mem (nseg);
22729         }
22730
22731         set_new_pin_info (m, generation_allocation_pointer (consing_gen));
22732         dprintf (2, ("pin %Ix b: %Ix->%Ix", plug, brick_of (plug),
22733             (size_t)(brick_table[brick_of (plug)])));
22734
22735         generation_allocation_pointer (consing_gen) = plug + len;
22736         generation_allocation_limit (consing_gen) =
22737             generation_allocation_pointer (consing_gen);
22738         //Add the size of the pinned plug to the right pinned allocations
22739         //find out which gen this pinned plug came from 
22740         int frgn = object_gennum (plug);
22741         if ((frgn != (int)max_generation) && settings.promotion)
22742         {
22743             generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
22744         }
22745
22746     }
22747
22748     plan_generation_starts (consing_gen);
22749     print_free_and_plug ("AP");
22750
22751     {
22752 #ifdef SIMPLE_DPRINTF
22753         for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
22754         {
22755             generation* temp_gen = generation_of (gen_idx);
22756             dynamic_data* temp_dd = dynamic_data_of (gen_idx);
22757
22758             int added_pinning_ratio = 0;
22759             int artificial_pinned_ratio = 0;
22760
22761             if (dd_pinned_survived_size (temp_dd) != 0)
22762             {
22763                 added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22764                 artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22765             }
22766
22767             size_t padding_size = 
22768 #ifdef SHORT_PLUGS
22769                 dd_padding_size (temp_dd);
22770 #else
22771                 0;
22772 #endif //SHORT_PLUGS
22773             dprintf (1, ("gen%d: %Ix, %Ix(%Id), NON PIN alloc: %Id, pin com: %Id, sweep: %Id, surv: %Id, pinsurv: %Id(%d%% added, %d%% art), np surv: %Id, pad: %Id",
22774                 gen_idx, 
22775                 generation_allocation_start (temp_gen),
22776                 generation_plan_allocation_start (temp_gen),
22777                 (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)),
22778                 generation_allocation_size (temp_gen),
22779                 generation_pinned_allocation_compact_size (temp_gen),
22780                 generation_pinned_allocation_sweep_size (temp_gen),
22781                 dd_survived_size (temp_dd),
22782                 dd_pinned_survived_size (temp_dd),
22783                 added_pinning_ratio,
22784                 artificial_pinned_ratio,
22785                 (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)),
22786                 padding_size));
22787         }
22788 #endif //SIMPLE_DPRINTF
22789     }
22790
22791     if (settings.condemned_generation == (max_generation - 1 ))
22792     {
22793         size_t plan_gen2_size = generation_plan_size (max_generation);
22794         size_t growth = plan_gen2_size - old_gen2_size;
22795
22796         generation* older_gen = generation_of (settings.condemned_generation + 1);
22797         size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space;
22798         size_t free_list_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated;
22799         size_t end_seg_allocated = generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated;
22800         size_t condemned_allocated = generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated;
22801
22802         if (growth > 0)
22803         {
22804             dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, condemned alloc: %Id", 
22805                          growth, end_seg_allocated, condemned_allocated));
22806
22807             maxgen_size_inc_p = true;
22808         }
22809         else
22810         {
22811             dprintf (2, ("gen2 shrank %Id (end seg alloc: %Id, , condemned alloc: %Id, gen1 c alloc: %Id", 
22812                          (old_gen2_size - plan_gen2_size), end_seg_allocated, condemned_allocated,
22813                          generation_condemned_allocated (generation_of (max_generation - 1))));
22814         }
22815
22816         dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id",
22817                     r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen),
22818                     r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen), 
22819                     r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen)));
22820
22821         dprintf (1, ("this GC did %Id free list alloc(%Id bytes free space rejected)",
22822             free_list_allocated, rejected_free_space));
22823
22824         maxgen_size_increase* maxgen_size_info = &(get_gc_data_per_heap()->maxgen_size_info);
22825         maxgen_size_info->free_list_allocated = free_list_allocated;
22826         maxgen_size_info->free_list_rejected = rejected_free_space;
22827         maxgen_size_info->end_seg_allocated = end_seg_allocated;
22828         maxgen_size_info->condemned_allocated = condemned_allocated;
22829         maxgen_size_info->pinned_allocated = maxgen_pinned_compact_before_advance;
22830         maxgen_size_info->pinned_allocated_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)) - maxgen_pinned_compact_before_advance;
22831
22832 #ifdef FREE_USAGE_STATS
22833         int free_list_efficiency = 0;
22834         if ((free_list_allocated + rejected_free_space) != 0)
22835             free_list_efficiency = (int)(((float) (free_list_allocated) / (float)(free_list_allocated + rejected_free_space)) * (float)100);
22836
22837         int running_free_list_efficiency = (int)(generation_allocator_efficiency(older_gen)*100);
22838
22839         dprintf (1, ("gen%d free list alloc effi: %d%%, current effi: %d%%",
22840                     older_gen->gen_num,
22841                     free_list_efficiency, running_free_list_efficiency));
22842
22843         dprintf (1, ("gen2 free list change"));
22844         for (int j = 0; j < NUM_GEN_POWER2; j++)
22845         {
22846             dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id", 
22847                 heap_number, 
22848                 settings.gc_index,
22849                 (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j], 
22850                 (ptrdiff_t)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]),
22851                 (generation_of(max_generation - 1))->gen_plugs[j]));
22852         }
22853 #endif //FREE_USAGE_STATS
22854     }
22855
22856     size_t fragmentation =
22857         generation_fragmentation (generation_of (condemned_gen_number),
22858                                   consing_gen,
22859                                   heap_segment_allocated (ephemeral_heap_segment));
22860
22861     dprintf (2,("Fragmentation: %Id", fragmentation));
22862     dprintf (2,("---- End of Plan phase ----"));
22863
22864 #ifdef TIME_GC
22865     finish = GetCycleCount32();
22866     plan_time = finish - start;
22867 #endif //TIME_GC
22868
22869     // We may update write barrier code.  We assume here EE has been suspended if we are on a GC thread.
22870     assert(IsGCInProgress());
22871
22872     BOOL should_expand = FALSE;
22873     BOOL should_compact= FALSE;
22874     ephemeral_promotion = FALSE;
22875
22876 #ifdef BIT64
22877     if ((!settings.concurrent) &&
22878         !provisional_mode_triggered &&
22879         ((condemned_gen_number < max_generation) && 
22880          ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95))))
22881     {
22882         dprintf (GTC_LOG, ("gen0 reduction count is %d, condemning %d, mem load %d",
22883                      settings.gen0_reduction_count,
22884                      condemned_gen_number,
22885                      settings.entry_memory_load));
22886         should_compact = TRUE;
22887
22888         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, 
22889             ((settings.gen0_reduction_count > 0) ? compact_fragmented_gen0 : compact_high_mem_load));
22890
22891         if ((condemned_gen_number >= (max_generation - 1)) && 
22892             dt_low_ephemeral_space_p (tuning_deciding_expansion))
22893         {
22894             dprintf (GTC_LOG, ("Not enough space for all ephemeral generations with compaction"));
22895             should_expand = TRUE;
22896         }
22897     }
22898     else
22899     {
22900 #endif // BIT64
22901         should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand);
22902 #ifdef BIT64
22903     }
22904 #endif // BIT64
22905
22906 #ifdef FEATURE_LOH_COMPACTION
22907     loh_compacted_p = FALSE;
22908 #endif //FEATURE_LOH_COMPACTION
22909
22910     if (condemned_gen_number == max_generation)
22911     {
22912 #ifdef FEATURE_LOH_COMPACTION
22913         if (settings.loh_compaction)
22914         {
22915             if (plan_loh())
22916             {
22917                 should_compact = TRUE;
22918                 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_loh_forced);
22919                 loh_compacted_p = TRUE;
22920             }
22921         }
22922         else
22923         {
22924             if ((heap_number == 0) && (loh_pinned_queue))
22925             {
22926                 loh_pinned_queue_decay--;
22927
22928                 if (!loh_pinned_queue_decay)
22929                 {
22930                     delete loh_pinned_queue;
22931                     loh_pinned_queue = 0;
22932                 }
22933             }
22934         }
22935
22936         if (!loh_compacted_p)
22937 #endif //FEATURE_LOH_COMPACTION
22938         {
22939             GCToEEInterface::DiagWalkLOHSurvivors(__this);
22940             sweep_large_objects();
22941         }
22942     }
22943     else
22944     {
22945         settings.loh_compaction = FALSE;
22946     }
22947
22948 #ifdef MULTIPLE_HEAPS
22949
22950     new_heap_segment = NULL;
22951
22952     if (should_compact && should_expand)
22953         gc_policy = policy_expand;
22954     else if (should_compact)
22955         gc_policy = policy_compact;
22956     else
22957         gc_policy = policy_sweep;
22958
22959     //vote for result of should_compact
22960     dprintf (3, ("Joining for compaction decision"));
22961     gc_t_join.join(this, gc_join_decide_on_compaction);
22962     if (gc_t_join.joined())
22963     {
22964         //safe place to delete large heap segments
22965         if (condemned_gen_number == max_generation)
22966         {
22967             for (int i = 0; i < n_heaps; i++)
22968             {
22969                 g_heaps [i]->rearrange_large_heap_segments ();
22970             }
22971         }
22972
22973         if (maxgen_size_inc_p && provisional_mode_triggered)
22974         {
22975             pm_trigger_full_gc = true;
22976             dprintf (GTC_LOG, ("in PM: maxgen size inc, doing a sweeping gen1 and trigger NGC2"));
22977         }
22978         else
22979         {
22980             settings.demotion = FALSE;
22981             int pol_max = policy_sweep;
22982 #ifdef GC_CONFIG_DRIVEN
22983             BOOL is_compaction_mandatory = FALSE;
22984 #endif //GC_CONFIG_DRIVEN
22985
22986             int i;
22987             for (i = 0; i < n_heaps; i++)
22988             {
22989                 if (pol_max < g_heaps[i]->gc_policy)
22990                     pol_max = policy_compact;
22991                 // set the demotion flag is any of the heap has demotion
22992                 if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low)
22993                 {
22994                     (g_heaps[i]->get_gc_data_per_heap())->set_mechanism_bit (gc_demotion_bit);
22995                     settings.demotion = TRUE;
22996                 }
22997
22998 #ifdef GC_CONFIG_DRIVEN
22999                 if (!is_compaction_mandatory)
23000                 {
23001                     int compact_reason = (g_heaps[i]->get_gc_data_per_heap())->get_mechanism (gc_heap_compact);
23002                     if (compact_reason >= 0)
23003                     {
23004                         if (gc_heap_compact_reason_mandatory_p[compact_reason])
23005                             is_compaction_mandatory = TRUE;
23006                     }
23007                 }
23008 #endif //GC_CONFIG_DRIVEN
23009             }
23010
23011 #ifdef GC_CONFIG_DRIVEN
23012             if (!is_compaction_mandatory)
23013             {
23014                 // If compaction is not mandatory we can feel free to change it to a sweeping GC.
23015                 // Note that we may want to change this to only checking every so often instead of every single GC.
23016                 if (should_do_sweeping_gc (pol_max >= policy_compact))
23017                 {
23018                     pol_max = policy_sweep;
23019                 }
23020                 else
23021                 {
23022                     if (pol_max == policy_sweep)
23023                         pol_max = policy_compact;
23024                 }
23025             }
23026 #endif //GC_CONFIG_DRIVEN
23027
23028             for (i = 0; i < n_heaps; i++)
23029             {
23030                 if (pol_max > g_heaps[i]->gc_policy)
23031                     g_heaps[i]->gc_policy = pol_max;
23032                 //get the segment while we are serialized
23033                 if (g_heaps[i]->gc_policy == policy_expand)
23034                 {
23035                     g_heaps[i]->new_heap_segment =
23036                         g_heaps[i]->soh_get_segment_to_expand();
23037                     if (!g_heaps[i]->new_heap_segment)
23038                     {
23039                         set_expand_in_full_gc (condemned_gen_number);
23040                         //we are out of memory, cancel the expansion
23041                         g_heaps[i]->gc_policy = policy_compact;
23042                     }
23043                 }
23044             }
23045
23046             BOOL is_full_compacting_gc = FALSE;
23047
23048             if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation))
23049             {
23050                 full_gc_counts[gc_type_compacting]++;
23051                 is_full_compacting_gc = TRUE;
23052             }
23053
23054             for (i = 0; i < n_heaps; i++)
23055             {
23056                 //copy the card and brick tables
23057                 if (g_gc_card_table!= g_heaps[i]->card_table)
23058                 {
23059                     g_heaps[i]->copy_brick_card_table();
23060                 }
23061
23062                 if (is_full_compacting_gc)
23063                 {
23064                     g_heaps[i]->loh_alloc_since_cg = 0;
23065                 }
23066             }
23067         }
23068
23069         //start all threads on the roots.
23070         dprintf(3, ("Starting all gc threads after compaction decision"));
23071         gc_t_join.restart();
23072     }
23073
23074     //reset the local variable accordingly
23075     should_compact = (gc_policy >= policy_compact);
23076     should_expand  = (gc_policy >= policy_expand);
23077
23078 #else //MULTIPLE_HEAPS
23079
23080     //safe place to delete large heap segments
23081     if (condemned_gen_number == max_generation)
23082     {
23083         rearrange_large_heap_segments ();
23084     }
23085
23086     if (maxgen_size_inc_p && provisional_mode_triggered)
23087     {
23088         pm_trigger_full_gc = true;
23089         dprintf (GTC_LOG, ("in PM: maxgen size inc, doing a sweeping gen1 and trigger NGC2"));
23090     }
23091     else
23092     {
23093         settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE);
23094         if (settings.demotion)
23095             get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
23096
23097 #ifdef GC_CONFIG_DRIVEN
23098         BOOL is_compaction_mandatory = FALSE;
23099         int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
23100         if (compact_reason >= 0)
23101             is_compaction_mandatory = gc_heap_compact_reason_mandatory_p[compact_reason];
23102
23103         if (!is_compaction_mandatory)
23104         {
23105             if (should_do_sweeping_gc (should_compact))
23106                 should_compact = FALSE;
23107             else
23108                 should_compact = TRUE;
23109         }
23110 #endif //GC_CONFIG_DRIVEN
23111
23112         if (should_compact && (condemned_gen_number == max_generation))
23113         {
23114             full_gc_counts[gc_type_compacting]++;
23115             loh_alloc_since_cg = 0;
23116         }
23117     }
23118 #endif //MULTIPLE_HEAPS
23119
23120     if (!pm_trigger_full_gc && pm_stress_on && provisional_mode_triggered)
23121     {
23122         if ((settings.condemned_generation == (max_generation - 1)) &&
23123             ((settings.gc_index % 5) == 0))
23124         {
23125             pm_trigger_full_gc = true;
23126         }
23127     }
23128
23129     if (settings.condemned_generation == (max_generation - 1))
23130     {
23131         if (provisional_mode_triggered)
23132         {
23133             if (should_expand)
23134             {
23135                 should_expand = FALSE;
23136                 dprintf (GTC_LOG, ("h%d in PM cannot expand", heap_number));
23137             }
23138         }
23139
23140         if (pm_trigger_full_gc)
23141         {
23142             should_compact = FALSE;
23143             dprintf (GTC_LOG, ("h%d PM doing sweeping", heap_number));
23144         }
23145     }
23146
23147     if (should_compact)
23148     {
23149         dprintf (2,( "**** Doing Compacting GC ****"));
23150
23151         if (should_expand)
23152         {
23153 #ifndef MULTIPLE_HEAPS
23154             heap_segment* new_heap_segment = soh_get_segment_to_expand();
23155 #endif //!MULTIPLE_HEAPS
23156             if (new_heap_segment)
23157             {
23158                 consing_gen = expand_heap(condemned_gen_number,
23159                                           consing_gen,
23160                                           new_heap_segment);
23161             }
23162
23163             // If we couldn't get a new segment, or we were able to 
23164             // reserve one but no space to commit, we couldn't
23165             // expand heap.
23166             if (ephemeral_heap_segment != new_heap_segment)
23167             {
23168                 set_expand_in_full_gc (condemned_gen_number);
23169                 should_expand = FALSE;
23170             }
23171         }
23172         generation_allocation_limit (condemned_gen1) =
23173             generation_allocation_pointer (condemned_gen1);
23174         if ((condemned_gen_number < max_generation))
23175         {
23176             generation_allocator (older_gen)->commit_alloc_list_changes();
23177
23178             // Fix the allocation area of the older generation
23179             fix_older_allocation_area (older_gen);
23180         }
23181         assert (generation_allocation_segment (consing_gen) ==
23182                 ephemeral_heap_segment);
23183
23184         GCToEEInterface::DiagWalkSurvivors(__this);
23185
23186         relocate_phase (condemned_gen_number, first_condemned_address);
23187         compact_phase (condemned_gen_number, first_condemned_address,
23188                        (!settings.demotion && settings.promotion));
23189         fix_generation_bounds (condemned_gen_number, consing_gen);
23190         assert (generation_allocation_limit (youngest_generation) ==
23191                 generation_allocation_pointer (youngest_generation));
23192         if (condemned_gen_number >= (max_generation -1))
23193         {
23194 #ifdef MULTIPLE_HEAPS
23195             // this needs be serialized just because we have one
23196             // segment_standby_list/seg_table for all heaps. We should make it at least
23197             // so that when hoarding is not on we don't need this join because
23198             // decommitting memory can take a long time.
23199             //must serialize on deleting segments
23200             gc_t_join.join(this, gc_join_rearrange_segs_compaction);
23201             if (gc_t_join.joined())
23202             {
23203                 for (int i = 0; i < n_heaps; i++)
23204                 {
23205                     g_heaps[i]->rearrange_heap_segments(TRUE);
23206                 }
23207                 gc_t_join.restart();
23208             }
23209 #else
23210             rearrange_heap_segments(TRUE);
23211 #endif //MULTIPLE_HEAPS
23212
23213             if (should_expand)
23214             {
23215                 //fix the start_segment for the ephemeral generations
23216                 for (int i = 0; i < max_generation; i++)
23217                 {
23218                     generation* gen = generation_of (i);
23219                     generation_start_segment (gen) = ephemeral_heap_segment;
23220                     generation_allocation_segment (gen) = ephemeral_heap_segment;
23221                 }
23222             }
23223         }
23224
23225         {
23226 #ifdef FEATURE_PREMORTEM_FINALIZATION
23227             finalize_queue->UpdatePromotedGenerations (condemned_gen_number,
23228                                                        (!settings.demotion && settings.promotion));
23229 #endif // FEATURE_PREMORTEM_FINALIZATION
23230
23231 #ifdef MULTIPLE_HEAPS
23232             dprintf(3, ("Joining after end of compaction"));
23233             gc_t_join.join(this, gc_join_adjust_handle_age_compact);
23234             if (gc_t_join.joined())
23235 #endif //MULTIPLE_HEAPS
23236             {
23237 #ifdef MULTIPLE_HEAPS
23238                 //join all threads to make sure they are synchronized
23239                 dprintf(3, ("Restarting after Promotion granted"));
23240                 gc_t_join.restart();
23241 #endif //MULTIPLE_HEAPS
23242             }
23243
23244             ScanContext sc;
23245             sc.thread_number = heap_number;
23246             sc.promotion = FALSE;
23247             sc.concurrent = FALSE;
23248             // new generations bounds are set can call this guy
23249             if (settings.promotion && !settings.demotion)
23250             {
23251                 dprintf (2, ("Promoting EE roots for gen %d",
23252                              condemned_gen_number));
23253                 GCScan::GcPromotionsGranted(condemned_gen_number,
23254                                                 max_generation, &sc);
23255             }
23256             else if (settings.demotion)
23257             {
23258                 dprintf (2, ("Demoting EE roots for gen %d",
23259                              condemned_gen_number));
23260                 GCScan::GcDemote (condemned_gen_number, max_generation, &sc);
23261             }
23262         }
23263
23264         {
23265             gen0_big_free_spaces = 0;
23266
23267             reset_pinned_queue_bos();
23268             unsigned int  gen_number = min (max_generation, 1 + condemned_gen_number);
23269             generation*  gen = generation_of (gen_number);
23270             uint8_t*  low = generation_allocation_start (generation_of (gen_number-1));
23271             uint8_t*  high =  heap_segment_allocated (ephemeral_heap_segment);
23272             
23273             while (!pinned_plug_que_empty_p())
23274             {
23275                 mark*  m = pinned_plug_of (deque_pinned_plug());
23276                 size_t len = pinned_len (m);
23277                 uint8_t*  arr = (pinned_plug (m) - len);
23278                 dprintf(3,("free [%Ix %Ix[ pin",
23279                             (size_t)arr, (size_t)arr + len));
23280                 if (len != 0)
23281                 {
23282                     assert (len >= Align (min_obj_size));
23283                     make_unused_array (arr, len);
23284                     // fix fully contained bricks + first one
23285                     // if the array goes beyond the first brick
23286                     size_t start_brick = brick_of (arr);
23287                     size_t end_brick = brick_of (arr + len);
23288                     if (end_brick != start_brick)
23289                     {
23290                         dprintf (3,
23291                                     ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix",
23292                                     start_brick, end_brick, (size_t)arr));
23293                         set_brick (start_brick,
23294                                     arr - brick_address (start_brick));
23295                         size_t brick = start_brick+1;
23296                         while (brick < end_brick)
23297                         {
23298                             set_brick (brick, start_brick - brick);
23299                             brick++;
23300                         }
23301                     }
23302
23303                     //when we take an old segment to make the new
23304                     //ephemeral segment. we can have a bunch of
23305                     //pinned plugs out of order going to the new ephemeral seg
23306                     //and then the next plugs go back to max_generation
23307                     if ((heap_segment_mem (ephemeral_heap_segment) <= arr) &&
23308                         (heap_segment_reserved (ephemeral_heap_segment) > arr))
23309                     {
23310
23311                         while ((low <= arr) && (high > arr))
23312                         {
23313                             gen_number--;
23314                             assert ((gen_number >= 1) || (demotion_low != MAX_PTR) ||
23315                                     settings.demotion || !settings.promotion);
23316                             dprintf (3, ("new free list generation %d", gen_number));
23317
23318                             gen = generation_of (gen_number);
23319                             if (gen_number >= 1)
23320                                 low = generation_allocation_start (generation_of (gen_number-1));
23321                             else
23322                                 low = high;
23323                         }
23324                     }
23325                     else
23326                     {
23327                         dprintf (3, ("new free list generation %d", max_generation));
23328                         gen_number = max_generation;
23329                         gen = generation_of (gen_number);
23330                     }
23331
23332                     dprintf(3,("threading it into generation %d", gen_number));
23333                     thread_gap (arr, len, gen);
23334                     add_gen_free (gen_number, len);
23335                 }
23336             }
23337         }
23338
23339 #ifdef _DEBUG
23340         for (int x = 0; x <= max_generation; x++)
23341         {
23342             assert (generation_allocation_start (generation_of (x)));
23343         }
23344 #endif //_DEBUG
23345
23346         if (!settings.demotion && settings.promotion)
23347         {
23348             //clear card for generation 1. generation 0 is empty
23349             clear_card_for_addresses (
23350                 generation_allocation_start (generation_of (1)),
23351                 generation_allocation_start (generation_of (0)));
23352         }
23353         if (settings.promotion && !settings.demotion)
23354         {
23355             uint8_t* start = generation_allocation_start (youngest_generation);
23356             MAYBE_UNUSED_VAR(start);
23357             assert (heap_segment_allocated (ephemeral_heap_segment) ==
23358                     (start + Align (size (start))));
23359         }
23360     }
23361     else
23362     {
23363         //force promotion for sweep
23364         settings.promotion = TRUE;
23365         settings.compaction = FALSE;
23366
23367         ScanContext sc;
23368         sc.thread_number = heap_number;
23369         sc.promotion = FALSE;
23370         sc.concurrent = FALSE;
23371
23372         dprintf (2, ("**** Doing Mark and Sweep GC****"));
23373
23374         if ((condemned_gen_number < max_generation))
23375         {
23376             generation_allocator (older_gen)->copy_from_alloc_list (r_free_list);
23377             generation_free_list_space (older_gen) = r_free_list_space;
23378             generation_free_obj_space (older_gen) = r_free_obj_space;
23379             generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated;
23380             generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated;
23381             generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated;
23382             generation_allocation_limit (older_gen) = r_allocation_limit;
23383             generation_allocation_pointer (older_gen) = r_allocation_pointer;
23384             generation_allocation_context_start_region (older_gen) = r_allocation_start_region;
23385             generation_allocation_segment (older_gen) = r_allocation_segment;
23386         }
23387
23388         if ((condemned_gen_number < max_generation))
23389         {
23390             // Fix the allocation area of the older generation
23391             fix_older_allocation_area (older_gen);
23392         }
23393
23394         GCToEEInterface::DiagWalkSurvivors(__this);
23395
23396         gen0_big_free_spaces = 0;
23397         make_free_lists (condemned_gen_number);
23398         recover_saved_pinned_info();
23399
23400 #ifdef FEATURE_PREMORTEM_FINALIZATION
23401         finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE);
23402 #endif // FEATURE_PREMORTEM_FINALIZATION
23403 // MTHTS: leave single thread for HT processing on plan_phase
23404 #ifdef MULTIPLE_HEAPS
23405         dprintf(3, ("Joining after end of sweep"));
23406         gc_t_join.join(this, gc_join_adjust_handle_age_sweep);
23407         if (gc_t_join.joined())
23408 #endif //MULTIPLE_HEAPS
23409         {
23410             GCScan::GcPromotionsGranted(condemned_gen_number,
23411                                             max_generation, &sc);
23412             if (condemned_gen_number >= (max_generation -1))
23413             {
23414 #ifdef MULTIPLE_HEAPS
23415                 for (int i = 0; i < n_heaps; i++)
23416                 {
23417                     g_heaps[i]->rearrange_heap_segments(FALSE);
23418                 }
23419 #else
23420                 rearrange_heap_segments(FALSE);
23421 #endif //MULTIPLE_HEAPS
23422             }
23423
23424 #ifdef MULTIPLE_HEAPS
23425             //join all threads to make sure they are synchronized
23426             dprintf(3, ("Restarting after Promotion granted"));
23427             gc_t_join.restart();
23428 #endif //MULTIPLE_HEAPS
23429         }
23430
23431 #ifdef _DEBUG
23432         for (int x = 0; x <= max_generation; x++)
23433         {
23434             assert (generation_allocation_start (generation_of (x)));
23435         }
23436 #endif //_DEBUG
23437
23438         //clear card for generation 1. generation 0 is empty
23439         clear_card_for_addresses (
23440             generation_allocation_start (generation_of (1)),
23441             generation_allocation_start (generation_of (0)));
23442         assert ((heap_segment_allocated (ephemeral_heap_segment) ==
23443                  (generation_allocation_start (youngest_generation) +
23444                   Align (min_obj_size))));
23445     }
23446
23447     //verify_partial();
23448 }
23449 #ifdef _PREFAST_
23450 #pragma warning(pop)
23451 #endif //_PREFAST_
23452
23453
23454 /*****************************
23455 Called after compact phase to fix all generation gaps
23456 ********************************/
23457 void gc_heap::fix_generation_bounds (int condemned_gen_number,
23458                                      generation* consing_gen)
23459 {
23460     UNREFERENCED_PARAMETER(consing_gen);
23461
23462     assert (generation_allocation_segment (consing_gen) ==
23463             ephemeral_heap_segment);
23464
23465     //assign the planned allocation start to the generation
23466     int gen_number = condemned_gen_number;
23467     int bottom_gen = 0;
23468
23469     while (gen_number >= bottom_gen)
23470     {
23471         generation*  gen = generation_of (gen_number);
23472         dprintf(3,("Fixing generation pointers for %Ix", gen_number));
23473         if ((gen_number < max_generation) && ephemeral_promotion)
23474         {
23475             make_unused_array (saved_ephemeral_plan_start[gen_number], 
23476                                saved_ephemeral_plan_start_size[gen_number]);
23477         }
23478         reset_allocation_pointers (gen, generation_plan_allocation_start (gen));
23479         make_unused_array (generation_allocation_start (gen), generation_plan_allocation_start_size (gen));
23480         dprintf(3,(" start %Ix", (size_t)generation_allocation_start (gen)));
23481         gen_number--;
23482     }
23483 #ifdef MULTIPLE_HEAPS
23484     if (ephemeral_promotion)
23485     {
23486         //we are creating a generation fault. set the cards.
23487         // and we are only doing this for multiple heaps because in the single heap scenario the 
23488         // new ephemeral generations will be empty and there'll be no need to set cards for the
23489         // old ephemeral generations that got promoted into max_generation.
23490         ptrdiff_t delta = 0;
23491 #ifdef SEG_MAPPING_TABLE
23492         heap_segment* old_ephemeral_seg = seg_mapping_table_segment_of (saved_ephemeral_plan_start[max_generation-1]);
23493 #else //SEG_MAPPING_TABLE
23494         heap_segment* old_ephemeral_seg = segment_of (saved_ephemeral_plan_start[max_generation-1], delta);
23495 #endif //SEG_MAPPING_TABLE
23496
23497         assert (in_range_for_segment (saved_ephemeral_plan_start[max_generation-1], old_ephemeral_seg));
23498         size_t end_card = card_of (align_on_card (heap_segment_plan_allocated (old_ephemeral_seg)));
23499         size_t card = card_of (saved_ephemeral_plan_start[max_generation-1]);
23500         while (card != end_card)
23501         {
23502             set_card (card);
23503             card++;
23504         }
23505     }
23506 #endif //MULTIPLE_HEAPS
23507     {
23508         alloc_allocated = heap_segment_plan_allocated(ephemeral_heap_segment);
23509         //reset the allocated size
23510         uint8_t* start = generation_allocation_start (youngest_generation);
23511         MAYBE_UNUSED_VAR(start);
23512         if (settings.promotion && !settings.demotion)
23513         {
23514             assert ((start + Align (size (start))) ==
23515                     heap_segment_plan_allocated(ephemeral_heap_segment));
23516         }
23517
23518         heap_segment_allocated(ephemeral_heap_segment)=
23519             heap_segment_plan_allocated(ephemeral_heap_segment);
23520     }
23521 }
23522
23523 uint8_t* gc_heap::generation_limit (int gen_number)
23524 {
23525     if (settings.promotion)
23526     {
23527         if (gen_number <= 1)
23528             return heap_segment_reserved (ephemeral_heap_segment);
23529         else
23530             return generation_allocation_start (generation_of ((gen_number - 2)));
23531     }
23532     else
23533     {
23534         if (gen_number <= 0)
23535             return heap_segment_reserved (ephemeral_heap_segment);
23536         else
23537             return generation_allocation_start (generation_of ((gen_number - 1)));
23538     }
23539 }
23540
23541 BOOL gc_heap::ensure_gap_allocation (int condemned_gen_number)
23542 {
23543     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
23544     size_t size = Align (min_obj_size)*(condemned_gen_number+1);
23545     assert ((start + size) <=
23546             heap_segment_reserved (ephemeral_heap_segment));
23547     if ((start + size) >
23548         heap_segment_committed (ephemeral_heap_segment))
23549     {
23550         if (!grow_heap_segment (ephemeral_heap_segment, start + size))
23551         {
23552             return FALSE;
23553         }
23554     }
23555     return TRUE;
23556 }
23557
23558 uint8_t* gc_heap::allocate_at_end (size_t size)
23559 {
23560     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
23561     size = Align (size);
23562     uint8_t* result = start;
23563     // only called to allocate a min obj so can't overflow here.
23564     assert ((start + size) <=
23565             heap_segment_reserved (ephemeral_heap_segment));
23566     //ensure_gap_allocation took care of it
23567     assert ((start + size) <=
23568             heap_segment_committed (ephemeral_heap_segment));
23569     heap_segment_allocated (ephemeral_heap_segment) += size;
23570     return result;
23571 }
23572
23573
23574 void gc_heap::make_free_lists (int condemned_gen_number)
23575 {
23576 #ifdef TIME_GC
23577     unsigned start;
23578     unsigned finish;
23579     start = GetCycleCount32();
23580 #endif //TIME_GC
23581
23582     //Promotion has to happen in sweep case.
23583     assert (settings.promotion);
23584
23585     generation* condemned_gen = generation_of (condemned_gen_number);
23586     uint8_t* start_address = generation_allocation_start (condemned_gen);
23587
23588     size_t  current_brick = brick_of (start_address);
23589     heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23590
23591     PREFIX_ASSUME(current_heap_segment != NULL);
23592
23593     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
23594     size_t  end_brick = brick_of (end_address-1);
23595     make_free_args args;
23596     args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number);
23597     args.current_gen_limit = (((condemned_gen_number == max_generation)) ?
23598                               MAX_PTR :
23599                               (generation_limit (args.free_list_gen_number)));
23600     args.free_list_gen = generation_of (args.free_list_gen_number);
23601     args.highest_plug = 0;
23602
23603     if ((start_address < end_address) ||
23604         (condemned_gen_number == max_generation))
23605     {
23606         while (1)
23607         {
23608             if ((current_brick > end_brick))
23609             {
23610                 if (args.current_gen_limit == MAX_PTR)
23611                 {
23612                     //We had an empty segment
23613                     //need to allocate the generation start
23614
23615                     generation* gen = generation_of (max_generation);
23616
23617                     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
23618
23619                     PREFIX_ASSUME(start_seg != NULL);
23620
23621                     uint8_t* gap = heap_segment_mem (start_seg);
23622
23623                     generation_allocation_start (gen) = gap;
23624                     heap_segment_allocated (start_seg) = gap + Align (min_obj_size);
23625                     make_unused_array (gap, Align (min_obj_size));
23626                     reset_allocation_pointers (gen, gap);
23627                     dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix",
23628                                  max_generation, (size_t)gap));
23629                     args.current_gen_limit = generation_limit (args.free_list_gen_number);
23630                 }
23631                 if (heap_segment_next_rw (current_heap_segment))
23632                 {
23633                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
23634                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
23635                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
23636
23637                     continue;
23638                 }
23639                 else
23640                 {
23641                     break;
23642                 }
23643             }
23644             {
23645                 int brick_entry =  brick_table [ current_brick ];
23646                 if ((brick_entry >= 0))
23647                 {
23648                     make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args);
23649                     dprintf(3,("Fixing brick entry %Ix to %Ix",
23650                                current_brick, (size_t)args.highest_plug));
23651                     set_brick (current_brick,
23652                                (args.highest_plug - brick_address (current_brick)));
23653                 }
23654                 else
23655                 {
23656                     if ((brick_entry > -32768))
23657                     {
23658
23659 #ifdef _DEBUG
23660                         ptrdiff_t offset = brick_of (args.highest_plug) - current_brick;
23661                         if ((brick_entry != -32767) && (! ((offset == brick_entry))))
23662                         {
23663                             assert ((brick_entry == -1));
23664                         }
23665 #endif //_DEBUG
23666                         //init to -1 for faster find_first_object
23667                         set_brick (current_brick, -1);
23668                     }
23669                 }
23670             }
23671             current_brick++;
23672         }
23673     }
23674     {
23675         int bottom_gen = 0;
23676         args.free_list_gen_number--;
23677         while (args.free_list_gen_number >= bottom_gen)
23678         {
23679             uint8_t*  gap = 0;
23680             generation* gen2 = generation_of (args.free_list_gen_number);
23681             gap = allocate_at_end (Align(min_obj_size));
23682             generation_allocation_start (gen2) = gap;
23683             reset_allocation_pointers (gen2, gap);
23684             dprintf(3,("Fixing generation start of %d to: %Ix",
23685                        args.free_list_gen_number, (size_t)gap));
23686             PREFIX_ASSUME(gap != NULL);
23687             make_unused_array (gap, Align (min_obj_size));
23688
23689             args.free_list_gen_number--;
23690         }
23691
23692         //reset the allocated size
23693         uint8_t* start2 = generation_allocation_start (youngest_generation);
23694         alloc_allocated = start2 + Align (size (start2));
23695     }
23696
23697 #ifdef TIME_GC
23698     finish = GetCycleCount32();
23699     sweep_time = finish - start;
23700 #endif //TIME_GC
23701 }
23702
23703 void gc_heap::make_free_list_in_brick (uint8_t* tree, make_free_args* args)
23704 {
23705     assert ((tree != NULL));
23706     {
23707         int  right_node = node_right_child (tree);
23708         int left_node = node_left_child (tree);
23709         args->highest_plug = 0;
23710         if (! (0 == tree))
23711         {
23712             if (! (0 == left_node))
23713             {
23714                 make_free_list_in_brick (tree + left_node, args);
23715
23716             }
23717             {
23718                 uint8_t*  plug = tree;
23719                 size_t  gap_size = node_gap_size (tree);
23720                 uint8_t*  gap = (plug - gap_size);
23721                 dprintf (3,("Making free list %Ix len %d in %d",
23722                 //dprintf (3,("F: %Ix len %Ix in %d",
23723                         (size_t)gap, gap_size, args->free_list_gen_number));
23724                 args->highest_plug = tree;
23725 #ifdef SHORT_PLUGS
23726                 if (is_plug_padded (plug))
23727                 {
23728                     dprintf (3, ("%Ix padded", plug));
23729                     clear_plug_padded (plug);
23730                 }
23731 #endif //SHORT_PLUGS
23732             gen_crossing:
23733                 {
23734                     if ((args->current_gen_limit == MAX_PTR) ||
23735                         ((plug >= args->current_gen_limit) &&
23736                          ephemeral_pointer_p (plug)))
23737                     {
23738                         dprintf(3,(" Crossing Generation boundary at %Ix",
23739                                (size_t)args->current_gen_limit));
23740                         if (!(args->current_gen_limit == MAX_PTR))
23741                         {
23742                             args->free_list_gen_number--;
23743                             args->free_list_gen = generation_of (args->free_list_gen_number);
23744                         }
23745                         dprintf(3,( " Fixing generation start of %d to: %Ix",
23746                                 args->free_list_gen_number, (size_t)gap));
23747
23748                         reset_allocation_pointers (args->free_list_gen, gap);
23749                         args->current_gen_limit = generation_limit (args->free_list_gen_number);
23750
23751                         if ((gap_size >= (2*Align (min_obj_size))))
23752                         {
23753                             dprintf(3,(" Splitting the gap in two %Id left",
23754                                    gap_size));
23755                             make_unused_array (gap, Align(min_obj_size));
23756                             gap_size = (gap_size - Align(min_obj_size));
23757                             gap = (gap + Align(min_obj_size));
23758                         }
23759                         else
23760                         {
23761                             make_unused_array (gap, gap_size);
23762                             gap_size = 0;
23763                         }
23764                         goto gen_crossing;
23765                     }
23766                 }
23767
23768                 thread_gap (gap, gap_size, args->free_list_gen);
23769                 add_gen_free (args->free_list_gen->gen_num, gap_size);
23770             }
23771             if (! (0 == right_node))
23772             {
23773                 make_free_list_in_brick (tree + right_node, args);
23774             }
23775         }
23776     }
23777 }
23778
23779 void gc_heap::thread_gap (uint8_t* gap_start, size_t size, generation*  gen)
23780 {
23781     assert (generation_allocation_start (gen));
23782     if ((size > 0))
23783     {
23784         if ((gen->gen_num == 0) && (size > CLR_SIZE))
23785         {
23786             gen0_big_free_spaces += size;
23787         }
23788
23789         assert ((heap_segment_rw (generation_start_segment (gen))!=
23790                  ephemeral_heap_segment) ||
23791                 (gap_start > generation_allocation_start (gen)));
23792         // The beginning of a segment gap is not aligned
23793         assert (size >= Align (min_obj_size));
23794         make_unused_array (gap_start, size, 
23795                           (!settings.concurrent && (gen != youngest_generation)),
23796                           (gen->gen_num == max_generation));
23797         dprintf (3, ("fr: [%Ix, %Ix[", (size_t)gap_start, (size_t)gap_start+size));
23798
23799         if ((size >= min_free_list))
23800         {
23801             generation_free_list_space (gen) += size;
23802             generation_allocator (gen)->thread_item (gap_start, size);
23803         }
23804         else
23805         {
23806             generation_free_obj_space (gen) += size;
23807         }
23808     }
23809 }
23810
23811 void gc_heap::loh_thread_gap_front (uint8_t* gap_start, size_t size, generation*  gen)
23812 {
23813     assert (generation_allocation_start (gen));
23814     if (size >= min_free_list)
23815     {
23816         generation_free_list_space (gen) += size;
23817         generation_allocator (gen)->thread_item_front (gap_start, size);
23818     }
23819 }
23820
23821 void gc_heap::make_unused_array (uint8_t* x, size_t size, BOOL clearp, BOOL resetp)
23822 {
23823     dprintf (3, ("Making unused array [%Ix, %Ix[",
23824         (size_t)x, (size_t)(x+size)));
23825     assert (size >= Align (min_obj_size));
23826
23827 //#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
23828 //    check_batch_mark_array_bits (x, x+size);
23829 //#endif //VERIFY_HEAP && BACKGROUND_GC
23830
23831     if (resetp)
23832         reset_memory (x, size);
23833
23834     ((CObjectHeader*)x)->SetFree(size);
23835
23836 #ifdef BIT64
23837
23838 #if BIGENDIAN
23839 #error "This won't work on big endian platforms"
23840 #endif
23841
23842     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23843
23844     if (size_as_object < size)
23845     {
23846         //
23847         // If the size is more than 4GB, we need to create multiple objects because of
23848         // the Array::m_NumComponents is uint32_t and the high 32 bits of unused array
23849         // size is ignored in regular object size computation.
23850         //
23851         uint8_t * tmp = x + size_as_object;
23852         size_t remaining_size = size - size_as_object;
23853
23854         while (remaining_size > UINT32_MAX)
23855         {
23856             // Make sure that there will be at least Align(min_obj_size) left
23857             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
23858                 - Align (min_obj_size, get_alignment_constant (FALSE));
23859
23860             ((CObjectHeader*)tmp)->SetFree(current_size);
23861
23862             remaining_size -= current_size;
23863             tmp += current_size;
23864         }
23865
23866         ((CObjectHeader*)tmp)->SetFree(remaining_size);
23867     }
23868 #endif
23869
23870     if (clearp)
23871         clear_card_for_addresses (x, x + Align(size));
23872 }
23873
23874 // Clear memory set by make_unused_array.
23875 void gc_heap::clear_unused_array (uint8_t* x, size_t size)
23876 {
23877     // Also clear the sync block
23878     *(((PTR_PTR)x)-1) = 0;
23879
23880     ((CObjectHeader*)x)->UnsetFree();
23881
23882 #ifdef BIT64
23883
23884 #if BIGENDIAN
23885 #error "This won't work on big endian platforms"
23886 #endif
23887
23888     // The memory could have been cleared in the meantime. We have to mirror the algorithm
23889     // from make_unused_array since we cannot depend on the object sizes in memory.
23890     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23891
23892     if (size_as_object < size)
23893     {
23894         uint8_t * tmp = x + size_as_object;
23895         size_t remaining_size = size - size_as_object;
23896
23897         while (remaining_size > UINT32_MAX)
23898         {
23899             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
23900                 - Align (min_obj_size, get_alignment_constant (FALSE));
23901
23902             ((CObjectHeader*)tmp)->UnsetFree();
23903
23904             remaining_size -= current_size;
23905             tmp += current_size;
23906         }
23907
23908         ((CObjectHeader*)tmp)->UnsetFree();
23909     }
23910 #else
23911     UNREFERENCED_PARAMETER(size);
23912 #endif
23913 }
23914
23915 inline
23916 uint8_t* tree_search (uint8_t* tree, uint8_t* old_address)
23917 {
23918     uint8_t* candidate = 0;
23919     int cn;
23920     while (1)
23921     {
23922         if (tree < old_address)
23923         {
23924             if ((cn = node_right_child (tree)) != 0)
23925             {
23926                 assert (candidate < tree);
23927                 candidate = tree;
23928                 tree = tree + cn;
23929                 Prefetch (tree - 8);
23930                 continue;
23931             }
23932             else
23933                 break;
23934         }
23935         else if (tree > old_address)
23936         {
23937             if ((cn = node_left_child (tree)) != 0)
23938             {
23939                 tree = tree + cn;
23940                 Prefetch (tree - 8);
23941                 continue;
23942             }
23943             else
23944                 break;
23945         } else
23946             break;
23947     }
23948     if (tree <= old_address)
23949         return tree;
23950     else if (candidate)
23951         return candidate;
23952     else
23953         return tree;
23954 }
23955
23956 #ifdef FEATURE_BASICFREEZE
23957 bool gc_heap::frozen_object_p (Object* obj)
23958 {
23959 #ifdef MULTIPLE_HEAPS
23960 #ifdef SEG_MAPPING_TABLE
23961     heap_segment* pSegment = seg_mapping_table_segment_of((uint8_t*)obj);
23962 #else
23963     ptrdiff_t delta = 0;
23964     heap_segment* pSegment = segment_of ((uint8_t*)obj, delta);
23965 #endif
23966 #else //MULTIPLE_HEAPS
23967     heap_segment* pSegment = gc_heap::find_segment ((uint8_t*)obj, FALSE);
23968     _ASSERTE(pSegment);
23969 #endif //MULTIPLE_HEAPS
23970
23971     return heap_segment_read_only_p(pSegment);
23972 }
23973 #endif // FEATURE_BASICFREEZE
23974
23975 #ifdef FEATURE_REDHAWK
23976 // TODO: this was added on RH, we have not done perf runs to see if this is the right
23977 // thing to do for other versions of the CLR.
23978 inline
23979 #endif // FEATURE_REDHAWK
23980 void gc_heap::relocate_address (uint8_t** pold_address THREAD_NUMBER_DCL)
23981 {
23982     uint8_t* old_address = *pold_address;
23983     if (!((old_address >= gc_low) && (old_address < gc_high)))
23984 #ifdef MULTIPLE_HEAPS
23985     {
23986         UNREFERENCED_PARAMETER(thread);
23987         if (old_address == 0)
23988             return;
23989         gc_heap* hp = heap_of (old_address);
23990         if ((hp == this) ||
23991             !((old_address >= hp->gc_low) && (old_address < hp->gc_high)))
23992             return;
23993     }
23994 #else //MULTIPLE_HEAPS
23995         return ;
23996 #endif //MULTIPLE_HEAPS
23997     // delta translates old_address into address_gc (old_address);
23998     size_t  brick = brick_of (old_address);
23999     int    brick_entry =  brick_table [ brick ];
24000     uint8_t*  new_address = old_address;
24001     if (! ((brick_entry == 0)))
24002     {
24003     retry:
24004         {
24005             while (brick_entry < 0)
24006             {
24007                 brick = (brick + brick_entry);
24008                 brick_entry =  brick_table [ brick ];
24009             }
24010             uint8_t* old_loc = old_address;
24011
24012             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
24013                                       old_loc);
24014             if ((node <= old_loc))
24015                 new_address = (old_address + node_relocation_distance (node));
24016             else
24017             {
24018                 if (node_left_p (node))
24019                 {
24020                     dprintf(3,(" L: %Ix", (size_t)node));
24021                     new_address = (old_address +
24022                                    (node_relocation_distance (node) +
24023                                     node_gap_size (node)));
24024                 }
24025                 else
24026                 {
24027                     brick = brick - 1;
24028                     brick_entry =  brick_table [ brick ];
24029                     goto retry;
24030                 }
24031             }
24032         }
24033
24034         *pold_address = new_address;
24035         return;
24036     }
24037
24038 #ifdef FEATURE_LOH_COMPACTION
24039     if (loh_compacted_p
24040 #ifdef FEATURE_BASICFREEZE
24041         && !frozen_object_p((Object*)old_address)
24042 #endif // FEATURE_BASICFREEZE
24043         )
24044     {
24045         *pold_address = old_address + loh_node_relocation_distance (old_address);
24046     }
24047     else
24048 #endif //FEATURE_LOH_COMPACTION
24049     {
24050         *pold_address = new_address;
24051     }
24052 }
24053
24054 inline void 
24055 gc_heap::check_class_object_demotion (uint8_t* obj)
24056 {
24057 #ifdef COLLECTIBLE_CLASS
24058     if (is_collectible(obj))
24059     {
24060         check_class_object_demotion_internal (obj);
24061     }
24062 #else
24063     UNREFERENCED_PARAMETER(obj);
24064 #endif //COLLECTIBLE_CLASS
24065 }
24066
24067 #ifdef COLLECTIBLE_CLASS
24068 NOINLINE void 
24069 gc_heap::check_class_object_demotion_internal (uint8_t* obj)
24070 {
24071     if (settings.demotion)
24072     {
24073 #ifdef MULTIPLE_HEAPS
24074         // We set the card without checking the demotion range 'cause at this point
24075         // the handle that points to the loader allocator object may or may not have
24076         // been relocated by other GC threads. 
24077         set_card (card_of (obj));
24078 #else
24079         THREAD_FROM_HEAP;
24080         uint8_t* class_obj = get_class_object (obj);
24081         dprintf (3, ("%Ix: got classobj %Ix", obj, class_obj));
24082         uint8_t* temp_class_obj = class_obj;
24083         uint8_t** temp = &temp_class_obj;
24084         relocate_address (temp THREAD_NUMBER_ARG);
24085
24086         check_demotion_helper (temp, obj);
24087 #endif //MULTIPLE_HEAPS
24088     }
24089 }
24090
24091 #endif //COLLECTIBLE_CLASS
24092
24093 inline void
24094 gc_heap::check_demotion_helper (uint8_t** pval, uint8_t* parent_obj)
24095 {
24096     // detect if we are demoting an object
24097     if ((*pval < demotion_high) &&
24098         (*pval >= demotion_low))
24099     {
24100         dprintf(3, ("setting card %Ix:%Ix",
24101                     card_of((uint8_t*)pval),
24102                     (size_t)pval));
24103
24104         set_card (card_of (parent_obj));
24105     }
24106 #ifdef MULTIPLE_HEAPS
24107     else if (settings.demotion)
24108     {
24109         dprintf (4, ("Demotion active, computing heap_of object"));
24110         gc_heap* hp = heap_of (*pval);
24111         if ((*pval < hp->demotion_high) &&
24112             (*pval >= hp->demotion_low))
24113         {
24114             dprintf(3, ("setting card %Ix:%Ix",
24115                         card_of((uint8_t*)pval),
24116                         (size_t)pval));
24117
24118             set_card (card_of (parent_obj));
24119         }
24120     }
24121 #endif //MULTIPLE_HEAPS
24122 }
24123
24124 inline void
24125 gc_heap::reloc_survivor_helper (uint8_t** pval)
24126 {
24127     THREAD_FROM_HEAP;
24128     relocate_address (pval THREAD_NUMBER_ARG);
24129
24130     check_demotion_helper (pval, (uint8_t*)pval);
24131 }
24132
24133 inline void
24134 gc_heap::relocate_obj_helper (uint8_t* x, size_t s)
24135 {
24136     THREAD_FROM_HEAP;
24137     if (contain_pointers (x))
24138     {
24139         dprintf (3, ("$%Ix$", (size_t)x));
24140
24141         go_through_object_nostart (method_table(x), x, s, pval,
24142                             {
24143                                 uint8_t* child = *pval;
24144                                 reloc_survivor_helper (pval);
24145                                 if (child)
24146                                 {
24147                                     dprintf (3, ("%Ix->%Ix->%Ix", (uint8_t*)pval, child, *pval));
24148                                 }
24149                             });
24150
24151     }
24152     check_class_object_demotion (x);
24153 }
24154
24155 inline 
24156 void gc_heap::reloc_ref_in_shortened_obj (uint8_t** address_to_set_card, uint8_t** address_to_reloc)
24157 {
24158     THREAD_FROM_HEAP;
24159
24160     uint8_t* old_val = (address_to_reloc ? *address_to_reloc : 0);
24161     relocate_address (address_to_reloc THREAD_NUMBER_ARG);
24162     if (address_to_reloc)
24163     {
24164         dprintf (3, ("SR %Ix: %Ix->%Ix", (uint8_t*)address_to_reloc, old_val, *address_to_reloc));
24165     }
24166
24167     //check_demotion_helper (current_saved_info_to_relocate, (uint8_t*)pval);
24168     uint8_t* relocated_addr = *address_to_reloc;
24169     if ((relocated_addr < demotion_high) &&
24170         (relocated_addr >= demotion_low))
24171     {
24172         dprintf (3, ("set card for location %Ix(%Ix)",
24173                     (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
24174
24175         set_card (card_of ((uint8_t*)address_to_set_card));
24176     }
24177 #ifdef MULTIPLE_HEAPS
24178     else if (settings.demotion)
24179     {
24180         gc_heap* hp = heap_of (relocated_addr);
24181         if ((relocated_addr < hp->demotion_high) &&
24182             (relocated_addr >= hp->demotion_low))
24183         {
24184             dprintf (3, ("%Ix on h%d, set card for location %Ix(%Ix)",
24185                         relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
24186
24187             set_card (card_of ((uint8_t*)address_to_set_card));
24188         }
24189     }
24190 #endif //MULTIPLE_HEAPS
24191 }
24192
24193 void gc_heap::relocate_pre_plug_info (mark* pinned_plug_entry)
24194 {
24195     THREAD_FROM_HEAP;
24196     uint8_t* plug = pinned_plug (pinned_plug_entry);
24197     uint8_t* pre_plug_start = plug - sizeof (plug_and_gap);
24198     // Note that we need to add one ptr size here otherwise we may not be able to find the relocated
24199     // address. Consider this scenario: 
24200     // gen1 start | 3-ptr sized NP | PP
24201     // 0          | 0x18           | 0x30
24202     // If we are asking for the reloc address of 0x10 we will AV in relocate_address because
24203     // the first plug we saw in the brick is 0x18 which means 0x10 will cause us to go back a brick
24204     // which is 0, and then we'll AV in tree_search when we try to do node_right_child (tree). 
24205     pre_plug_start += sizeof (uint8_t*);
24206     uint8_t** old_address = &pre_plug_start;
24207
24208     uint8_t* old_val = (old_address ? *old_address : 0);
24209     relocate_address (old_address THREAD_NUMBER_ARG);
24210     if (old_address)
24211     {
24212         dprintf (3, ("PreR %Ix: %Ix->%Ix, set reloc: %Ix", 
24213             (uint8_t*)old_address, old_val, *old_address, (pre_plug_start - sizeof (uint8_t*))));
24214     }
24215
24216     pinned_plug_entry->set_pre_plug_info_reloc_start (pre_plug_start - sizeof (uint8_t*));
24217 }
24218
24219 inline
24220 void gc_heap::relocate_shortened_obj_helper (uint8_t* x, size_t s, uint8_t* end, mark* pinned_plug_entry, BOOL is_pinned)
24221 {
24222     THREAD_FROM_HEAP;
24223     uint8_t* plug = pinned_plug (pinned_plug_entry);
24224
24225     if (!is_pinned)
24226     {
24227         //// Temporary - we just wanna make sure we are doing things right when padding is needed.
24228         //if ((x + s) < plug)
24229         //{
24230         //    dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix", 
24231         //        x, (x + s), (plug- (x + s)), plug));
24232         //    GCToOSInterface::DebugBreak();
24233         //}
24234
24235         relocate_pre_plug_info (pinned_plug_entry);
24236     }
24237
24238     verify_pins_with_post_plug_info("after relocate_pre_plug_info");
24239
24240     uint8_t* saved_plug_info_start = 0;
24241     uint8_t** saved_info_to_relocate = 0;
24242
24243     if (is_pinned)
24244     {
24245         saved_plug_info_start = (uint8_t*)(pinned_plug_entry->get_post_plug_info_start());
24246         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
24247     }
24248     else
24249     {
24250         saved_plug_info_start = (plug - sizeof (plug_and_gap));
24251         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
24252     }
24253     
24254     uint8_t** current_saved_info_to_relocate = 0;
24255     uint8_t* child = 0;
24256
24257     dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix", x, plug, end));
24258
24259     if (contain_pointers (x))
24260     {
24261         dprintf (3,("$%Ix$", (size_t)x));
24262
24263         go_through_object_nostart (method_table(x), x, s, pval,
24264         {
24265             dprintf (3, ("obj %Ix, member: %Ix->%Ix", x, (uint8_t*)pval, *pval));
24266
24267             if ((uint8_t*)pval >= end)
24268             {
24269                 current_saved_info_to_relocate = saved_info_to_relocate + ((uint8_t*)pval - saved_plug_info_start) / sizeof (uint8_t**);
24270                 child = *current_saved_info_to_relocate;
24271                 reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate);
24272                 dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix",
24273                     (uint8_t*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate));
24274             }
24275             else
24276             {
24277                 reloc_survivor_helper (pval);
24278             }
24279         });
24280     }
24281
24282     check_class_object_demotion (x);
24283 }
24284
24285 void gc_heap::relocate_survivor_helper (uint8_t* plug, uint8_t* plug_end)
24286 {
24287     uint8_t*  x = plug;
24288     while (x < plug_end)
24289     {
24290         size_t s = size (x);
24291         uint8_t* next_obj = x + Align (s);
24292         Prefetch (next_obj);
24293         relocate_obj_helper (x, s);
24294         assert (s > 0);
24295         x = next_obj;
24296     }
24297 }
24298
24299 // if we expanded, right now we are not handling it as We are not saving the new reloc info.
24300 void gc_heap::verify_pins_with_post_plug_info (const char* msg)
24301 {
24302 #if defined  (_DEBUG) && defined (VERIFY_HEAP)
24303     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
24304     {
24305         if (!verify_pinned_queue_p)
24306             return;
24307
24308         if (settings.heap_expansion)
24309             return;
24310
24311         for (size_t i = 0; i < mark_stack_tos; i++)
24312         {
24313             mark& m = mark_stack_array[i];
24314
24315             mark* pinned_plug_entry = pinned_plug_of(i);
24316
24317             if (pinned_plug_entry->has_post_plug_info() && 
24318                 pinned_plug_entry->post_short_p() && 
24319                 (pinned_plug_entry->saved_post_plug_debug.gap != 1))
24320             {
24321                 uint8_t* next_obj = pinned_plug_entry->get_post_plug_info_start() + sizeof (plug_and_gap);
24322                 // object after pin
24323                 dprintf (3, ("OFP: %Ix, G: %Ix, R: %Ix, LC: %d, RC: %d", 
24324                     next_obj, node_gap_size (next_obj), node_relocation_distance (next_obj),
24325                     (int)node_left_child (next_obj), (int)node_right_child (next_obj)));
24326
24327                 size_t* post_plug_debug = (size_t*)(&m.saved_post_plug_debug);
24328
24329                 if (node_gap_size (next_obj) != *post_plug_debug)
24330                 {
24331                     dprintf (3, ("obj: %Ix gap should be %Ix but it is %Ix", 
24332                         next_obj, *post_plug_debug, (size_t)(node_gap_size (next_obj))));
24333                     FATAL_GC_ERROR();
24334                 }
24335                 post_plug_debug++;
24336                 // can't do node_relocation_distance here as it clears the left bit.
24337                 //if (node_relocation_distance (next_obj) != *post_plug_debug)
24338                 if (*((size_t*)(next_obj - 3 * sizeof (size_t))) != *post_plug_debug)
24339                 {
24340                     dprintf (3, ("obj: %Ix reloc should be %Ix but it is %Ix", 
24341                         next_obj, *post_plug_debug, (size_t)(node_relocation_distance (next_obj))));
24342                     FATAL_GC_ERROR();
24343                 }
24344                 if (node_left_child (next_obj) > 0)
24345                 {
24346                     dprintf (3, ("obj: %Ix, vLC: %d\n", next_obj, (int)(node_left_child (next_obj))));
24347                     FATAL_GC_ERROR();
24348                 }
24349             }
24350         }
24351
24352         dprintf (3, ("%s verified", msg));
24353     }
24354 #else // _DEBUG && VERIFY_HEAP
24355     UNREFERENCED_PARAMETER(msg);
24356 #endif // _DEBUG && VERIFY_HEAP
24357 }
24358
24359 #ifdef COLLECTIBLE_CLASS
24360 // We don't want to burn another ptr size space for pinned plugs to record this so just 
24361 // set the card unconditionally for collectible objects if we are demoting.
24362 inline void
24363 gc_heap::unconditional_set_card_collectible (uint8_t* obj)
24364 {
24365     if (settings.demotion)
24366     {
24367         set_card (card_of (obj));
24368     }
24369 }
24370 #endif //COLLECTIBLE_CLASS
24371
24372 void gc_heap::relocate_shortened_survivor_helper (uint8_t* plug, uint8_t* plug_end, mark* pinned_plug_entry)
24373 {
24374     uint8_t*  x = plug;
24375     uint8_t* p_plug = pinned_plug (pinned_plug_entry);
24376     BOOL is_pinned = (plug == p_plug);
24377     BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p());
24378
24379     plug_end += sizeof (gap_reloc_pair);
24380
24381     //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not")));
24382     dprintf (3, ("%s %Ix-%Ix short, LO: %s OW", (is_pinned ? "PP" : "NP"), plug, plug_end, (check_short_obj_p ? "is" : "is not")));
24383
24384     verify_pins_with_post_plug_info("begin reloc short surv");
24385
24386     while (x < plug_end)
24387     {
24388         if (check_short_obj_p && ((plug_end - x) < min_pre_pin_obj_size))
24389         {
24390             dprintf (3, ("last obj %Ix is short", x));
24391
24392             if (is_pinned)
24393             {
24394 #ifdef COLLECTIBLE_CLASS
24395                 if (pinned_plug_entry->post_short_collectible_p())
24396                     unconditional_set_card_collectible (x);
24397 #endif //COLLECTIBLE_CLASS
24398
24399                 // Relocate the saved references based on bits set.
24400                 uint8_t** saved_plug_info_start = (uint8_t**)(pinned_plug_entry->get_post_plug_info_start());
24401                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
24402                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
24403                 {
24404                     if (pinned_plug_entry->post_short_bit_p (i))
24405                     {
24406                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
24407                     }
24408                 }
24409             }
24410             else
24411             {
24412 #ifdef COLLECTIBLE_CLASS
24413                 if (pinned_plug_entry->pre_short_collectible_p())
24414                     unconditional_set_card_collectible (x);
24415 #endif //COLLECTIBLE_CLASS
24416
24417                 relocate_pre_plug_info (pinned_plug_entry);
24418
24419                 // Relocate the saved references based on bits set.
24420                 uint8_t** saved_plug_info_start = (uint8_t**)(p_plug - sizeof (plug_and_gap));
24421                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
24422                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
24423                 {
24424                     if (pinned_plug_entry->pre_short_bit_p (i))
24425                     {
24426                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
24427                     }
24428                 }
24429             }
24430
24431             break;
24432         }
24433
24434         size_t s = size (x);
24435         uint8_t* next_obj = x + Align (s);
24436         Prefetch (next_obj);
24437
24438         if (next_obj >= plug_end) 
24439         {
24440             dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix", 
24441                 next_obj, plug, plug_end));
24442
24443             verify_pins_with_post_plug_info("before reloc short obj");
24444
24445             relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned);
24446         }
24447         else
24448         {
24449             relocate_obj_helper (x, s);
24450         }
24451
24452         assert (s > 0);
24453         x = next_obj;
24454     }
24455
24456     verify_pins_with_post_plug_info("end reloc short surv");
24457 }
24458
24459 void gc_heap::relocate_survivors_in_plug (uint8_t* plug, uint8_t* plug_end,
24460                                           BOOL check_last_object_p, 
24461                                           mark* pinned_plug_entry)
24462 {
24463     //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
24464     dprintf (3,("RP: [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
24465
24466     if (check_last_object_p)
24467     {
24468         relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry);
24469     }
24470     else
24471     {
24472         relocate_survivor_helper (plug, plug_end);
24473     }
24474 }
24475
24476 void gc_heap::relocate_survivors_in_brick (uint8_t* tree, relocate_args* args)
24477 {
24478     assert ((tree != NULL));
24479
24480     dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix",
24481         tree, args->last_plug, 
24482         (tree + node_left_child (tree)),
24483         (tree + node_right_child (tree)),
24484         node_gap_size (tree)));
24485
24486     if (node_left_child (tree))
24487     {
24488         relocate_survivors_in_brick (tree + node_left_child (tree), args);
24489     }
24490     {
24491         uint8_t*  plug = tree;
24492         BOOL   has_post_plug_info_p = FALSE;
24493         BOOL   has_pre_plug_info_p = FALSE;
24494
24495         if (tree == oldest_pinned_plug)
24496         {
24497             args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24498                                                                &has_post_plug_info_p);
24499             assert (tree == pinned_plug (args->pinned_plug_entry));
24500
24501             dprintf (3, ("tree is the oldest pin: %Ix", tree));
24502         }
24503         if (args->last_plug)
24504         {
24505             size_t  gap_size = node_gap_size (tree);
24506             uint8_t*  gap = (plug - gap_size);
24507             dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)", tree, gap, gap_size));
24508             assert (gap_size >= Align (min_obj_size));
24509             uint8_t*  last_plug_end = gap;
24510
24511             BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24512
24513             {
24514                 relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry);
24515             }
24516         }
24517         else
24518         {
24519             assert (!has_pre_plug_info_p);
24520         }
24521
24522         args->last_plug = plug;
24523         args->is_shortened = has_post_plug_info_p;
24524         if (has_post_plug_info_p)
24525         {
24526             dprintf (3, ("setting %Ix as shortened", plug));
24527         }
24528         dprintf (3, ("last_plug: %Ix(shortened: %d)", plug, (args->is_shortened ? 1 : 0)));
24529     }
24530     if (node_right_child (tree))
24531     {
24532         relocate_survivors_in_brick (tree + node_right_child (tree), args);
24533     }
24534 }
24535
24536 inline
24537 void gc_heap::update_oldest_pinned_plug()
24538 {
24539     oldest_pinned_plug = (pinned_plug_que_empty_p() ? 0 : pinned_plug (oldest_pin()));
24540 }
24541
24542 void gc_heap::relocate_survivors (int condemned_gen_number,
24543                                   uint8_t* first_condemned_address)
24544 {
24545     generation* condemned_gen = generation_of (condemned_gen_number);
24546     uint8_t*  start_address = first_condemned_address;
24547     size_t  current_brick = brick_of (start_address);
24548     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24549
24550     PREFIX_ASSUME(current_heap_segment != NULL);
24551
24552     uint8_t*  end_address = 0;
24553
24554     reset_pinned_queue_bos();
24555     update_oldest_pinned_plug();
24556     
24557     end_address = heap_segment_allocated (current_heap_segment);
24558
24559     size_t  end_brick = brick_of (end_address - 1);
24560     relocate_args args;
24561     args.low = gc_low;
24562     args.high = gc_high;
24563     args.is_shortened = FALSE;
24564     args.pinned_plug_entry = 0;
24565     args.last_plug = 0;
24566     while (1)
24567     {
24568         if (current_brick > end_brick)
24569         {
24570             if (args.last_plug)
24571             {
24572                 {
24573                     assert (!(args.is_shortened));
24574                     relocate_survivors_in_plug (args.last_plug,
24575                                                 heap_segment_allocated (current_heap_segment),
24576                                                 args.is_shortened, 
24577                                                 args.pinned_plug_entry);
24578                 }
24579
24580                 args.last_plug = 0;
24581             }
24582
24583             if (heap_segment_next_rw (current_heap_segment))
24584             {
24585                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24586                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24587                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24588                 continue;
24589             }
24590             else
24591             {
24592                 break;
24593             }
24594         }
24595         {
24596             int brick_entry =  brick_table [ current_brick ];
24597
24598             if (brick_entry >= 0)
24599             {
24600                 relocate_survivors_in_brick (brick_address (current_brick) +
24601                                              brick_entry -1,
24602                                              &args);
24603             }
24604         }
24605         current_brick++;
24606     }
24607 }
24608
24609 void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args)
24610 {
24611     if (check_last_object_p)
24612     {
24613         size += sizeof (gap_reloc_pair);
24614         mark* entry = args->pinned_plug_entry;
24615
24616         if (args->is_shortened)
24617         {
24618             assert (entry->has_post_plug_info());
24619             entry->swap_post_plug_and_saved_for_profiler();
24620         }
24621         else
24622         {
24623             assert (entry->has_pre_plug_info());
24624             entry->swap_pre_plug_and_saved_for_profiler();
24625         }
24626     }
24627
24628     ptrdiff_t last_plug_relocation = node_relocation_distance (plug);
24629     STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation);
24630     ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0;
24631
24632     (args->fn) (plug, (plug + size), reloc, args->profiling_context, !!settings.compaction, false);
24633
24634     if (check_last_object_p)
24635     {
24636         mark* entry = args->pinned_plug_entry;
24637
24638         if (args->is_shortened)
24639         {
24640             entry->swap_post_plug_and_saved_for_profiler();
24641         }
24642         else
24643         {
24644             entry->swap_pre_plug_and_saved_for_profiler();
24645         }
24646     }
24647 }
24648
24649 void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args)
24650 {
24651     assert ((tree != NULL));
24652     if (node_left_child (tree))
24653     {
24654         walk_relocation_in_brick (tree + node_left_child (tree), args);
24655     }
24656
24657     uint8_t*  plug = tree;
24658     BOOL   has_pre_plug_info_p = FALSE;
24659     BOOL   has_post_plug_info_p = FALSE;
24660
24661     if (tree == oldest_pinned_plug)
24662     {
24663         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24664                                                            &has_post_plug_info_p);
24665         assert (tree == pinned_plug (args->pinned_plug_entry));
24666     }
24667
24668     if (args->last_plug != 0)
24669     {
24670         size_t gap_size = node_gap_size (tree);
24671         uint8_t*  gap = (plug - gap_size);
24672         uint8_t*  last_plug_end = gap;
24673         size_t last_plug_size = (last_plug_end - args->last_plug);
24674         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
24675             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24676         
24677         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24678         if (!check_last_object_p)
24679         {
24680             assert (last_plug_size >= Align (min_obj_size));
24681         }
24682
24683         walk_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24684     }
24685     else
24686     {
24687         assert (!has_pre_plug_info_p);
24688     }
24689
24690     dprintf (3, ("set args last plug to plug: %Ix", plug));
24691     args->last_plug = plug;
24692     args->is_shortened = has_post_plug_info_p;
24693
24694     if (node_right_child (tree))
24695     {
24696         walk_relocation_in_brick (tree + node_right_child (tree), args);
24697     }
24698 }
24699
24700 void gc_heap::walk_relocation (void* profiling_context, record_surv_fn fn)
24701 {
24702     generation* condemned_gen = generation_of (settings.condemned_generation);
24703     uint8_t*  start_address = generation_allocation_start (condemned_gen);
24704     size_t  current_brick = brick_of (start_address);
24705     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24706
24707     PREFIX_ASSUME(current_heap_segment != NULL);
24708
24709     reset_pinned_queue_bos();
24710     update_oldest_pinned_plug();
24711     size_t end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24712     walk_relocate_args args;
24713     args.is_shortened = FALSE;
24714     args.pinned_plug_entry = 0;
24715     args.last_plug = 0;
24716     args.profiling_context = profiling_context;
24717     args.fn = fn;
24718
24719     while (1)
24720     {
24721         if (current_brick > end_brick)
24722         {
24723             if (args.last_plug)
24724             {
24725                 walk_plug (args.last_plug, 
24726                            (heap_segment_allocated (current_heap_segment) - args.last_plug), 
24727                            args.is_shortened,
24728                            &args);
24729                 args.last_plug = 0;
24730             }
24731             if (heap_segment_next_rw (current_heap_segment))
24732             {
24733                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24734                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24735                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24736                 continue;
24737             }
24738             else
24739             {
24740                 break;
24741             }
24742         }
24743         {
24744             int brick_entry =  brick_table [ current_brick ];
24745             if (brick_entry >= 0)
24746             {
24747                 walk_relocation_in_brick (brick_address (current_brick) +
24748                                           brick_entry - 1,
24749                                           &args);
24750             }
24751         }
24752         current_brick++;
24753     }
24754 }
24755
24756 void gc_heap::walk_survivors (record_surv_fn fn, void* context, walk_surv_type type)
24757 {
24758     if (type == walk_for_gc)
24759         walk_survivors_relocation (context, fn);
24760 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24761     else if (type == walk_for_bgc)
24762         walk_survivors_for_bgc (context, fn);
24763 #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
24764     else if (type == walk_for_loh)
24765         walk_survivors_for_loh (context, fn);
24766     else
24767         assert (!"unknown type!");
24768 }
24769
24770 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24771 void gc_heap::walk_survivors_for_bgc (void* profiling_context, record_surv_fn fn)
24772 {
24773     // This should only be called for BGCs
24774     assert(settings.concurrent);
24775
24776     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24777
24778     BOOL small_object_segments = TRUE;
24779     int align_const = get_alignment_constant (small_object_segments);
24780
24781     while (1)
24782     {
24783         if (seg == 0)
24784         {
24785             if (small_object_segments)
24786             {
24787                 //switch to large segment
24788                 small_object_segments = FALSE;
24789
24790                 align_const = get_alignment_constant (small_object_segments);
24791                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
24792
24793                 PREFIX_ASSUME(seg != NULL);
24794
24795                 continue;
24796             }
24797             else 
24798                 break;
24799         }
24800
24801         uint8_t* o = heap_segment_mem (seg);
24802         uint8_t* end = heap_segment_allocated (seg);
24803
24804         while (o < end)
24805         {
24806             if (method_table(o) == g_gc_pFreeObjectMethodTable)
24807             {
24808                 o += Align (size (o), align_const);
24809                 continue;
24810             }
24811
24812             // It's survived. Make a fake plug, starting at o,
24813             // and send the event
24814
24815             uint8_t* plug_start = o;
24816
24817             while (method_table(o) != g_gc_pFreeObjectMethodTable)
24818             {
24819                 o += Align (size (o), align_const);
24820                 if (o >= end)
24821                 {
24822                     break;
24823                 }
24824             }
24825                 
24826             uint8_t* plug_end = o;
24827
24828             fn (plug_start, 
24829                 plug_end,
24830                 0,              // Reloc distance == 0 as this is non-compacting
24831                 profiling_context,
24832                 false,          // Non-compacting
24833                 true);          // BGC
24834         }
24835
24836         seg = heap_segment_next (seg);
24837     }
24838 }
24839 #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24840
24841 void gc_heap::relocate_phase (int condemned_gen_number,
24842                               uint8_t* first_condemned_address)
24843 {
24844     ScanContext sc;
24845     sc.thread_number = heap_number;
24846     sc.promotion = FALSE;
24847     sc.concurrent = FALSE;
24848
24849
24850 #ifdef TIME_GC
24851         unsigned start;
24852         unsigned finish;
24853         start = GetCycleCount32();
24854 #endif //TIME_GC
24855
24856 //  %type%  category = quote (relocate);
24857     dprintf (2,("---- Relocate phase -----"));
24858
24859 #ifdef MULTIPLE_HEAPS
24860     //join all threads to make sure they are synchronized
24861     dprintf(3, ("Joining after end of plan"));
24862     gc_t_join.join(this, gc_join_begin_relocate_phase);
24863     if (gc_t_join.joined())
24864 #endif //MULTIPLE_HEAPS
24865
24866     {
24867 #ifdef MULTIPLE_HEAPS
24868
24869         //join all threads to make sure they are synchronized
24870         dprintf(3, ("Restarting for relocation"));
24871         gc_t_join.restart();
24872 #endif //MULTIPLE_HEAPS
24873     }
24874
24875     dprintf(3,("Relocating roots"));
24876     GCScan::GcScanRoots(GCHeap::Relocate,
24877                             condemned_gen_number, max_generation, &sc);
24878
24879     verify_pins_with_post_plug_info("after reloc stack");
24880
24881 #ifdef BACKGROUND_GC
24882     if (recursive_gc_sync::background_running_p())
24883     {
24884         scan_background_roots (GCHeap::Relocate, heap_number, &sc);
24885     }
24886 #endif //BACKGROUND_GC
24887
24888     if (condemned_gen_number != max_generation)
24889     {
24890         dprintf(3,("Relocating cross generation pointers"));
24891         mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE);
24892         verify_pins_with_post_plug_info("after reloc cards");
24893     }
24894     if (condemned_gen_number != max_generation)
24895     {
24896         dprintf(3,("Relocating cross generation pointers for large objects"));
24897         mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE);
24898     }
24899     else
24900     {
24901 #ifdef FEATURE_LOH_COMPACTION
24902         if (loh_compacted_p)
24903         {
24904             assert (settings.condemned_generation == max_generation);
24905             relocate_in_loh_compact();
24906         }
24907         else
24908 #endif //FEATURE_LOH_COMPACTION
24909         {
24910             relocate_in_large_objects ();
24911         }
24912     }
24913     {
24914         dprintf(3,("Relocating survivors"));
24915         relocate_survivors (condemned_gen_number,
24916                             first_condemned_address);
24917     }
24918
24919 #ifdef FEATURE_PREMORTEM_FINALIZATION
24920         dprintf(3,("Relocating finalization data"));
24921         finalize_queue->RelocateFinalizationData (condemned_gen_number,
24922                                                        __this);
24923 #endif // FEATURE_PREMORTEM_FINALIZATION
24924
24925
24926 // MTHTS
24927     {
24928         dprintf(3,("Relocating handle table"));
24929         GCScan::GcScanHandles(GCHeap::Relocate,
24930                                   condemned_gen_number, max_generation, &sc);
24931     }
24932
24933 #ifdef MULTIPLE_HEAPS
24934     //join all threads to make sure they are synchronized
24935     dprintf(3, ("Joining after end of relocation"));
24936     gc_t_join.join(this, gc_join_relocate_phase_done);
24937
24938 #endif //MULTIPLE_HEAPS
24939
24940 #ifdef TIME_GC
24941         finish = GetCycleCount32();
24942         reloc_time = finish - start;
24943 #endif //TIME_GC
24944
24945     dprintf(2,( "---- End of Relocate phase ----"));
24946 }
24947
24948 // This compares to see if tree is the current pinned plug and returns info
24949 // for this pinned plug. Also advances the pinned queue if that's the case.
24950 //
24951 // We don't change the values of the plug info if tree is not the same as 
24952 // the current pinned plug - the caller is responsible for setting the right
24953 // values to begin with.
24954 //
24955 // POPO TODO: We are keeping this temporarily as this is also used by realloc 
24956 // where it passes FALSE to deque_p, change it to use the same optimization 
24957 // as relocate. Not as essential since realloc is already a slow path.
24958 mark* gc_heap::get_next_pinned_entry (uint8_t* tree,
24959                                       BOOL* has_pre_plug_info_p, 
24960                                       BOOL* has_post_plug_info_p,
24961                                       BOOL deque_p)
24962 {
24963     if (!pinned_plug_que_empty_p())
24964     {
24965         mark* oldest_entry = oldest_pin();
24966         uint8_t* oldest_plug = pinned_plug (oldest_entry);
24967         if (tree == oldest_plug)
24968         {
24969             *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24970             *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24971
24972             if (deque_p)
24973             {
24974                 deque_pinned_plug();
24975             }
24976
24977             dprintf (3, ("found a pinned plug %Ix, pre: %d, post: %d", 
24978                 tree, 
24979                 (*has_pre_plug_info_p ? 1 : 0),
24980                 (*has_post_plug_info_p ? 1 : 0)));
24981
24982             return oldest_entry;
24983         }
24984     }
24985
24986     return NULL;
24987 }
24988
24989 // This also deques the oldest entry and update the oldest plug
24990 mark* gc_heap::get_oldest_pinned_entry (BOOL* has_pre_plug_info_p, 
24991                                         BOOL* has_post_plug_info_p)
24992 {
24993     mark* oldest_entry = oldest_pin();
24994     *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24995     *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24996
24997     deque_pinned_plug();
24998     update_oldest_pinned_plug();
24999     return oldest_entry;
25000 }
25001
25002 inline
25003 void gc_heap::copy_cards_range (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
25004 {
25005     if (copy_cards_p)
25006         copy_cards_for_addresses (dest, src, len);
25007     else
25008         clear_card_for_addresses (dest, dest + len);
25009 }
25010
25011 // POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
25012 // we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
25013 // we won't need to individually recover each overwritten part of plugs.
25014 inline
25015 void  gc_heap::gcmemcopy (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
25016 {
25017     if (dest != src)
25018     {
25019 #ifdef BACKGROUND_GC
25020         if (current_c_gc_state == c_gc_state_marking) 
25021         {
25022             //TODO: should look to see whether we should consider changing this
25023             // to copy a consecutive region of the mark array instead.
25024             copy_mark_bits_for_addresses (dest, src, len);
25025         }
25026 #endif //BACKGROUND_GC
25027         //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
25028         dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
25029         memcopy (dest - plug_skew, src - plug_skew, len);
25030 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25031         if (SoftwareWriteWatch::IsEnabledForGCHeap())
25032         {
25033             // The ranges [src - plug_kew .. src[ and [src + len - plug_skew .. src + len[ are ObjHeaders, which don't have GC
25034             // references, and are not relevant for write watch. The latter range actually corresponds to the ObjHeader for the
25035             // object at (src + len), so it can be ignored anyway.
25036             SoftwareWriteWatch::SetDirtyRegion(dest, len - plug_skew);
25037         }
25038 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25039         copy_cards_range (dest, src, len, copy_cards_p);
25040     }
25041 }
25042
25043 void gc_heap::compact_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, compact_args* args)
25044 {
25045     args->print();
25046     uint8_t* reloc_plug = plug + args->last_plug_relocation;
25047
25048     if (check_last_object_p)
25049     {
25050         size += sizeof (gap_reloc_pair);
25051         mark* entry = args->pinned_plug_entry;
25052
25053         if (args->is_shortened)
25054         {
25055             assert (entry->has_post_plug_info());
25056             entry->swap_post_plug_and_saved();
25057         }
25058         else
25059         {
25060             assert (entry->has_pre_plug_info());
25061             entry->swap_pre_plug_and_saved();
25062         }
25063     }
25064
25065     int  old_brick_entry =  brick_table [brick_of (plug)];
25066
25067     assert (node_relocation_distance (plug) == args->last_plug_relocation);
25068
25069 #ifdef FEATURE_STRUCTALIGN
25070     ptrdiff_t alignpad = node_alignpad(plug);
25071     if (alignpad)
25072     {
25073         make_unused_array (reloc_plug - alignpad, alignpad);
25074         if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug))
25075         {
25076             // The alignment padding is straddling one or more bricks;
25077             // it has to be the last "object" of its first brick.
25078             fix_brick_to_highest (reloc_plug - alignpad, reloc_plug);
25079         }
25080     }
25081 #else // FEATURE_STRUCTALIGN
25082     size_t unused_arr_size = 0; 
25083     BOOL  already_padded_p = FALSE;
25084 #ifdef SHORT_PLUGS
25085     if (is_plug_padded (plug))
25086     {
25087         already_padded_p = TRUE;
25088         clear_plug_padded (plug);
25089         unused_arr_size = Align (min_obj_size);
25090     }
25091 #endif //SHORT_PLUGS
25092     if (node_realigned (plug))
25093     {
25094         unused_arr_size += switch_alignment_size (already_padded_p);
25095     }
25096
25097     if (unused_arr_size != 0) 
25098     {
25099         make_unused_array (reloc_plug - unused_arr_size, unused_arr_size);
25100
25101         if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug))
25102         {
25103             dprintf (3, ("fix B for padding: %Id: %Ix->%Ix", 
25104                 unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug));
25105             // The alignment padding is straddling one or more bricks;
25106             // it has to be the last "object" of its first brick.
25107             fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug);
25108         }
25109     }
25110 #endif // FEATURE_STRUCTALIGN
25111
25112 #ifdef SHORT_PLUGS
25113     if (is_plug_padded (plug))
25114     {
25115         make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size));
25116
25117         if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug))
25118         {
25119             // The alignment padding is straddling one or more bricks;
25120             // it has to be the last "object" of its first brick.
25121             fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug);
25122         }
25123     }
25124 #endif //SHORT_PLUGS
25125
25126     gcmemcopy (reloc_plug, plug, size, args->copy_cards_p);
25127
25128     if (args->check_gennum_p)
25129     {
25130         int src_gennum = args->src_gennum;
25131         if (src_gennum == -1)
25132         {
25133             src_gennum = object_gennum (plug);
25134         }
25135
25136         int dest_gennum = object_gennum_plan (reloc_plug);
25137
25138         if (src_gennum < dest_gennum)
25139         {
25140             generation_allocation_size (generation_of (dest_gennum)) += size;
25141         }
25142     }
25143
25144     size_t current_reloc_brick = args->current_compacted_brick;
25145
25146     if (brick_of (reloc_plug) != current_reloc_brick)
25147     {
25148         dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix", 
25149             current_reloc_brick, brick_of (reloc_plug)));
25150
25151         if (args->before_last_plug)
25152         {
25153             dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)",
25154                      current_reloc_brick,
25155                      args->before_last_plug, 
25156                      (args->before_last_plug - brick_address (current_reloc_brick))));
25157
25158             {
25159                 set_brick (current_reloc_brick,
25160                         args->before_last_plug - brick_address (current_reloc_brick));
25161             }
25162         }
25163         current_reloc_brick = brick_of (reloc_plug);
25164     }
25165     size_t end_brick = brick_of (reloc_plug + size-1);
25166     if (end_brick != current_reloc_brick)
25167     {
25168         // The plug is straddling one or more bricks
25169         // It has to be the last plug of its first brick
25170         dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)",
25171                  current_reloc_brick, (size_t)reloc_plug,
25172                  (reloc_plug - brick_address (current_reloc_brick))));
25173
25174         {
25175             set_brick (current_reloc_brick,
25176                     reloc_plug - brick_address (current_reloc_brick));
25177         }
25178         // update all intervening brick
25179         size_t brick = current_reloc_brick + 1;
25180         dprintf (3,("setting intervening bricks %Ix->%Ix to -1",
25181             brick, (end_brick - 1)));
25182         while (brick < end_brick)
25183         {
25184             set_brick (brick, -1);
25185             brick++;
25186         }
25187         // code last brick offset as a plug address
25188         args->before_last_plug = brick_address (end_brick) -1;
25189         current_reloc_brick = end_brick;
25190         dprintf (3, ("setting before last to %Ix, last brick to %Ix",
25191             args->before_last_plug, current_reloc_brick));
25192     } 
25193     else
25194     {
25195         dprintf (3, ("still in the same brick: %Ix", end_brick));
25196         args->before_last_plug = reloc_plug;
25197     }
25198     args->current_compacted_brick = current_reloc_brick;
25199
25200     if (check_last_object_p)
25201     {
25202         mark* entry = args->pinned_plug_entry;
25203
25204         if (args->is_shortened)
25205         {
25206             entry->swap_post_plug_and_saved();
25207         }
25208         else
25209         {
25210             entry->swap_pre_plug_and_saved();
25211         }
25212     }
25213 }
25214
25215 void gc_heap::compact_in_brick (uint8_t* tree, compact_args* args)
25216 {
25217     assert (tree != NULL);
25218     int   left_node = node_left_child (tree);
25219     int   right_node = node_right_child (tree);
25220     ptrdiff_t relocation = node_relocation_distance (tree);
25221
25222     args->print();
25223
25224     if (left_node)
25225     {
25226         dprintf (3, ("B: L: %d->%Ix", left_node, (tree + left_node)));
25227         compact_in_brick ((tree + left_node), args);
25228     }
25229
25230     uint8_t*  plug = tree;
25231     BOOL   has_pre_plug_info_p = FALSE;
25232     BOOL   has_post_plug_info_p = FALSE;
25233
25234     if (tree == oldest_pinned_plug)
25235     {
25236         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
25237                                                            &has_post_plug_info_p);
25238         assert (tree == pinned_plug (args->pinned_plug_entry));
25239     }
25240
25241     if (args->last_plug != 0)
25242     {
25243         size_t gap_size = node_gap_size (tree);
25244         uint8_t*  gap = (plug - gap_size);
25245         uint8_t*  last_plug_end = gap;
25246         size_t last_plug_size = (last_plug_end - args->last_plug);
25247         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
25248             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
25249         
25250         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
25251         if (!check_last_object_p)
25252         {
25253             assert (last_plug_size >= Align (min_obj_size));
25254         }
25255
25256         compact_plug (args->last_plug, last_plug_size, check_last_object_p, args);
25257     }
25258     else
25259     {
25260         assert (!has_pre_plug_info_p);
25261     }
25262
25263     dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix", plug, relocation));
25264     args->last_plug = plug;
25265     args->last_plug_relocation = relocation;
25266     args->is_shortened = has_post_plug_info_p;
25267
25268     if (right_node)
25269     {
25270         dprintf (3, ("B: R: %d->%Ix", right_node, (tree + right_node)));
25271         compact_in_brick ((tree + right_node), args);
25272     }
25273 }
25274
25275 void gc_heap::recover_saved_pinned_info()
25276 {
25277     reset_pinned_queue_bos();
25278
25279     while (!(pinned_plug_que_empty_p()))
25280     {
25281         mark* oldest_entry = oldest_pin();
25282         oldest_entry->recover_plug_info();
25283 #ifdef GC_CONFIG_DRIVEN
25284         if (oldest_entry->has_pre_plug_info() && oldest_entry->has_post_plug_info())
25285             record_interesting_data_point (idp_pre_and_post_pin);
25286         else if (oldest_entry->has_pre_plug_info())
25287             record_interesting_data_point (idp_pre_pin);
25288         else if (oldest_entry->has_post_plug_info())
25289             record_interesting_data_point (idp_post_pin);
25290 #endif //GC_CONFIG_DRIVEN
25291
25292         deque_pinned_plug();
25293     }
25294 }
25295
25296 void gc_heap::compact_phase (int condemned_gen_number,
25297                              uint8_t*  first_condemned_address,
25298                              BOOL clear_cards)
25299 {
25300 //  %type%  category = quote (compact);
25301 #ifdef TIME_GC
25302         unsigned start;
25303         unsigned finish;
25304         start = GetCycleCount32();
25305 #endif //TIME_GC
25306     generation*   condemned_gen = generation_of (condemned_gen_number);
25307     uint8_t*  start_address = first_condemned_address;
25308     size_t   current_brick = brick_of (start_address);
25309     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
25310
25311     PREFIX_ASSUME(current_heap_segment != NULL);
25312
25313     reset_pinned_queue_bos();
25314     update_oldest_pinned_plug();
25315
25316     BOOL reused_seg = expand_reused_seg_p();
25317     if (reused_seg)
25318     {
25319         for (int i = 1; i <= max_generation; i++)
25320         {
25321             generation_allocation_size (generation_of (i)) = 0;
25322         }
25323     }
25324
25325     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
25326
25327     size_t  end_brick = brick_of (end_address-1);
25328     compact_args args;
25329     args.last_plug = 0;
25330     args.before_last_plug = 0;
25331     args.current_compacted_brick = ~((size_t)1);
25332     args.is_shortened = FALSE;
25333     args.pinned_plug_entry = 0;
25334     args.copy_cards_p =  (condemned_gen_number >= 1) || !clear_cards;
25335     args.check_gennum_p = reused_seg;
25336     if (args.check_gennum_p)
25337     {
25338         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
25339     }
25340
25341     dprintf (2,("---- Compact Phase: %Ix(%Ix)----", 
25342         first_condemned_address, brick_of (first_condemned_address)));
25343
25344 #ifdef MULTIPLE_HEAPS
25345     //restart
25346     if (gc_t_join.joined())
25347     {
25348 #endif //MULTIPLE_HEAPS
25349
25350 #ifdef MULTIPLE_HEAPS
25351         dprintf(3, ("Restarting for compaction"));
25352         gc_t_join.restart();
25353     }
25354 #endif //MULTIPLE_HEAPS
25355
25356     reset_pinned_queue_bos();
25357
25358 #ifdef FEATURE_LOH_COMPACTION
25359     if (loh_compacted_p)
25360     {
25361         compact_loh();
25362     }
25363 #endif //FEATURE_LOH_COMPACTION
25364
25365     if ((start_address < end_address) ||
25366         (condemned_gen_number == max_generation))
25367     {
25368         while (1)
25369         {
25370             if (current_brick > end_brick)
25371             {
25372                 if (args.last_plug != 0)
25373                 {
25374                     dprintf (3, ("compacting last plug: %Ix", args.last_plug))
25375                     compact_plug (args.last_plug,
25376                                   (heap_segment_allocated (current_heap_segment) - args.last_plug),
25377                                   args.is_shortened,
25378                                   &args);
25379                 }
25380
25381                 if (heap_segment_next_rw (current_heap_segment))
25382                 {
25383                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
25384                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
25385                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
25386                     args.last_plug = 0;
25387                     if (args.check_gennum_p)
25388                     {
25389                         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
25390                     }
25391                     continue;
25392                 }
25393                 else
25394                 {
25395                     if (args.before_last_plug !=0)
25396                     {
25397                         dprintf (3, ("Fixing last brick %Ix to point to plug %Ix",
25398                                     args.current_compacted_brick, (size_t)args.before_last_plug));
25399                         assert (args.current_compacted_brick != ~1u);
25400                         set_brick (args.current_compacted_brick,
25401                                    args.before_last_plug - brick_address (args.current_compacted_brick));
25402                     }
25403                     break;
25404                 }
25405             }
25406             {
25407                 int  brick_entry =  brick_table [ current_brick ];
25408                 dprintf (3, ("B: %Ix(%Ix)->%Ix", 
25409                     current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1)));
25410
25411                 if (brick_entry >= 0)
25412                 {
25413                     compact_in_brick ((brick_address (current_brick) + brick_entry -1),
25414                                       &args);
25415
25416                 }
25417             }
25418             current_brick++;
25419         }
25420     }
25421
25422     recover_saved_pinned_info();
25423
25424 #ifdef TIME_GC
25425     finish = GetCycleCount32();
25426     compact_time = finish - start;
25427 #endif //TIME_GC
25428
25429     concurrent_print_time_delta ("compact end");
25430
25431     dprintf(2,("---- End of Compact phase ----"));
25432 }
25433
25434 #ifdef MULTIPLE_HEAPS
25435
25436 #ifdef _MSC_VER
25437 #pragma warning(push)
25438 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
25439 #endif //_MSC_VER
25440 void gc_heap::gc_thread_stub (void* arg)
25441 {
25442     gc_heap* heap = (gc_heap*)arg;
25443     if (!gc_thread_no_affinitize_p)
25444     {
25445         GCThreadAffinity affinity;
25446         affinity.Group = GCThreadAffinity::None;
25447         affinity.Processor = GCThreadAffinity::None;
25448
25449         // We are about to set affinity for GC threads. It is a good place to set up NUMA and
25450         // CPU groups because the process mask, processor number, and group number are all
25451         // readily available.
25452         if (GCToOSInterface::CanEnableGCCPUGroups())
25453             set_thread_group_affinity_for_heap(heap->heap_number, &affinity);
25454         else
25455             set_thread_affinity_mask_for_heap(heap->heap_number, &affinity);
25456
25457         if (!GCToOSInterface::SetThreadAffinity(&affinity))
25458         {
25459             dprintf(1, ("Failed to set thread affinity for server GC thread"));
25460         }
25461     }
25462
25463     // server GC threads run at a higher priority than normal.
25464     GCToOSInterface::BoostThreadPriority();
25465     _alloca (256*heap->heap_number);
25466     heap->gc_thread_function();
25467 }
25468 #ifdef _MSC_VER
25469 #pragma warning(pop)
25470 #endif //_MSC_VER
25471
25472 #endif //MULTIPLE_HEAPS
25473
25474 #ifdef BACKGROUND_GC
25475
25476 #ifdef _MSC_VER
25477 #pragma warning(push)
25478 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
25479 #endif //_MSC_VER
25480 void gc_heap::bgc_thread_stub (void* arg)
25481 {
25482     gc_heap* heap = (gc_heap*)arg;
25483     heap->bgc_thread = GCToEEInterface::GetThread();
25484     assert(heap->bgc_thread != nullptr);
25485     heap->bgc_thread_function();
25486 }
25487 #ifdef _MSC_VER
25488 #pragma warning(pop)
25489 #endif //_MSC_VER
25490
25491 #endif //BACKGROUND_GC
25492
25493 /*------------------ Background GC ----------------------------*/
25494
25495 #ifdef BACKGROUND_GC
25496
25497 void gc_heap::background_drain_mark_list (int thread)
25498 {
25499     UNREFERENCED_PARAMETER(thread);
25500
25501     size_t saved_c_mark_list_index = c_mark_list_index;
25502
25503     if (saved_c_mark_list_index)
25504     {
25505         concurrent_print_time_delta ("SML");
25506     }
25507     while (c_mark_list_index != 0)
25508     {
25509         size_t current_index = c_mark_list_index - 1;
25510         uint8_t* o = c_mark_list [current_index];
25511         background_mark_object (o THREAD_NUMBER_ARG);
25512         c_mark_list_index--;
25513     }
25514     if (saved_c_mark_list_index)
25515     {
25516
25517         concurrent_print_time_delta ("EML");
25518     }
25519
25520     fire_drain_mark_list_event (saved_c_mark_list_index);
25521 }
25522
25523
25524 // The background GC version of scan_dependent_handles (see that method for a more in-depth comment).
25525 #ifdef MULTIPLE_HEAPS
25526 // Since we only scan dependent handles while we are stopped we'll never interfere with FGCs scanning
25527 // them. So we can use the same static variables.
25528 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
25529 {
25530     // Whenever we call this method there may have been preceding object promotions. So set
25531     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
25532     // based on the how the scanning proceeded).
25533     s_fUnscannedPromotions = TRUE;
25534
25535     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
25536     // the state of this thread's portion of the dependent handle table. That's because promotions on other
25537     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
25538     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
25539     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
25540     // as all the others or they'll get out of step).
25541     while (true)
25542     {
25543         // The various worker threads are all currently racing in this code. We need to work out if at least
25544         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
25545         // dependent handle table when both of the following conditions apply:
25546         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
25547         //     object happens to correspond to a primary in one of our handles we might potentially have to
25548         //     promote the associated secondary).
25549         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
25550         //
25551         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
25552         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
25553         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
25554         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
25555         // follows below. Note that we can't read this outside of the join since on any iteration apart from
25556         // the first threads will be racing between reading this value and completing their previous
25557         // iteration's table scan.
25558         //
25559         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
25560         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
25561         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
25562         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
25563         // we're safely joined.
25564         if (GCScan::GcDhUnpromotedHandlesExist(sc))
25565             s_fUnpromotedHandles = TRUE;
25566
25567         // Synchronize all the threads so we can read our state variables safely. The following shared
25568         // variable (indicating whether we should scan the tables or terminate the loop) will be set by a
25569         // single thread inside the join.
25570         bgc_t_join.join(this, gc_join_scan_dependent_handles);
25571         if (bgc_t_join.joined())
25572         {
25573             // We're synchronized so it's safe to read our shared state variables. We update another shared
25574             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
25575             // the loop. We scan if there has been at least one object promotion since last time and at least
25576             // one thread has a dependent handle table with a potential handle promotion possible.
25577             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
25578
25579             // Reset our shared state variables (ready to be set again on this scan or with a good initial
25580             // value for the next call if we're terminating the loop).
25581             s_fUnscannedPromotions = FALSE;
25582             s_fUnpromotedHandles = FALSE;
25583
25584             if (!s_fScanRequired)
25585             {
25586                 uint8_t* all_heaps_max = 0;
25587                 uint8_t* all_heaps_min = MAX_PTR;
25588                 int i;
25589                 for (i = 0; i < n_heaps; i++)
25590                 {
25591                     if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
25592                         all_heaps_max = g_heaps[i]->background_max_overflow_address;
25593                     if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
25594                         all_heaps_min = g_heaps[i]->background_min_overflow_address;
25595                 }
25596                 for (i = 0; i < n_heaps; i++)
25597                 {
25598                     g_heaps[i]->background_max_overflow_address = all_heaps_max;
25599                     g_heaps[i]->background_min_overflow_address = all_heaps_min;
25600                 }
25601             }
25602
25603             // Restart all the workers.
25604             dprintf(2, ("Starting all gc thread mark stack overflow processing"));
25605             bgc_t_join.restart();
25606         }
25607
25608         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25609         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
25610         // global flag indicating that at least one object promotion may have occurred (the usual comment
25611         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
25612         // exit the method since we unconditionally set this variable on method entry anyway).
25613         if (background_process_mark_overflow (sc->concurrent))
25614             s_fUnscannedPromotions = TRUE;
25615
25616         // If we decided that no scan was required we can terminate the loop now.
25617         if (!s_fScanRequired)
25618             break;
25619
25620         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
25621         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
25622         // could miss noting the promotion of some primary objects).
25623         bgc_t_join.join(this, gc_join_rescan_dependent_handles);
25624         if (bgc_t_join.joined())
25625         {
25626             // Restart all the workers.
25627             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
25628             bgc_t_join.restart();
25629         }
25630
25631         // If the portion of the dependent handle table managed by this worker has handles that could still be
25632         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
25633         // could require a rescan of handles on this or other workers.
25634         if (GCScan::GcDhUnpromotedHandlesExist(sc))
25635             if (GCScan::GcDhReScan(sc))
25636                 s_fUnscannedPromotions = TRUE;
25637     }
25638 }
25639 #else
25640 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
25641 {
25642     // Whenever we call this method there may have been preceding object promotions. So set
25643     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
25644     // based on the how the scanning proceeded).
25645     bool fUnscannedPromotions = true;
25646
25647     // Scan dependent handles repeatedly until there are no further promotions that can be made or we made a
25648     // scan without performing any new promotions.
25649     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
25650     {
25651         // On each iteration of the loop start with the assumption that no further objects have been promoted.
25652         fUnscannedPromotions = false;
25653
25654         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25655         // being visible. If there was an overflow (background_process_mark_overflow returned true) then
25656         // additional objects now appear to be promoted and we should set the flag.
25657         if (background_process_mark_overflow (sc->concurrent))
25658             fUnscannedPromotions = true;
25659
25660         // Perform the scan and set the flag if any promotions resulted.
25661         if (GCScan::GcDhReScan (sc))
25662             fUnscannedPromotions = true;
25663     }
25664
25665     // Perform a last processing of any overflowed mark stack.
25666     background_process_mark_overflow (sc->concurrent);
25667 }
25668 #endif //MULTIPLE_HEAPS
25669
25670 void gc_heap::recover_bgc_settings()
25671 {
25672     if ((settings.condemned_generation < max_generation) && recursive_gc_sync::background_running_p())
25673     {
25674         dprintf (2, ("restoring bgc settings"));
25675         settings = saved_bgc_settings;
25676         GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
25677     }
25678 }
25679
25680 void gc_heap::allow_fgc()
25681 {
25682     assert (bgc_thread == GCToEEInterface::GetThread());
25683     bool bToggleGC = false;
25684
25685     if (g_fSuspensionPending > 0)
25686     {
25687         bToggleGC = GCToEEInterface::EnablePreemptiveGC();
25688         if (bToggleGC)
25689         {
25690             GCToEEInterface::DisablePreemptiveGC();
25691         }
25692     }
25693 }
25694
25695 BOOL gc_heap::should_commit_mark_array()
25696 {
25697     return (recursive_gc_sync::background_running_p() || (current_bgc_state == bgc_initialized));
25698 }
25699
25700 void gc_heap::clear_commit_flag()
25701 {
25702     generation* gen = generation_of (max_generation);
25703     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25704     while (1)
25705     {
25706         if (seg == 0)
25707         {
25708             if (gen != large_object_generation)
25709             {
25710                 gen = large_object_generation;
25711                 seg = heap_segment_in_range (generation_start_segment (gen));
25712             }
25713             else
25714             {
25715                 break;
25716             }
25717         }
25718
25719         if (seg->flags & heap_segment_flags_ma_committed)
25720         {
25721             seg->flags &= ~heap_segment_flags_ma_committed;
25722         }
25723
25724         if (seg->flags & heap_segment_flags_ma_pcommitted)
25725         {
25726             seg->flags &= ~heap_segment_flags_ma_pcommitted;
25727         }
25728
25729         seg = heap_segment_next (seg);
25730     }
25731 }
25732
25733 void gc_heap::clear_commit_flag_global()
25734 {
25735 #ifdef MULTIPLE_HEAPS
25736     for (int i = 0; i < n_heaps; i++)
25737     {
25738         g_heaps[i]->clear_commit_flag();
25739     }
25740 #else
25741     clear_commit_flag();
25742 #endif //MULTIPLE_HEAPS
25743 }
25744
25745 void gc_heap::verify_mark_array_cleared (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25746 {
25747 #ifdef _DEBUG
25748     size_t  markw = mark_word_of (begin);
25749     size_t  markw_end = mark_word_of (end);
25750
25751     while (markw < markw_end)
25752     {
25753         if (mark_array_addr[markw])
25754         {
25755             dprintf  (1, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
25756                             markw, mark_array_addr[markw], mark_word_address (markw)));
25757             FATAL_GC_ERROR();
25758         }
25759         markw++;
25760     }
25761 #else // _DEBUG
25762     UNREFERENCED_PARAMETER(begin);
25763     UNREFERENCED_PARAMETER(end);
25764     UNREFERENCED_PARAMETER(mark_array_addr);
25765 #endif //_DEBUG
25766 }
25767
25768 void gc_heap::verify_mark_array_cleared (heap_segment* seg, uint32_t* mark_array_addr)
25769 {
25770     verify_mark_array_cleared (heap_segment_mem (seg), heap_segment_reserved (seg), mark_array_addr);
25771 }
25772
25773 BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp, 
25774                                          heap_segment* seg,
25775                                          uint32_t* new_card_table,
25776                                          uint8_t* new_lowest_address)
25777 {
25778     UNREFERENCED_PARAMETER(hp); // compiler bug? -- this *is*, indeed, referenced
25779
25780     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25781     uint8_t* end = heap_segment_reserved (seg);
25782
25783     uint8_t* lowest = hp->background_saved_lowest_address;
25784     uint8_t* highest = hp->background_saved_highest_address;
25785
25786     uint8_t* commit_start = NULL;
25787     uint8_t* commit_end = NULL;
25788     size_t commit_flag = 0;
25789
25790     if ((highest >= start) &&
25791         (lowest <= end))
25792     {
25793         if ((start >= lowest) && (end <= highest))
25794         {
25795             dprintf (GC_TABLE_LOG, ("completely in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25796                                     start, end, lowest, highest));
25797             commit_flag = heap_segment_flags_ma_committed;
25798         }
25799         else
25800         {
25801             dprintf (GC_TABLE_LOG, ("partially in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25802                                     start, end, lowest, highest));
25803             commit_flag = heap_segment_flags_ma_pcommitted;
25804         }
25805
25806         commit_start = max (lowest, start);
25807         commit_end = min (highest, end);
25808
25809         if (!commit_mark_array_by_range (commit_start, commit_end, hp->mark_array))
25810         {
25811             return FALSE;
25812         }
25813
25814         if (new_card_table == 0)
25815         {
25816             new_card_table = g_gc_card_table;
25817         }
25818
25819         if (hp->card_table != new_card_table)
25820         {
25821             if (new_lowest_address == 0)
25822             {
25823                 new_lowest_address = g_gc_lowest_address;
25824             }
25825
25826             uint32_t* ct = &new_card_table[card_word (gcard_of (new_lowest_address))];
25827             uint32_t* ma = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, new_lowest_address));
25828
25829             dprintf (GC_TABLE_LOG, ("table realloc-ed: %Ix->%Ix, MA: %Ix->%Ix", 
25830                                     hp->card_table, new_card_table,
25831                                     hp->mark_array, ma));
25832
25833             if (!commit_mark_array_by_range (commit_start, commit_end, ma))
25834             {
25835                 return FALSE;
25836             }
25837         }
25838
25839         seg->flags |= commit_flag;
25840     }
25841
25842     return TRUE;
25843 }
25844
25845 BOOL gc_heap::commit_mark_array_by_range (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25846 {
25847     size_t beg_word = mark_word_of (begin);
25848     size_t end_word = mark_word_of (align_on_mark_word (end));
25849     uint8_t* commit_start = align_lower_page ((uint8_t*)&mark_array_addr[beg_word]);
25850     uint8_t* commit_end = align_on_page ((uint8_t*)&mark_array_addr[end_word]);
25851     size_t size = (size_t)(commit_end - commit_start);
25852
25853 #ifdef SIMPLE_DPRINTF
25854     dprintf (GC_TABLE_LOG, ("range: %Ix->%Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), commit %Ix->%Ix(%Id)",
25855                             begin, end,
25856                             beg_word, end_word,
25857                             (end_word - beg_word) * sizeof (uint32_t),
25858                             &mark_array_addr[beg_word],
25859                             &mark_array_addr[end_word],
25860                             (size_t)(&mark_array_addr[end_word] - &mark_array_addr[beg_word]),
25861                             commit_start, commit_end,
25862                             size));
25863 #endif //SIMPLE_DPRINTF
25864
25865     if (virtual_commit (commit_start, size))
25866     {
25867         // We can only verify the mark array is cleared from begin to end, the first and the last
25868         // page aren't necessarily all cleared 'cause they could be used by other segments or 
25869         // card bundle.
25870         verify_mark_array_cleared (begin, end, mark_array_addr);
25871         return TRUE;
25872     }
25873     else
25874     {
25875         dprintf (GC_TABLE_LOG, ("failed to commit %Id bytes", (end_word - beg_word) * sizeof (uint32_t)));
25876         return FALSE;
25877     }
25878 }
25879
25880 BOOL gc_heap::commit_mark_array_with_check (heap_segment* seg, uint32_t* new_mark_array_addr)
25881 {
25882     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25883     uint8_t* end = heap_segment_reserved (seg);
25884
25885 #ifdef MULTIPLE_HEAPS
25886     uint8_t* lowest = heap_segment_heap (seg)->background_saved_lowest_address;
25887     uint8_t* highest = heap_segment_heap (seg)->background_saved_highest_address;
25888 #else
25889     uint8_t* lowest = background_saved_lowest_address;
25890     uint8_t* highest = background_saved_highest_address;
25891 #endif //MULTIPLE_HEAPS
25892
25893     if ((highest >= start) &&
25894         (lowest <= end))
25895     {
25896         start = max (lowest, start);
25897         end = min (highest, end);
25898         if (!commit_mark_array_by_range (start, end, new_mark_array_addr))
25899         {
25900             return FALSE;
25901         }
25902     }
25903
25904     return TRUE;
25905 }
25906
25907 BOOL gc_heap::commit_mark_array_by_seg (heap_segment* seg, uint32_t* mark_array_addr)
25908 {
25909     dprintf (GC_TABLE_LOG, ("seg: %Ix->%Ix; MA: %Ix",
25910         seg,
25911         heap_segment_reserved (seg),
25912         mark_array_addr));
25913     uint8_t* start = (heap_segment_read_only_p (seg) ? heap_segment_mem (seg) : (uint8_t*)seg);
25914
25915     return commit_mark_array_by_range (start, heap_segment_reserved (seg), mark_array_addr);
25916 }
25917
25918 BOOL gc_heap::commit_mark_array_bgc_init (uint32_t* mark_array_addr)
25919 {
25920     UNREFERENCED_PARAMETER(mark_array_addr);
25921
25922     dprintf (GC_TABLE_LOG, ("BGC init commit: lowest: %Ix, highest: %Ix, mark_array: %Ix", 
25923                             lowest_address, highest_address, mark_array));
25924
25925     generation* gen = generation_of (max_generation);
25926     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25927     while (1)
25928     {
25929         if (seg == 0)
25930         {
25931             if (gen != large_object_generation)
25932             {
25933                 gen = large_object_generation;
25934                 seg = heap_segment_in_range (generation_start_segment (gen));
25935             }
25936             else
25937             {
25938                 break;
25939             }
25940         }
25941
25942         dprintf (GC_TABLE_LOG, ("seg: %Ix, flags: %Id", seg, seg->flags));
25943
25944         if (!(seg->flags & heap_segment_flags_ma_committed))
25945         {
25946             // For ro segments they could always be only partially in range so we'd
25947             // be calling this at the beginning of every BGC. We are not making this 
25948             // more efficient right now - ro segments are currently only used by redhawk.
25949             if (heap_segment_read_only_p (seg))
25950             {
25951                 if ((heap_segment_mem (seg) >= lowest_address) && 
25952                     (heap_segment_reserved (seg) <= highest_address))
25953                 {
25954                     if (commit_mark_array_by_seg (seg, mark_array))
25955                     {
25956                         seg->flags |= heap_segment_flags_ma_committed;
25957                     }
25958                     else
25959                     {
25960                         return FALSE;
25961                     }
25962                 }
25963                 else
25964                 {
25965                     uint8_t* start = max (lowest_address, heap_segment_mem (seg));
25966                     uint8_t* end = min (highest_address, heap_segment_reserved (seg));
25967                     if (commit_mark_array_by_range (start, end, mark_array))
25968                     {
25969                         seg->flags |= heap_segment_flags_ma_pcommitted;
25970                     }
25971                     else
25972                     {
25973                         return FALSE;
25974                     }
25975                 }
25976             }
25977             else
25978             {
25979                 // For normal segments they are by design completely in range so just 
25980                 // commit the whole mark array for each seg.
25981                 if (commit_mark_array_by_seg (seg, mark_array))
25982                 {
25983                     if (seg->flags & heap_segment_flags_ma_pcommitted)
25984                     {
25985                         seg->flags &= ~heap_segment_flags_ma_pcommitted;
25986                     }
25987                     seg->flags |= heap_segment_flags_ma_committed;
25988                 }
25989                 else
25990                 {
25991                     return FALSE;
25992                 }
25993             }
25994         }
25995
25996         seg = heap_segment_next (seg);
25997     }
25998
25999     return TRUE;
26000 }
26001
26002 // This function doesn't check the commit flag since it's for a new array -
26003 // the mark_array flag for these segments will remain the same.
26004 BOOL gc_heap::commit_new_mark_array (uint32_t* new_mark_array_addr)
26005 {
26006     dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix", new_mark_array_addr));
26007     generation* gen = generation_of (max_generation);
26008     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
26009     while (1)
26010     {
26011         if (seg == 0)
26012         {
26013             if (gen != large_object_generation)
26014             {
26015                 gen = large_object_generation;
26016                 seg = heap_segment_in_range (generation_start_segment (gen));
26017             }
26018             else
26019             {
26020                 break;
26021             }
26022         }
26023
26024         if (!commit_mark_array_with_check (seg, new_mark_array_addr))
26025         {
26026             return FALSE;
26027         }
26028
26029         seg = heap_segment_next (seg);
26030     }
26031
26032 #ifdef MULTIPLE_HEAPS
26033     if (new_heap_segment)
26034     {
26035         if (!commit_mark_array_with_check (new_heap_segment, new_mark_array_addr))
26036         {
26037             return FALSE;
26038         }        
26039     }
26040 #endif //MULTIPLE_HEAPS
26041
26042     return TRUE;
26043 }
26044
26045 BOOL gc_heap::commit_new_mark_array_global (uint32_t* new_mark_array)
26046 {
26047 #ifdef MULTIPLE_HEAPS
26048     for (int i = 0; i < n_heaps; i++)
26049     {
26050         if (!g_heaps[i]->commit_new_mark_array (new_mark_array))
26051         {
26052             return FALSE;
26053         }
26054     }
26055 #else
26056     if (!commit_new_mark_array (new_mark_array))
26057     {
26058         return FALSE;
26059     }
26060 #endif //MULTIPLE_HEAPS
26061
26062     return TRUE;
26063 }
26064
26065 void gc_heap::decommit_mark_array_by_seg (heap_segment* seg)
26066 {
26067     // if BGC is disabled (the finalize watchdog does this at shutdown), the mark array could have
26068     // been set to NULL. 
26069     if (mark_array == NULL)
26070     {
26071         return;
26072     }
26073
26074     dprintf (GC_TABLE_LOG, ("decommitting seg %Ix(%Ix), MA: %Ix", seg, seg->flags, mark_array));
26075
26076     size_t flags = seg->flags;
26077
26078     if ((flags & heap_segment_flags_ma_committed) ||
26079         (flags & heap_segment_flags_ma_pcommitted))
26080     {
26081         uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
26082         uint8_t* end = heap_segment_reserved (seg);
26083
26084         if (flags & heap_segment_flags_ma_pcommitted)
26085         {
26086             start = max (lowest_address, start);
26087             end = min (highest_address, end);
26088         }
26089
26090         size_t beg_word = mark_word_of (start);
26091         size_t end_word = mark_word_of (align_on_mark_word (end));
26092         uint8_t* decommit_start = align_on_page ((uint8_t*)&mark_array[beg_word]);
26093         uint8_t* decommit_end = align_lower_page ((uint8_t*)&mark_array[end_word]);
26094         size_t size = (size_t)(decommit_end - decommit_start);
26095
26096 #ifdef SIMPLE_DPRINTF
26097         dprintf (GC_TABLE_LOG, ("seg: %Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), decommit %Ix->%Ix(%Id)",
26098                                 seg,
26099                                 beg_word, end_word,
26100                                 (end_word - beg_word) * sizeof (uint32_t),
26101                                 &mark_array[beg_word],
26102                                 &mark_array[end_word],
26103                                 (size_t)(&mark_array[end_word] - &mark_array[beg_word]),
26104                                 decommit_start, decommit_end,
26105                                 size));
26106 #endif //SIMPLE_DPRINTF
26107         
26108         if (decommit_start < decommit_end)
26109         {
26110             if (!virtual_decommit (decommit_start, size))
26111             {
26112                 dprintf (GC_TABLE_LOG, ("decommit on %Ix for %Id bytes failed", 
26113                                         decommit_start, size));
26114                 assert (!"decommit failed");
26115             }
26116         }
26117
26118         dprintf (GC_TABLE_LOG, ("decommited [%Ix for address [%Ix", beg_word, seg));
26119     }
26120 }
26121
26122 void gc_heap::background_mark_phase ()
26123 {
26124     verify_mark_array_cleared();
26125
26126     ScanContext sc;
26127     sc.thread_number = heap_number;
26128     sc.promotion = TRUE;
26129     sc.concurrent = FALSE;
26130
26131     THREAD_FROM_HEAP;
26132     BOOL cooperative_mode = TRUE;
26133 #ifndef MULTIPLE_HEAPS
26134     const int thread = heap_number;
26135 #endif //!MULTIPLE_HEAPS
26136
26137     dprintf(2,("-(GC%d)BMark-", VolatileLoad(&settings.gc_index)));
26138
26139     assert (settings.concurrent);
26140
26141 #ifdef TIME_GC
26142     unsigned start;
26143     unsigned finish;
26144     start = GetCycleCount32();
26145 #endif //TIME_GC
26146
26147 #ifdef FFIND_OBJECT
26148     if (gen0_must_clear_bricks > 0)
26149         gen0_must_clear_bricks--;
26150 #endif //FFIND_OBJECT
26151
26152     background_soh_alloc_count = 0;
26153     background_loh_alloc_count = 0;
26154     bgc_overflow_count = 0;
26155
26156     bpromoted_bytes (heap_number) = 0;
26157     static uint32_t num_sizedrefs = 0;
26158
26159     background_min_overflow_address = MAX_PTR;
26160     background_max_overflow_address = 0;
26161     background_min_soh_overflow_address = MAX_PTR;
26162     background_max_soh_overflow_address = 0;
26163     processed_soh_overflow_p = FALSE;
26164
26165     {
26166         //set up the mark lists from g_mark_list
26167         assert (g_mark_list);
26168         mark_list = g_mark_list;
26169         //dont use the mark list for full gc
26170         //because multiple segments are more complex to handle and the list
26171         //is likely to overflow
26172         mark_list_end = &mark_list [0];
26173         mark_list_index = &mark_list [0];
26174
26175         c_mark_list_index = 0;
26176
26177 #ifndef MULTIPLE_HEAPS
26178         shigh = (uint8_t*) 0;
26179         slow  = MAX_PTR;
26180 #endif //MULTIPLE_HEAPS
26181
26182         generation*   gen = generation_of (max_generation);
26183
26184         dprintf(3,("BGC: stack marking"));
26185         sc.concurrent = TRUE;
26186
26187         GCScan::GcScanRoots(background_promote_callback,
26188                                 max_generation, max_generation,
26189                                 &sc);
26190     }
26191
26192     {
26193         dprintf(3,("BGC: finalization marking"));
26194         finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0);
26195     }
26196
26197     size_t total_loh_size = generation_size (max_generation + 1);
26198     bgc_begin_loh_size = total_loh_size;
26199     bgc_alloc_spin_loh = 0;
26200     bgc_loh_size_increased = 0;
26201     bgc_loh_allocated_in_free = 0;
26202     size_t total_soh_size = generation_sizes (generation_of (max_generation));
26203
26204     dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
26205
26206     {
26207         //concurrent_print_time_delta ("copying stack roots");
26208         concurrent_print_time_delta ("CS");
26209
26210         FIRE_EVENT(BGC1stNonConEnd);
26211
26212         expanded_in_fgc = FALSE;
26213         saved_overflow_ephemeral_seg = 0;
26214         current_bgc_state = bgc_reset_ww;
26215
26216         // we don't need a join here - just whichever thread that gets here
26217         // first can change the states and call restart_vm.
26218         // this is not true - we can't let the EE run when we are scanning stack.
26219         // since we now allow reset ww to run concurrently and have a join for it,
26220         // we can do restart ee on the 1st thread that got here. Make sure we handle the 
26221         // sizedref handles correctly.
26222 #ifdef MULTIPLE_HEAPS
26223         bgc_t_join.join(this, gc_join_restart_ee);
26224         if (bgc_t_join.joined())
26225 #endif //MULTIPLE_HEAPS
26226         {
26227 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26228             // Resetting write watch for software write watch is pretty fast, much faster than for hardware write watch. Reset
26229             // can be done while the runtime is suspended or after the runtime is restarted, the preference was to reset while
26230             // the runtime is suspended. The reset for hardware write watch is done after the runtime is restarted below.
26231 #ifdef WRITE_WATCH
26232             concurrent_print_time_delta ("CRWW begin");
26233
26234 #ifdef MULTIPLE_HEAPS
26235             for (int i = 0; i < n_heaps; i++)
26236             {
26237                 g_heaps[i]->reset_write_watch (FALSE);
26238             }
26239 #else
26240             reset_write_watch (FALSE);
26241 #endif //MULTIPLE_HEAPS
26242
26243             concurrent_print_time_delta ("CRWW");
26244 #endif //WRITE_WATCH
26245 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26246
26247             num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
26248
26249             // this c_write is not really necessary because restart_vm
26250             // has an instruction that will flush the cpu cache (interlocked
26251             // or whatever) but we don't want to rely on that.
26252             dprintf (BGC_LOG, ("setting cm_in_progress"));
26253             c_write (cm_in_progress, TRUE);
26254
26255             //restart all thread, doing the marking from the array
26256             assert (dont_restart_ee_p);
26257             dont_restart_ee_p = FALSE;
26258
26259             restart_vm();
26260             GCToOSInterface::YieldThread (0);
26261 #ifdef MULTIPLE_HEAPS
26262             dprintf(3, ("Starting all gc threads for gc"));
26263             bgc_t_join.restart();
26264 #endif //MULTIPLE_HEAPS
26265         }
26266
26267 #ifdef MULTIPLE_HEAPS
26268         bgc_t_join.join(this, gc_join_after_reset);
26269         if (bgc_t_join.joined())
26270 #endif //MULTIPLE_HEAPS
26271         {
26272             disable_preemptive (true);
26273
26274 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26275             // When software write watch is enabled, resetting write watch is done while the runtime is suspended above. The
26276             // post-reset call to revisit_written_pages is only necessary for concurrent reset_write_watch, to discard dirtied
26277             // pages during the concurrent reset.
26278
26279 #ifdef WRITE_WATCH
26280             concurrent_print_time_delta ("CRWW begin");
26281
26282 #ifdef MULTIPLE_HEAPS
26283             for (int i = 0; i < n_heaps; i++)
26284             {
26285                 g_heaps[i]->reset_write_watch (TRUE);
26286             }
26287 #else
26288             reset_write_watch (TRUE);
26289 #endif //MULTIPLE_HEAPS
26290
26291             concurrent_print_time_delta ("CRWW");
26292 #endif //WRITE_WATCH
26293
26294 #ifdef MULTIPLE_HEAPS
26295             for (int i = 0; i < n_heaps; i++)
26296             {
26297                 g_heaps[i]->revisit_written_pages (TRUE, TRUE);
26298             }
26299 #else
26300             revisit_written_pages (TRUE, TRUE);
26301 #endif //MULTIPLE_HEAPS
26302
26303             concurrent_print_time_delta ("CRW");
26304 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26305
26306 #ifdef MULTIPLE_HEAPS
26307             for (int i = 0; i < n_heaps; i++)
26308             {
26309                 g_heaps[i]->current_bgc_state = bgc_mark_handles;
26310             }
26311 #else
26312             current_bgc_state = bgc_mark_handles;
26313 #endif //MULTIPLE_HEAPS
26314
26315             current_c_gc_state = c_gc_state_marking;
26316
26317             enable_preemptive ();
26318
26319 #ifdef MULTIPLE_HEAPS
26320             dprintf(3, ("Joining BGC threads after resetting writewatch"));
26321             bgc_t_join.restart();
26322 #endif //MULTIPLE_HEAPS
26323         }
26324
26325         disable_preemptive (true);
26326
26327         if (num_sizedrefs > 0)
26328         {
26329             GCScan::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc);
26330
26331             enable_preemptive ();
26332
26333 #ifdef MULTIPLE_HEAPS
26334             bgc_t_join.join(this, gc_join_scan_sizedref_done);
26335             if (bgc_t_join.joined())
26336             {
26337                 dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots"));
26338                 bgc_t_join.restart();
26339             }
26340 #endif //MULTIPLE_HEAPS
26341
26342             disable_preemptive (true);
26343         }
26344
26345         dprintf (3,("BGC: handle table marking"));
26346         GCScan::GcScanHandles(background_promote,
26347                                   max_generation, max_generation,
26348                                   &sc);
26349         //concurrent_print_time_delta ("concurrent marking handle table");
26350         concurrent_print_time_delta ("CRH");
26351
26352         current_bgc_state = bgc_mark_stack;
26353         dprintf (2,("concurrent draining mark list"));
26354         background_drain_mark_list (thread);
26355         //concurrent_print_time_delta ("concurrent marking stack roots");
26356         concurrent_print_time_delta ("CRS");
26357
26358         dprintf (2,("concurrent revisiting dirtied pages"));
26359         revisit_written_pages (TRUE);
26360         revisit_written_pages (TRUE);
26361         //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH");
26362         concurrent_print_time_delta ("CRre");
26363
26364         enable_preemptive ();
26365
26366 #ifdef MULTIPLE_HEAPS
26367         bgc_t_join.join(this, gc_join_concurrent_overflow);
26368         if (bgc_t_join.joined())
26369         {
26370             uint8_t* all_heaps_max = 0;
26371             uint8_t* all_heaps_min = MAX_PTR;
26372             int i;
26373             for (i = 0; i < n_heaps; i++)
26374             {
26375                 dprintf (3, ("heap %d overflow max is %Ix, min is %Ix", 
26376                     i,
26377                     g_heaps[i]->background_max_overflow_address,
26378                     g_heaps[i]->background_min_overflow_address));
26379                 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
26380                     all_heaps_max = g_heaps[i]->background_max_overflow_address;
26381                 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
26382                     all_heaps_min = g_heaps[i]->background_min_overflow_address;
26383             }
26384             for (i = 0; i < n_heaps; i++)
26385             {
26386                 g_heaps[i]->background_max_overflow_address = all_heaps_max;
26387                 g_heaps[i]->background_min_overflow_address = all_heaps_min;
26388             }
26389             dprintf(3, ("Starting all bgc threads after updating the overflow info"));
26390             bgc_t_join.restart();
26391         }
26392 #endif //MULTIPLE_HEAPS
26393
26394         disable_preemptive (true);
26395
26396         dprintf (2, ("before CRov count: %d", bgc_overflow_count));
26397         bgc_overflow_count = 0;
26398         background_process_mark_overflow (TRUE);
26399         dprintf (2, ("after CRov count: %d", bgc_overflow_count));
26400         bgc_overflow_count = 0;
26401         //concurrent_print_time_delta ("concurrent processing mark overflow");
26402         concurrent_print_time_delta ("CRov");
26403
26404         // Stop all threads, crawl all stacks and revisit changed pages.
26405         FIRE_EVENT(BGC1stConEnd);
26406
26407         dprintf (2, ("Stopping the EE"));
26408
26409         enable_preemptive ();
26410
26411 #ifdef MULTIPLE_HEAPS
26412         bgc_t_join.join(this, gc_join_suspend_ee);
26413         if (bgc_t_join.joined())
26414         {
26415             bgc_threads_sync_event.Reset();
26416
26417             dprintf(3, ("Joining BGC threads for non concurrent final marking"));
26418             bgc_t_join.restart();
26419         }
26420 #endif //MULTIPLE_HEAPS
26421
26422         if (heap_number == 0)
26423         {
26424             enter_spin_lock (&gc_lock);
26425
26426             bgc_suspend_EE ();
26427             //suspend_EE ();
26428             bgc_threads_sync_event.Set();
26429         }
26430         else
26431         {
26432             bgc_threads_sync_event.Wait(INFINITE, FALSE);
26433             dprintf (2, ("bgc_threads_sync_event is signalled"));
26434         }
26435
26436         assert (settings.concurrent);
26437         assert (settings.condemned_generation == max_generation);
26438
26439         dprintf (2, ("clearing cm_in_progress"));
26440         c_write (cm_in_progress, FALSE);
26441
26442         bgc_alloc_lock->check();
26443
26444         current_bgc_state = bgc_final_marking;
26445
26446         //concurrent_print_time_delta ("concurrent marking ended");
26447         concurrent_print_time_delta ("CR");
26448
26449         FIRE_EVENT(BGC2ndNonConBegin);
26450
26451         mark_absorb_new_alloc();
26452
26453         // We need a join here 'cause find_object would complain if the gen0
26454         // bricks of another heap haven't been fixed up. So we need to make sure
26455         // that every heap's gen0 bricks are fixed up before we proceed.
26456 #ifdef MULTIPLE_HEAPS
26457         bgc_t_join.join(this, gc_join_after_absorb);
26458         if (bgc_t_join.joined())
26459         {
26460             dprintf(3, ("Joining BGC threads after absorb"));
26461             bgc_t_join.restart();
26462         }
26463 #endif //MULTIPLE_HEAPS
26464
26465         // give VM a chance to do work
26466         GCToEEInterface::GcBeforeBGCSweepWork();
26467
26468         //reset the flag, indicating that the EE no longer expect concurrent
26469         //marking
26470         sc.concurrent = FALSE;
26471
26472         total_loh_size = generation_size (max_generation + 1);
26473         total_soh_size = generation_sizes (generation_of (max_generation));
26474
26475         dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
26476
26477         dprintf (2, ("nonconcurrent marking stack roots"));
26478         GCScan::GcScanRoots(background_promote,
26479                                 max_generation, max_generation,
26480                                 &sc);
26481         //concurrent_print_time_delta ("nonconcurrent marking stack roots");
26482         concurrent_print_time_delta ("NRS");
26483
26484 //        finalize_queue->EnterFinalizeLock();
26485         finalize_queue->GcScanRoots(background_promote, heap_number, 0);
26486 //        finalize_queue->LeaveFinalizeLock();
26487
26488         dprintf (2, ("nonconcurrent marking handle table"));
26489         GCScan::GcScanHandles(background_promote,
26490                                   max_generation, max_generation,
26491                                   &sc);
26492         //concurrent_print_time_delta ("nonconcurrent marking handle table");
26493         concurrent_print_time_delta ("NRH");
26494
26495         dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index)));
26496         revisit_written_pages (FALSE);
26497         //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH");
26498         concurrent_print_time_delta ("NRre LOH");
26499
26500 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26501 #ifdef MULTIPLE_HEAPS
26502         bgc_t_join.join(this, gc_join_disable_software_write_watch);
26503         if (bgc_t_join.joined())
26504 #endif // MULTIPLE_HEAPS
26505         {
26506             // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to
26507             // avoid further perf penalty after the runtime is restarted
26508             SoftwareWriteWatch::DisableForGCHeap();
26509
26510 #ifdef MULTIPLE_HEAPS
26511             dprintf(3, ("Restarting BGC threads after disabling software write watch"));
26512             bgc_t_join.restart();
26513 #endif // MULTIPLE_HEAPS
26514         }
26515 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26516
26517         dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count));
26518         bgc_overflow_count = 0;
26519
26520         // Dependent handles need to be scanned with a special algorithm (see the header comment on
26521         // scan_dependent_handles for more detail). We perform an initial scan without processing any mark
26522         // stack overflow. This is not guaranteed to complete the operation but in a common case (where there
26523         // are no dependent handles that are due to be collected) it allows us to optimize away further scans.
26524         // The call to background_scan_dependent_handles is what will cycle through more iterations if
26525         // required and will also perform processing of any mark stack overflow once the dependent handle
26526         // table has been fully promoted.
26527         dprintf (2, ("1st dependent handle scan and process mark overflow"));
26528         GCScan::GcDhInitialScan(background_promote, max_generation, max_generation, &sc);
26529         background_scan_dependent_handles (&sc);
26530         //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow");
26531         concurrent_print_time_delta ("NR 1st Hov");
26532
26533         dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count));
26534         bgc_overflow_count = 0;
26535
26536 #ifdef MULTIPLE_HEAPS
26537         bgc_t_join.join(this, gc_join_null_dead_short_weak);
26538         if (bgc_t_join.joined())
26539 #endif //MULTIPLE_HEAPS
26540         {
26541             GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc);
26542
26543 #ifdef MULTIPLE_HEAPS
26544             dprintf(3, ("Joining BGC threads for short weak handle scan"));
26545             bgc_t_join.restart();
26546 #endif //MULTIPLE_HEAPS
26547         }
26548
26549         // null out the target of short weakref that were not promoted.
26550         GCScan::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc);
26551
26552         //concurrent_print_time_delta ("bgc GcShortWeakPtrScan");
26553         concurrent_print_time_delta ("NR GcShortWeakPtrScan");
26554     }
26555
26556     {
26557 #ifdef MULTIPLE_HEAPS
26558         bgc_t_join.join(this, gc_join_scan_finalization);
26559         if (bgc_t_join.joined())
26560         {
26561             dprintf(3, ("Joining BGC threads for finalization"));
26562             bgc_t_join.restart();
26563         }
26564 #endif //MULTIPLE_HEAPS
26565
26566         //Handle finalization.
26567         dprintf(3,("Marking finalization data"));
26568         //concurrent_print_time_delta ("bgc joined to mark finalization");
26569         concurrent_print_time_delta ("NRj");
26570
26571 //        finalize_queue->EnterFinalizeLock();
26572         finalize_queue->ScanForFinalization (background_promote, max_generation, FALSE, __this);
26573 //        finalize_queue->LeaveFinalizeLock();
26574
26575         concurrent_print_time_delta ("NRF");
26576     }
26577
26578     dprintf (2, ("before NR 2nd Hov count: %d", bgc_overflow_count));
26579     bgc_overflow_count = 0;
26580
26581     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
26582     // for finalization. As before background_scan_dependent_handles will also process any mark stack
26583     // overflow.
26584     dprintf (2, ("2nd dependent handle scan and process mark overflow"));
26585     background_scan_dependent_handles (&sc);
26586     //concurrent_print_time_delta ("2nd nonconcurrent dependent handle scan and process mark overflow");
26587     concurrent_print_time_delta ("NR 2nd Hov");
26588
26589 #ifdef MULTIPLE_HEAPS
26590     bgc_t_join.join(this, gc_join_null_dead_long_weak);
26591     if (bgc_t_join.joined())
26592     {
26593         dprintf(2, ("Joining BGC threads for weak pointer deletion"));
26594         bgc_t_join.restart();
26595     }
26596 #endif //MULTIPLE_HEAPS
26597
26598     // null out the target of long weakref that were not promoted.
26599     GCScan::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc);
26600     concurrent_print_time_delta ("NR GcWeakPtrScan");
26601
26602 #ifdef MULTIPLE_HEAPS
26603     bgc_t_join.join(this, gc_join_null_dead_syncblk);
26604     if (bgc_t_join.joined())
26605 #endif //MULTIPLE_HEAPS
26606     {
26607         dprintf (2, ("calling GcWeakPtrScanBySingleThread"));
26608         // scan for deleted entries in the syncblk cache
26609         GCScan::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc);
26610         concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread");
26611 #ifdef MULTIPLE_HEAPS
26612         dprintf(2, ("Starting BGC threads for end of background mark phase"));
26613         bgc_t_join.restart();
26614 #endif //MULTIPLE_HEAPS
26615     }
26616
26617     gen0_bricks_cleared = FALSE;
26618
26619     dprintf (2, ("end of bgc mark: loh: %d, soh: %d", 
26620                  generation_size (max_generation + 1), 
26621                  generation_sizes (generation_of (max_generation))));
26622
26623     for (int gen_idx = max_generation; gen_idx <= (max_generation + 1); gen_idx++)
26624     {
26625         generation* gen = generation_of (gen_idx);
26626         dynamic_data* dd = dynamic_data_of (gen_idx);
26627         dd_begin_data_size (dd) = generation_size (gen_idx) - 
26628                                    (generation_free_list_space (gen) + generation_free_obj_space (gen)) -
26629                                    Align (size (generation_allocation_start (gen)));
26630         dd_survived_size (dd) = 0;
26631         dd_pinned_survived_size (dd) = 0;
26632         dd_artificial_pinned_survived_size (dd) = 0;
26633         dd_added_pinned_size (dd) = 0;
26634     }
26635
26636     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26637     PREFIX_ASSUME(seg != NULL);
26638
26639     while (seg)
26640     {
26641         seg->flags &= ~heap_segment_flags_swept;
26642
26643         if (heap_segment_allocated (seg) == heap_segment_mem (seg))
26644         {
26645             // This can't happen...
26646             FATAL_GC_ERROR();
26647         }
26648
26649         if (seg == ephemeral_heap_segment)
26650         {
26651             heap_segment_background_allocated (seg) = generation_allocation_start (generation_of (max_generation - 1));
26652         }
26653         else
26654         {
26655             heap_segment_background_allocated (seg) = heap_segment_allocated (seg);
26656         }
26657
26658         dprintf (2, ("seg %Ix background allocated is %Ix", 
26659                       heap_segment_mem (seg), 
26660                       heap_segment_background_allocated (seg)));
26661         seg = heap_segment_next_rw (seg);
26662     }
26663
26664     // We need to void alloc contexts here 'cause while background_ephemeral_sweep is running
26665     // we can't let the user code consume the left over parts in these alloc contexts.
26666     repair_allocation_contexts (FALSE);
26667
26668 #ifdef TIME_GC
26669         finish = GetCycleCount32();
26670         mark_time = finish - start;
26671 #endif //TIME_GC
26672
26673     dprintf (2, ("end of bgc mark: gen2 free list space: %d, free obj space: %d", 
26674         generation_free_list_space (generation_of (max_generation)), 
26675         generation_free_obj_space (generation_of (max_generation))));
26676
26677     dprintf(2,("---- (GC%d)End of background mark phase ----", VolatileLoad(&settings.gc_index)));
26678 }
26679
26680 void
26681 gc_heap::suspend_EE ()
26682 {
26683     dprintf (2, ("suspend_EE"));
26684 #ifdef MULTIPLE_HEAPS
26685     gc_heap* hp = gc_heap::g_heaps[0];
26686     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26687 #else
26688     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26689 #endif //MULTIPLE_HEAPS
26690 }
26691
26692 #ifdef MULTIPLE_HEAPS
26693 void
26694 gc_heap::bgc_suspend_EE ()
26695 {
26696     for (int i = 0; i < n_heaps; i++)
26697     {
26698         gc_heap::g_heaps[i]->reset_gc_done();
26699     }
26700     gc_started = TRUE;
26701     dprintf (2, ("bgc_suspend_EE"));
26702     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26703
26704     gc_started = FALSE;
26705     for (int i = 0; i < n_heaps; i++)
26706     {
26707         gc_heap::g_heaps[i]->set_gc_done();
26708     }
26709 }
26710 #else
26711 void
26712 gc_heap::bgc_suspend_EE ()
26713 {
26714     reset_gc_done();
26715     gc_started = TRUE;
26716     dprintf (2, ("bgc_suspend_EE"));
26717     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26718     gc_started = FALSE;
26719     set_gc_done();
26720 }
26721 #endif //MULTIPLE_HEAPS
26722
26723 void
26724 gc_heap::restart_EE ()
26725 {
26726     dprintf (2, ("restart_EE"));
26727 #ifdef MULTIPLE_HEAPS
26728     GCToEEInterface::RestartEE(FALSE);
26729 #else
26730     GCToEEInterface::RestartEE(FALSE);
26731 #endif //MULTIPLE_HEAPS
26732 }
26733
26734 inline uint8_t* gc_heap::high_page ( heap_segment* seg, BOOL concurrent_p)
26735 {
26736     if (concurrent_p)
26737     {
26738         uint8_t* end = ((seg == ephemeral_heap_segment) ?
26739                      generation_allocation_start (generation_of (max_generation-1)) :
26740                      heap_segment_allocated (seg));
26741         return align_lower_page (end);
26742     }
26743     else 
26744     {
26745         return heap_segment_allocated (seg);
26746     }
26747 }
26748
26749 void gc_heap::revisit_written_page (uint8_t* page,
26750                                     uint8_t* end,
26751                                     BOOL concurrent_p,
26752                                     heap_segment* seg,
26753                                     uint8_t*& last_page,
26754                                     uint8_t*& last_object,
26755                                     BOOL large_objects_p,
26756                                     size_t& num_marked_objects)
26757 {
26758     UNREFERENCED_PARAMETER(seg);
26759
26760     uint8_t*   start_address = page;
26761     uint8_t*   o             = 0;
26762     int align_const = get_alignment_constant (!large_objects_p);
26763     uint8_t* high_address = end;
26764     uint8_t* current_lowest_address = background_saved_lowest_address;
26765     uint8_t* current_highest_address = background_saved_highest_address;
26766     BOOL no_more_loop_p = FALSE;
26767
26768     THREAD_FROM_HEAP;
26769 #ifndef MULTIPLE_HEAPS
26770     const int thread = heap_number;
26771 #endif //!MULTIPLE_HEAPS
26772
26773     if (large_objects_p)
26774     {
26775         o = last_object;
26776     }
26777     else
26778     {
26779         if (((last_page + WRITE_WATCH_UNIT_SIZE) == page)
26780             || (start_address <= last_object))
26781         {
26782             o = last_object;
26783         }
26784         else
26785         {
26786             o = find_first_object (start_address, last_object);
26787             // We can visit the same object again, but on a different page.
26788             assert (o >= last_object);
26789         }
26790     }
26791
26792     dprintf (3,("page %Ix start: %Ix, %Ix[ ",
26793                (size_t)page, (size_t)o,
26794                (size_t)(min (high_address, page + WRITE_WATCH_UNIT_SIZE))));
26795
26796     while (o < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26797     {
26798         size_t s;
26799
26800         if (concurrent_p && large_objects_p)
26801         {
26802             bgc_alloc_lock->bgc_mark_set (o);
26803
26804             if (((CObjectHeader*)o)->IsFree())
26805             {
26806                 s = unused_array_size (o);
26807             }
26808             else
26809             {
26810                 s = size (o);
26811             }
26812         }
26813         else
26814         {
26815             s = size (o);
26816         }
26817
26818         dprintf (3,("Considering object %Ix(%s)", (size_t)o, (background_object_marked (o, FALSE) ? "bm" : "nbm")));
26819
26820         assert (Align (s) >= Align (min_obj_size));
26821
26822         uint8_t* next_o =  o + Align (s, align_const);
26823
26824         if (next_o >= start_address) 
26825         {
26826 #ifdef MULTIPLE_HEAPS
26827             if (concurrent_p)
26828             {
26829                 // We set last_object here for SVR BGC here because SVR BGC has more than 
26830                 // one GC thread. When we have more than one GC thread we would run into this 
26831                 // situation if we skipped unmarked objects:
26832                 // bgc thread 1 calls GWW, and detect object X not marked so it would skip it 
26833                 // for revisit. 
26834                 // bgc thread 2 marks X and all its current children.
26835                 // user thread comes along and dirties more (and later) pages in X.
26836                 // bgc thread 1 calls GWW again and gets those later pages but it will not mark anything
26837                 // on them because it had already skipped X. We need to detect that this object is now
26838                 // marked and mark the children on the dirtied pages.
26839                 // In the future if we have less BGC threads than we have heaps we should add
26840                 // the check to the number of BGC threads.
26841                 last_object = o;
26842             }
26843 #endif //MULTIPLE_HEAPS
26844
26845             if (contain_pointers (o) &&
26846                 (!((o >= current_lowest_address) && (o < current_highest_address)) ||
26847                 background_marked (o)))
26848             {
26849                 dprintf (3, ("going through %Ix", (size_t)o));
26850                 go_through_object (method_table(o), o, s, poo, start_address, use_start, (o + s),
26851                                     if ((uint8_t*)poo >= min (high_address, page + WRITE_WATCH_UNIT_SIZE))
26852                                     {
26853                                         no_more_loop_p = TRUE;
26854                                         goto end_limit;
26855                                     }
26856                                     uint8_t* oo = *poo;
26857
26858                                     num_marked_objects++;
26859                                     background_mark_object (oo THREAD_NUMBER_ARG);
26860                                 );
26861             }
26862             else if (
26863                 concurrent_p &&
26864 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP // see comment below
26865                 large_objects_p &&
26866 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26867                 ((CObjectHeader*)o)->IsFree() &&
26868                 (next_o > min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26869             {
26870                 // We need to not skip the object here because of this corner scenario:
26871                 // A large object was being allocated during BGC mark so we first made it 
26872                 // into a free object, then cleared its memory. In this loop we would detect
26873                 // that it's a free object which normally we would skip. But by the next time
26874                 // we call GetWriteWatch we could still be on this object and the object had
26875                 // been made into a valid object and some of its memory was changed. We need
26876                 // to be sure to process those written pages so we can't skip the object just
26877                 // yet.
26878                 //
26879                 // Similarly, when using software write watch, don't advance last_object when
26880                 // the current object is a free object that spans beyond the current page or
26881                 // high_address. Software write watch acquires gc_lock before the concurrent
26882                 // GetWriteWatch() call during revisit_written_pages(). A foreground GC may
26883                 // happen at that point and allocate from this free region, so when
26884                 // revisit_written_pages() continues, it cannot skip now-valid objects in this
26885                 // region.
26886                 no_more_loop_p = TRUE;
26887                 goto end_limit;                
26888             }
26889         }
26890 end_limit:
26891         if (concurrent_p && large_objects_p)
26892         {
26893             bgc_alloc_lock->bgc_mark_done ();
26894         }
26895         if (no_more_loop_p)
26896         {
26897             break;
26898         }
26899         o = next_o;
26900     }
26901
26902 #ifdef MULTIPLE_HEAPS
26903     if (concurrent_p)
26904     {
26905         assert (last_object < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)));
26906     }
26907     else
26908 #endif //MULTIPLE_HEAPS
26909     {
26910         last_object = o;
26911     }
26912
26913     dprintf (3,("Last object: %Ix", (size_t)last_object));
26914     last_page = align_write_watch_lower_page (o);
26915 }
26916
26917 // When reset_only_p is TRUE, we should only reset pages that are in range
26918 // because we need to consider the segments or part of segments that were
26919 // allocated out of range all live.
26920 void gc_heap::revisit_written_pages (BOOL concurrent_p, BOOL reset_only_p)
26921 {
26922 #ifdef WRITE_WATCH
26923     if (concurrent_p && !reset_only_p)
26924     {
26925         current_bgc_state = bgc_revisit_soh;
26926     }
26927
26928     size_t total_dirtied_pages = 0;
26929     size_t total_marked_objects = 0;
26930
26931     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26932
26933     PREFIX_ASSUME(seg != NULL);
26934
26935     bool reset_watch_state = !!concurrent_p;
26936     bool is_runtime_suspended = !concurrent_p;
26937     BOOL small_object_segments = TRUE;
26938     int align_const = get_alignment_constant (small_object_segments);
26939
26940     while (1)
26941     {
26942         if (seg == 0)
26943         {
26944             if (small_object_segments)
26945             {
26946                 //switch to large segment
26947                 if (concurrent_p && !reset_only_p)
26948                 {
26949                     current_bgc_state = bgc_revisit_loh;
26950                 }
26951
26952                 if (!reset_only_p)
26953                 {
26954                     dprintf (GTC_LOG, ("h%d: SOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26955                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26956                     concurrent_print_time_delta (concurrent_p ? "CR SOH" : "NR SOH");
26957                     total_dirtied_pages = 0;
26958                     total_marked_objects = 0;
26959                 }
26960
26961                 small_object_segments = FALSE;
26962                 //concurrent_print_time_delta (concurrent_p ? "concurrent marking dirtied pages on SOH" : "nonconcurrent marking dirtied pages on SOH");
26963
26964                 dprintf (3, ("now revisiting large object segments"));
26965                 align_const = get_alignment_constant (small_object_segments);
26966                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
26967
26968                 PREFIX_ASSUME(seg != NULL);
26969
26970                 continue;
26971             }
26972             else
26973             {
26974                 if (reset_only_p)
26975                 {
26976                     dprintf (GTC_LOG, ("h%d: tdp: %Id", heap_number, total_dirtied_pages));
26977                 } 
26978                 else
26979                 {
26980                     dprintf (GTC_LOG, ("h%d: LOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26981                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26982                 }
26983                 break;
26984             }
26985         }
26986         uint8_t* base_address = (uint8_t*)heap_segment_mem (seg);
26987         //we need to truncate to the base of the page because
26988         //some newly allocated could exist beyond heap_segment_allocated
26989         //and if we reset the last page write watch status,
26990         // they wouldn't be guaranteed to be visited -> gc hole.
26991         uintptr_t bcount = array_size;
26992         uint8_t* last_page = 0;
26993         uint8_t* last_object = heap_segment_mem (seg);
26994         uint8_t* high_address = 0;
26995
26996         BOOL skip_seg_p = FALSE;
26997
26998         if (reset_only_p)
26999         {
27000             if ((heap_segment_mem (seg) >= background_saved_lowest_address) ||
27001                 (heap_segment_reserved (seg) <= background_saved_highest_address))
27002             {
27003                 dprintf (3, ("h%d: sseg: %Ix(-%Ix)", heap_number, 
27004                     heap_segment_mem (seg), heap_segment_reserved (seg)));
27005                 skip_seg_p = TRUE;
27006             }
27007         }
27008
27009         if (!skip_seg_p)
27010         {
27011             dprintf (3, ("looking at seg %Ix", (size_t)last_object));
27012
27013             if (reset_only_p)
27014             {
27015                 base_address = max (base_address, background_saved_lowest_address);
27016                 dprintf (3, ("h%d: reset only starting %Ix", heap_number, base_address));
27017             }
27018
27019             dprintf (3, ("h%d: starting: %Ix, seg %Ix-%Ix", heap_number, base_address, 
27020                 heap_segment_mem (seg), heap_segment_reserved (seg)));
27021
27022
27023             while (1)
27024             {
27025                 if (reset_only_p)
27026                 {
27027                     high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
27028                     high_address = min (high_address, background_saved_highest_address);
27029                 }
27030                 else
27031                 {
27032                     high_address = high_page (seg, concurrent_p);
27033                 }
27034
27035                 if ((base_address < high_address) &&
27036                     (bcount >= array_size))
27037                 {
27038                     ptrdiff_t region_size = high_address - base_address;
27039                     dprintf (3, ("h%d: gw: [%Ix(%Id)", heap_number, (size_t)base_address, (size_t)region_size));
27040
27041 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
27042                     // When the runtime is not suspended, it's possible for the table to be resized concurrently with the scan
27043                     // for dirty pages below. Prevent that by synchronizing with grow_brick_card_tables(). When the runtime is
27044                     // suspended, it's ok to scan for dirty pages concurrently from multiple background GC threads for disjoint
27045                     // memory regions.
27046                     if (!is_runtime_suspended)
27047                     {
27048                         enter_spin_lock(&gc_lock);
27049                     }
27050 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
27051
27052                     get_write_watch_for_gc_heap (reset_watch_state, base_address, region_size,
27053                                                  (void**)background_written_addresses,
27054                                                  &bcount, is_runtime_suspended);
27055
27056 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
27057                     if (!is_runtime_suspended)
27058                     {
27059                         leave_spin_lock(&gc_lock);
27060                     }
27061 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
27062
27063                     if (bcount != 0)
27064                     {
27065                         total_dirtied_pages += bcount;
27066
27067                         dprintf (3, ("Found %d pages [%Ix, %Ix[", 
27068                                         bcount, (size_t)base_address, (size_t)high_address));
27069                     }
27070
27071                     if (!reset_only_p)
27072                     {
27073                         for (unsigned i = 0; i < bcount; i++)
27074                         {
27075                             uint8_t* page = (uint8_t*)background_written_addresses[i];
27076                             dprintf (3, ("looking at page %d at %Ix(h: %Ix)", i, 
27077                                 (size_t)page, (size_t)high_address));
27078                             if (page < high_address)
27079                             {
27080                                 //search for marked objects in the page
27081                                 revisit_written_page (page, high_address, concurrent_p,
27082                                                     seg, last_page, last_object,
27083                                                     !small_object_segments,
27084                                                     total_marked_objects);
27085                             }
27086                             else
27087                             {
27088                                 dprintf (3, ("page %d at %Ix is >= %Ix!", i, (size_t)page, (size_t)high_address));
27089                                 assert (!"page shouldn't have exceeded limit");
27090                             }
27091                         }
27092                     }
27093
27094                     if (bcount >= array_size){
27095                         base_address = background_written_addresses [array_size-1] + WRITE_WATCH_UNIT_SIZE;
27096                         bcount = array_size;
27097                     }
27098                 }
27099                 else
27100                 {
27101                     break;
27102                 }
27103             }
27104         }
27105
27106         seg = heap_segment_next_rw (seg);
27107     }
27108
27109 #endif //WRITE_WATCH
27110 }
27111
27112 void gc_heap::background_grow_c_mark_list()
27113 {
27114     assert (c_mark_list_index >= c_mark_list_length);
27115     BOOL should_drain_p = FALSE;
27116     THREAD_FROM_HEAP;
27117 #ifndef MULTIPLE_HEAPS
27118     const int thread = heap_number;
27119 #endif //!MULTIPLE_HEAPS
27120
27121     dprintf (2, ("stack copy buffer overflow"));
27122     uint8_t** new_c_mark_list = 0;
27123     {
27124         FAULT_NOT_FATAL();
27125         if (c_mark_list_length >= (SIZE_T_MAX / (2 * sizeof (uint8_t*))))
27126         {
27127             should_drain_p = TRUE;
27128         }
27129         else
27130         {
27131             new_c_mark_list = new (nothrow) uint8_t*[c_mark_list_length*2];
27132             if (new_c_mark_list == 0)
27133             {
27134                 should_drain_p = TRUE;
27135             }
27136         }
27137     }
27138     if (should_drain_p)
27139
27140     {
27141         dprintf (2, ("No more memory for the stacks copy, draining.."));
27142         //drain the list by marking its elements
27143         background_drain_mark_list (thread);
27144     }
27145     else
27146     {
27147         assert (new_c_mark_list);
27148         memcpy (new_c_mark_list, c_mark_list, c_mark_list_length*sizeof(uint8_t*));
27149         c_mark_list_length = c_mark_list_length*2;
27150         delete c_mark_list;
27151         c_mark_list = new_c_mark_list;
27152     }
27153 }
27154
27155 void gc_heap::background_promote_callback (Object** ppObject, ScanContext* sc,
27156                                   uint32_t flags)
27157 {
27158     UNREFERENCED_PARAMETER(sc);
27159     //in order to save space on the array, mark the object,
27160     //knowing that it will be visited later
27161     assert (settings.concurrent);
27162
27163     THREAD_NUMBER_FROM_CONTEXT;
27164 #ifndef MULTIPLE_HEAPS
27165     const int thread = 0;
27166 #endif //!MULTIPLE_HEAPS
27167
27168     uint8_t* o = (uint8_t*)*ppObject;
27169
27170     if (o == 0)
27171         return;
27172
27173     HEAP_FROM_THREAD;
27174
27175     gc_heap* hp = gc_heap::heap_of (o);
27176
27177     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
27178     {
27179         return;
27180     }
27181
27182 #ifdef INTERIOR_POINTERS
27183     if (flags & GC_CALL_INTERIOR)
27184     {
27185         o = hp->find_object (o, hp->background_saved_lowest_address);
27186         if (o == 0)
27187             return;
27188     }
27189 #endif //INTERIOR_POINTERS
27190
27191 #ifdef FEATURE_CONSERVATIVE_GC
27192     // For conservative GC, a value on stack may point to middle of a free object.
27193     // In this case, we don't need to promote the pointer.
27194     if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
27195     {
27196         return;
27197     }
27198 #endif //FEATURE_CONSERVATIVE_GC
27199
27200 #ifdef _DEBUG
27201     ((CObjectHeader*)o)->Validate();
27202 #endif //_DEBUG
27203
27204     dprintf (3, ("Concurrent Background Promote %Ix", (size_t)o));
27205     if (o && (size (o) > loh_size_threshold))
27206     {
27207         dprintf (3, ("Brc %Ix", (size_t)o));
27208     }
27209
27210     if (hpt->c_mark_list_index >= hpt->c_mark_list_length)
27211     {
27212         hpt->background_grow_c_mark_list();
27213     }
27214     dprintf (3, ("pushing %08x into mark_list", (size_t)o));
27215     hpt->c_mark_list [hpt->c_mark_list_index++] = o;
27216
27217     STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, "    GCHeap::Background Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL);
27218 }
27219
27220 void gc_heap::mark_absorb_new_alloc()
27221 {
27222     fix_allocation_contexts (FALSE);
27223     
27224     gen0_bricks_cleared = FALSE;
27225
27226     clear_gen0_bricks();
27227 }
27228
27229 BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
27230 {
27231     BOOL success = FALSE;
27232     BOOL thread_created = FALSE;
27233     dprintf (2, ("Preparing gc thread"));
27234     gh->bgc_threads_timeout_cs.Enter();
27235     if (!(gh->bgc_thread_running))
27236     {
27237         dprintf (2, ("GC thread not runnning"));
27238         if ((gh->bgc_thread == 0) && create_bgc_thread(gh))
27239         {
27240             success = TRUE;
27241             thread_created = TRUE;
27242         }
27243     }
27244     else
27245     {
27246         dprintf (3, ("GC thread already running"));
27247         success = TRUE;
27248     }
27249     gh->bgc_threads_timeout_cs.Leave();
27250
27251     if(thread_created)
27252         FIRE_EVENT(GCCreateConcurrentThread_V1);
27253
27254     return success;
27255 }
27256
27257 BOOL gc_heap::create_bgc_thread(gc_heap* gh)
27258 {
27259     assert (background_gc_done_event.IsValid());
27260
27261     //dprintf (2, ("Creating BGC thread"));
27262
27263     gh->bgc_thread_running = GCToEEInterface::CreateThread(gh->bgc_thread_stub, gh, true, ".NET Background GC");
27264     return gh->bgc_thread_running;
27265 }
27266
27267 BOOL gc_heap::create_bgc_threads_support (int number_of_heaps)
27268 {
27269     BOOL ret = FALSE;
27270     dprintf (3, ("Creating concurrent GC thread for the first time"));
27271     if (!background_gc_done_event.CreateManualEventNoThrow(TRUE))
27272     {
27273         goto cleanup;
27274     }
27275     if (!bgc_threads_sync_event.CreateManualEventNoThrow(FALSE))
27276     {
27277         goto cleanup;
27278     }
27279     if (!ee_proceed_event.CreateAutoEventNoThrow(FALSE))
27280     {
27281         goto cleanup;
27282     }
27283     if (!bgc_start_event.CreateManualEventNoThrow(FALSE))
27284     {
27285         goto cleanup;
27286     }
27287
27288 #ifdef MULTIPLE_HEAPS
27289     bgc_t_join.init (number_of_heaps, join_flavor_bgc);
27290 #else
27291     UNREFERENCED_PARAMETER(number_of_heaps);
27292 #endif //MULTIPLE_HEAPS
27293
27294     ret = TRUE;
27295
27296 cleanup:
27297
27298     if (!ret)
27299     {
27300         if (background_gc_done_event.IsValid())
27301         {
27302             background_gc_done_event.CloseEvent();
27303         }
27304         if (bgc_threads_sync_event.IsValid())
27305         {
27306             bgc_threads_sync_event.CloseEvent();
27307         }
27308         if (ee_proceed_event.IsValid())
27309         {
27310             ee_proceed_event.CloseEvent();
27311         }
27312         if (bgc_start_event.IsValid())
27313         {
27314             bgc_start_event.CloseEvent();
27315         }
27316     }
27317
27318     return ret;
27319 }
27320
27321 BOOL gc_heap::create_bgc_thread_support()
27322 {
27323     BOOL ret = FALSE;
27324     uint8_t** parr;
27325     
27326     if (!gc_lh_block_event.CreateManualEventNoThrow(FALSE))
27327     {
27328         goto cleanup;
27329     }
27330
27331     //needs to have room for enough smallest objects fitting on a page
27332     parr = new (nothrow) uint8_t*[1 + OS_PAGE_SIZE / MIN_OBJECT_SIZE];
27333     if (!parr)
27334     {
27335         goto cleanup;
27336     }
27337
27338     make_c_mark_list (parr);
27339
27340     ret = TRUE;
27341
27342 cleanup:
27343
27344     if (!ret)
27345     {
27346         if (gc_lh_block_event.IsValid())
27347         {
27348             gc_lh_block_event.CloseEvent();
27349         }
27350     }
27351
27352     return ret;
27353 }
27354
27355 int gc_heap::check_for_ephemeral_alloc()
27356 {
27357     int gen = ((settings.reason == reason_oos_soh) ? (max_generation - 1) : -1);
27358
27359     if (gen == -1)
27360     {
27361 #ifdef MULTIPLE_HEAPS
27362         for (int heap_index = 0; heap_index < n_heaps; heap_index++)
27363 #endif //MULTIPLE_HEAPS
27364         {
27365             for (int i = 0; i <= (max_generation - 1); i++)
27366             {
27367 #ifdef MULTIPLE_HEAPS
27368                 if (g_heaps[heap_index]->get_new_allocation (i) <= 0)
27369 #else
27370                 if (get_new_allocation (i) <= 0)
27371 #endif //MULTIPLE_HEAPS
27372                 {
27373                     gen = max (gen, i);
27374                 }
27375                 else
27376                     break;
27377             }
27378         }
27379     }
27380
27381     return gen;
27382 }
27383
27384 // Wait for gc to finish sequential part
27385 void gc_heap::wait_to_proceed()
27386 {
27387     assert (background_gc_done_event.IsValid());
27388     assert (bgc_start_event.IsValid());
27389
27390     user_thread_wait(&ee_proceed_event, FALSE);
27391 }
27392
27393 // Start a new concurrent gc
27394 void gc_heap::start_c_gc()
27395 {
27396     assert (background_gc_done_event.IsValid());
27397     assert (bgc_start_event.IsValid());
27398
27399 //Need to make sure that the gc thread is in the right place.
27400     background_gc_done_event.Wait(INFINITE, FALSE);
27401     background_gc_done_event.Reset();
27402     bgc_start_event.Set();
27403 }
27404
27405 void gc_heap::do_background_gc()
27406 {
27407     dprintf (2, ("starting a BGC"));
27408 #ifdef MULTIPLE_HEAPS
27409     for (int i = 0; i < n_heaps; i++)
27410     {
27411         g_heaps[i]->init_background_gc();
27412     }
27413 #else
27414     init_background_gc();
27415 #endif //MULTIPLE_HEAPS
27416     //start the background gc
27417     start_c_gc ();
27418
27419     //wait until we get restarted by the BGC.
27420     wait_to_proceed();
27421 }
27422
27423 void gc_heap::kill_gc_thread()
27424 {
27425     //assert (settings.concurrent == FALSE);
27426
27427     // We are doing a two-stage shutdown now.
27428     // In the first stage, we do minimum work, and call ExitProcess at the end.
27429     // In the secodn stage, we have the Loader lock and only one thread is
27430     // alive.  Hence we do not need to kill gc thread.
27431     background_gc_done_event.CloseEvent();
27432     gc_lh_block_event.CloseEvent();
27433     bgc_start_event.CloseEvent();
27434     bgc_threads_timeout_cs.Destroy();
27435     bgc_thread = 0;
27436     recursive_gc_sync::shutdown();
27437 }
27438
27439 void gc_heap::bgc_thread_function()
27440 {
27441     assert (background_gc_done_event.IsValid());
27442     assert (bgc_start_event.IsValid());
27443
27444     dprintf (3, ("gc_thread thread starting..."));
27445
27446     BOOL do_exit = FALSE;
27447
27448     bool cooperative_mode = true;
27449     bgc_thread_id.SetToCurrentThread();
27450     dprintf (1, ("bgc_thread_id is set to %x", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging()));
27451     while (1)
27452     {
27453         // Wait for work to do...
27454         dprintf (3, ("bgc thread: waiting..."));
27455
27456         cooperative_mode = enable_preemptive ();
27457         //current_thread->m_fPreemptiveGCDisabled = 0;
27458
27459         uint32_t result = bgc_start_event.Wait(
27460 #ifdef _DEBUG
27461 #ifdef MULTIPLE_HEAPS
27462                                              INFINITE,
27463 #else
27464                                              2000,
27465 #endif //MULTIPLE_HEAPS
27466 #else //_DEBUG
27467 #ifdef MULTIPLE_HEAPS
27468                                              INFINITE,
27469 #else
27470                                              20000,
27471 #endif //MULTIPLE_HEAPS
27472 #endif //_DEBUG
27473             FALSE);
27474         dprintf (2, ("gc thread: finished waiting"));
27475
27476         // not calling disable_preemptive here 'cause we 
27477         // can't wait for GC complete here - RestartEE will be called 
27478         // when we've done the init work.
27479
27480         if (result == WAIT_TIMEOUT)
27481         {
27482             // Should join the bgc threads and terminate all of them
27483             // at once.
27484             dprintf (1, ("GC thread timeout"));
27485             bgc_threads_timeout_cs.Enter();
27486             if (!keep_bgc_threads_p)
27487             {
27488                 dprintf (2, ("GC thread exiting"));
27489                 bgc_thread_running = FALSE;
27490                 bgc_thread = 0;
27491                 bgc_thread_id.Clear();
27492                 do_exit = TRUE;
27493             }
27494             bgc_threads_timeout_cs.Leave();
27495             if (do_exit)
27496                 break;
27497             else
27498             {
27499                 dprintf (3, ("GC thread needed, not exiting"));
27500                 continue;
27501             }
27502         }
27503         // if we signal the thread with no concurrent work to do -> exit
27504         if (!settings.concurrent)
27505         {
27506             dprintf (3, ("no concurrent GC needed, exiting"));
27507             break;
27508         }
27509 #ifdef TRACE_GC
27510         //trace_gc = TRUE;
27511 #endif //TRACE_GC
27512         recursive_gc_sync::begin_background();
27513         dprintf (2, ("beginning of bgc: gen2 FL: %d, FO: %d, frag: %d", 
27514             generation_free_list_space (generation_of (max_generation)),
27515             generation_free_obj_space (generation_of (max_generation)),
27516             dd_fragmentation (dynamic_data_of (max_generation))));
27517
27518         gc1();
27519
27520         current_bgc_state = bgc_not_in_process;
27521
27522 #ifdef TRACE_GC
27523         //trace_gc = FALSE;
27524 #endif //TRACE_GC
27525
27526         enable_preemptive ();
27527 #ifdef MULTIPLE_HEAPS
27528         bgc_t_join.join(this, gc_join_done);
27529         if (bgc_t_join.joined())
27530 #endif //MULTIPLE_HEAPS
27531         {
27532             enter_spin_lock (&gc_lock);
27533             dprintf (SPINLOCK_LOG, ("bgc Egc"));
27534             
27535             bgc_start_event.Reset();
27536             do_post_gc();
27537 #ifdef MULTIPLE_HEAPS
27538             for (int gen = max_generation; gen <= (max_generation + 1); gen++)
27539             {
27540                 size_t desired_per_heap = 0;
27541                 size_t total_desired = 0;
27542                 gc_heap* hp = 0;
27543                 dynamic_data* dd;
27544                 for (int i = 0; i < n_heaps; i++)
27545                 {
27546                     hp = g_heaps[i];
27547                     dd = hp->dynamic_data_of (gen);
27548                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
27549                     if (temp_total_desired < total_desired)
27550                     {
27551                         // we overflowed.
27552                         total_desired = (size_t)MAX_PTR;
27553                         break;
27554                     }
27555                     total_desired = temp_total_desired;
27556                 }
27557
27558                 desired_per_heap = Align ((total_desired/n_heaps), get_alignment_constant (FALSE));
27559
27560                 for (int i = 0; i < n_heaps; i++)
27561                 {
27562                     hp = gc_heap::g_heaps[i];
27563                     dd = hp->dynamic_data_of (gen);
27564                     dd_desired_allocation (dd) = desired_per_heap;
27565                     dd_gc_new_allocation (dd) = desired_per_heap;
27566                     dd_new_allocation (dd) = desired_per_heap;
27567                 }
27568             }
27569 #endif //MULTIPLE_HEAPS
27570 #ifdef MULTIPLE_HEAPS
27571             fire_pevents();
27572 #endif //MULTIPLE_HEAPS
27573
27574             c_write (settings.concurrent, FALSE);
27575             recursive_gc_sync::end_background();
27576             keep_bgc_threads_p = FALSE;
27577             background_gc_done_event.Set();
27578
27579             dprintf (SPINLOCK_LOG, ("bgc Lgc"));
27580             leave_spin_lock (&gc_lock);
27581 #ifdef MULTIPLE_HEAPS
27582             dprintf(1, ("End of BGC - starting all BGC threads"));
27583             bgc_t_join.restart();
27584 #endif //MULTIPLE_HEAPS
27585         }
27586         // We can't disable preempt here because there might've been a GC already
27587         // started and decided to do a BGC and waiting for a BGC thread to restart 
27588         // vm. That GC will be waiting in wait_to_proceed and we are waiting for it
27589         // to restart the VM so we deadlock.
27590         //gc_heap::disable_preemptive (true);
27591     }
27592
27593     FIRE_EVENT(GCTerminateConcurrentThread_V1);
27594
27595     dprintf (3, ("bgc_thread thread exiting"));
27596     return;
27597 }
27598
27599 #endif //BACKGROUND_GC
27600
27601 //Clear the cards [start_card, end_card[
27602 void gc_heap::clear_cards (size_t start_card, size_t end_card)
27603 {
27604     if (start_card < end_card)
27605     {
27606         size_t start_word = card_word (start_card);
27607         size_t end_word = card_word (end_card);
27608         if (start_word < end_word)
27609         {
27610             // Figure out the bit positions of the cards within their words
27611             unsigned bits = card_bit (start_card);
27612             card_table [start_word] &= lowbits (~0, bits);
27613             for (size_t i = start_word+1; i < end_word; i++)
27614                 card_table [i] = 0;
27615             bits = card_bit (end_card);
27616             // Don't write beyond end_card (and possibly uncommitted card table space).
27617             if (bits != 0)
27618             {
27619                 card_table [end_word] &= highbits (~0, bits);
27620             }
27621         }
27622         else
27623         {
27624             // If the start and end cards are in the same word, just clear the appropriate card
27625             // bits in that word.
27626             card_table [start_word] &= (lowbits (~0, card_bit (start_card)) |
27627                                         highbits (~0, card_bit (end_card)));
27628         }
27629 #ifdef VERYSLOWDEBUG
27630         size_t  card = start_card;
27631         while (card < end_card)
27632         {
27633             assert (! (card_set_p (card)));
27634             card++;
27635         }
27636 #endif //VERYSLOWDEBUG
27637         dprintf (3,("Cleared cards [%Ix:%Ix, %Ix:%Ix[",
27638                   start_card, (size_t)card_address (start_card),
27639                   end_card, (size_t)card_address (end_card)));
27640     }
27641 }
27642
27643 void gc_heap::clear_card_for_addresses (uint8_t* start_address, uint8_t* end_address)
27644 {
27645     size_t   start_card = card_of (align_on_card (start_address));
27646     size_t   end_card = card_of (align_lower_card (end_address));
27647     clear_cards (start_card, end_card);
27648 }
27649
27650 // copy [srccard, ...[ to [dst_card, end_card[
27651 // This will set the same bit twice. Can be optimized.
27652 inline
27653 void gc_heap::copy_cards (size_t dst_card,
27654                           size_t src_card,
27655                           size_t end_card, 
27656                           BOOL nextp)
27657 {
27658     // If the range is empty, this function is a no-op - with the subtlety that
27659     // either of the accesses card_table[srcwrd] or card_table[dstwrd] could be
27660     // outside the committed region.  To avoid the access, leave early.
27661     if (!(dst_card < end_card))
27662         return;
27663
27664     unsigned int srcbit = card_bit (src_card);
27665     unsigned int dstbit = card_bit (dst_card);
27666     size_t srcwrd = card_word (src_card);
27667     size_t dstwrd = card_word (dst_card);
27668     unsigned int srctmp = card_table[srcwrd];
27669     unsigned int dsttmp = card_table[dstwrd];
27670
27671     for (size_t card = dst_card; card < end_card; card++)
27672     {
27673         if (srctmp & (1 << srcbit))
27674             dsttmp |= 1 << dstbit;
27675         else
27676             dsttmp &= ~(1 << dstbit);
27677         if (!(++srcbit % 32))
27678         {
27679             srctmp = card_table[++srcwrd];
27680             srcbit = 0;
27681         }
27682
27683         if (nextp)
27684         {
27685             if (srctmp & (1 << srcbit))
27686                 dsttmp |= 1 << dstbit;
27687         }
27688
27689         if (!(++dstbit % 32))
27690         {
27691             card_table[dstwrd] = dsttmp;
27692
27693 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27694             if (dsttmp != 0)
27695             {
27696                 card_bundle_set(cardw_card_bundle(dstwrd));
27697             }
27698 #endif
27699
27700             dstwrd++;
27701             dsttmp = card_table[dstwrd];
27702             dstbit = 0;
27703         }
27704     }
27705
27706     card_table[dstwrd] = dsttmp;
27707
27708 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27709     if (dsttmp != 0)
27710     {
27711         card_bundle_set(cardw_card_bundle(dstwrd));
27712     }
27713 #endif
27714 }
27715
27716 void gc_heap::copy_cards_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27717 {
27718     ptrdiff_t relocation_distance = src - dest;
27719     size_t start_dest_card = card_of (align_on_card (dest));
27720     size_t end_dest_card = card_of (dest + len - 1);
27721     size_t dest_card = start_dest_card;
27722     size_t src_card = card_of (card_address (dest_card)+relocation_distance);
27723     dprintf (3,("Copying cards [%Ix:%Ix->%Ix:%Ix, ",
27724                  src_card, (size_t)src, dest_card, (size_t)dest));
27725     dprintf (3,(" %Ix->%Ix:%Ix[",
27726               (size_t)src+len, end_dest_card, (size_t)dest+len));
27727
27728     dprintf (3, ("dest: %Ix, src: %Ix, len: %Ix, reloc: %Ix, align_on_card(dest) is %Ix",
27729         dest, src, len, relocation_distance, (align_on_card (dest))));
27730
27731     dprintf (3, ("start_dest_card: %Ix (address: %Ix), end_dest_card: %Ix(addr: %Ix), card_of (dest): %Ix",
27732         start_dest_card, card_address (start_dest_card), end_dest_card, card_address (end_dest_card), card_of (dest)));
27733
27734     //First card has two boundaries
27735     if (start_dest_card != card_of (dest))
27736     {
27737         if ((card_of (card_address (start_dest_card) + relocation_distance) <= card_of (src + len - 1))&&
27738             card_set_p (card_of (card_address (start_dest_card) + relocation_distance)))
27739         {
27740             dprintf (3, ("card_address (start_dest_card) + reloc is %Ix, card: %Ix(set), src+len-1: %Ix, card: %Ix",
27741                     (card_address (start_dest_card) + relocation_distance),
27742                     card_of (card_address (start_dest_card) + relocation_distance),
27743                     (src + len - 1),
27744                     card_of (src + len - 1)));
27745
27746             dprintf (3, ("setting card: %Ix", card_of (dest)));
27747             set_card (card_of (dest));
27748         }
27749     }
27750
27751     if (card_set_p (card_of (src)))
27752         set_card (card_of (dest));
27753
27754
27755     copy_cards (dest_card, src_card, end_dest_card,
27756                 ((dest - align_lower_card (dest)) != (src - align_lower_card (src))));
27757
27758     //Last card has two boundaries.
27759     if ((card_of (card_address (end_dest_card) + relocation_distance) >= card_of (src)) &&
27760         card_set_p (card_of (card_address (end_dest_card) + relocation_distance)))
27761     {
27762         dprintf (3, ("card_address (end_dest_card) + reloc is %Ix, card: %Ix(set), src: %Ix, card: %Ix",
27763                 (card_address (end_dest_card) + relocation_distance),
27764                 card_of (card_address (end_dest_card) + relocation_distance),
27765                 src,
27766                 card_of (src)));
27767
27768         dprintf (3, ("setting card: %Ix", end_dest_card));
27769         set_card (end_dest_card);
27770     }
27771
27772     if (card_set_p (card_of (src + len - 1)))
27773         set_card (end_dest_card);
27774
27775 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27776     card_bundles_set(cardw_card_bundle(card_word(card_of(dest))), cardw_card_bundle(align_cardw_on_bundle(card_word(end_dest_card))));
27777 #endif
27778 }
27779
27780 #ifdef BACKGROUND_GC
27781 // this does not need the Interlocked version of mark_array_set_marked.
27782 void gc_heap::copy_mark_bits_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27783 {
27784     dprintf (3, ("Copying mark_bits for addresses [%Ix->%Ix, %Ix->%Ix[",
27785                  (size_t)src, (size_t)dest,
27786                  (size_t)src+len, (size_t)dest+len));
27787
27788     uint8_t* src_o = src;
27789     uint8_t* dest_o;
27790     uint8_t* src_end = src + len;
27791     int align_const = get_alignment_constant (TRUE);
27792     ptrdiff_t reloc = dest - src;
27793
27794     while (src_o < src_end)
27795     {
27796         uint8_t*  next_o = src_o + Align (size (src_o), align_const);
27797
27798         if (background_object_marked (src_o, TRUE))
27799         {
27800             dest_o = src_o + reloc;
27801
27802             //if (background_object_marked (dest_o, FALSE))
27803             //{
27804             //    dprintf (3, ("*%Ix shouldn't have already been marked!", (size_t)(dest_o)));
27805             //    FATAL_GC_ERROR();
27806             //}
27807
27808             background_mark (dest_o, 
27809                              background_saved_lowest_address, 
27810                              background_saved_highest_address);
27811             dprintf (3, ("bc*%Ix*bc, b*%Ix*b", (size_t)src_o, (size_t)(dest_o)));
27812         }
27813
27814         src_o = next_o;
27815     }
27816 }
27817 #endif //BACKGROUND_GC
27818
27819 void gc_heap::fix_brick_to_highest (uint8_t* o, uint8_t* next_o)
27820 {
27821     size_t new_current_brick = brick_of (o);
27822     set_brick (new_current_brick,
27823                (o - brick_address (new_current_brick)));
27824     size_t b = 1 + new_current_brick;
27825     size_t limit = brick_of (next_o);
27826     //dprintf(3,(" fixing brick %Ix to point to object %Ix, till %Ix(%Ix)",
27827     dprintf(3,("b:%Ix->%Ix-%Ix", 
27828                new_current_brick, (size_t)o, (size_t)next_o));
27829     while (b < limit)
27830     {
27831         set_brick (b,(new_current_brick - b));
27832         b++;
27833     }
27834 }
27835
27836 // start can not be >= heap_segment_allocated for the segment.
27837 uint8_t* gc_heap::find_first_object (uint8_t* start, uint8_t* first_object)
27838 {
27839     size_t brick = brick_of (start);
27840     uint8_t* o = 0;
27841     //last_object == null -> no search shortcut needed
27842     if ((brick == brick_of (first_object) || (start <= first_object)))
27843     {
27844         o = first_object;
27845     }
27846     else
27847     {
27848         ptrdiff_t  min_brick = (ptrdiff_t)brick_of (first_object);
27849         ptrdiff_t  prev_brick = (ptrdiff_t)brick - 1;
27850         int         brick_entry = 0;
27851         while (1)
27852         {
27853             if (prev_brick < min_brick)
27854             {
27855                 break;
27856             }
27857             if ((brick_entry = get_brick_entry(prev_brick)) >= 0)
27858             {
27859                 break;
27860             }
27861             assert (! ((brick_entry == 0)));
27862             prev_brick = (brick_entry + prev_brick);
27863
27864         }
27865         o = ((prev_brick < min_brick) ? first_object :
27866                       brick_address (prev_brick) + brick_entry - 1);
27867         assert (o <= start);
27868     }
27869
27870     assert (Align (size (o)) >= Align (min_obj_size));
27871     uint8_t*  next_o = o + Align (size (o));
27872     size_t curr_cl = (size_t)next_o / brick_size;
27873     size_t min_cl = (size_t)first_object / brick_size;
27874
27875     //dprintf (3,( "Looking for intersection with %Ix from %Ix", (size_t)start, (size_t)o));
27876 #ifdef TRACE_GC
27877     unsigned int n_o = 1;
27878 #endif //TRACE_GC
27879
27880     uint8_t* next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27881
27882     while (next_o <= start)
27883     {
27884         do
27885         {
27886 #ifdef TRACE_GC
27887             n_o++;
27888 #endif //TRACE_GC
27889             o = next_o;
27890             assert (Align (size (o)) >= Align (min_obj_size));
27891             next_o = o + Align (size (o));
27892             Prefetch (next_o);
27893         }while (next_o < next_b);
27894
27895         if (((size_t)next_o / brick_size) != curr_cl)
27896         {
27897             if (curr_cl >= min_cl)
27898             {
27899                 fix_brick_to_highest (o, next_o);
27900             }
27901             curr_cl = (size_t) next_o / brick_size;
27902         }
27903         next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27904     }
27905
27906     size_t bo = brick_of (o);
27907     //dprintf (3, ("Looked at %Id objects, fixing brick [%Ix-[%Ix", 
27908     dprintf (3, ("%Id o, [%Ix-[%Ix", 
27909         n_o, bo, brick));
27910     if (bo < brick)
27911     {
27912         set_brick (bo, (o - brick_address(bo)));
27913         size_t b = 1 + bo;
27914         int x = -1;
27915         while (b < brick)
27916         {
27917             set_brick (b,x--);
27918             b++;
27919         }
27920     }
27921
27922     return o;
27923 }
27924
27925 #ifdef CARD_BUNDLE
27926
27927 // Find the first non-zero card word between cardw and cardw_end.
27928 // The index of the word we find is returned in cardw.
27929 BOOL gc_heap::find_card_dword (size_t& cardw, size_t cardw_end)
27930 {
27931     dprintf (3, ("gc: %d, find_card_dword cardw: %Ix, cardw_end: %Ix",
27932                  dd_collection_count (dynamic_data_of (0)), cardw, cardw_end));
27933
27934     if (card_bundles_enabled())
27935     {
27936         size_t cardb = cardw_card_bundle (cardw);
27937         size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (cardw_end));
27938         while (1)
27939         {
27940             // Find a non-zero bundle
27941             while ((cardb < end_cardb) && (card_bundle_set_p (cardb) == 0))
27942             {
27943                 cardb++;
27944             }
27945             if (cardb == end_cardb)
27946                 return FALSE;
27947
27948             uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb),cardw)];
27949             uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1),cardw_end)];
27950             while ((card_word < card_word_end) && !(*card_word))
27951             {
27952                 card_word++;
27953             }
27954
27955             if (card_word != card_word_end)
27956             {
27957                 cardw = (card_word - &card_table[0]);
27958                 return TRUE;
27959             }
27960             else if ((cardw <= card_bundle_cardw (cardb)) &&
27961                      (card_word == &card_table [card_bundle_cardw (cardb+1)]))
27962             {
27963                 // a whole bundle was explored and is empty
27964                 dprintf  (3, ("gc: %d, find_card_dword clear bundle: %Ix cardw:[%Ix,%Ix[",
27965                         dd_collection_count (dynamic_data_of (0)), 
27966                         cardb, card_bundle_cardw (cardb),
27967                         card_bundle_cardw (cardb+1)));
27968                 card_bundle_clear (cardb);
27969             }
27970
27971             cardb++;
27972         }
27973     }
27974     else
27975     {
27976         uint32_t* card_word = &card_table[cardw];
27977         uint32_t* card_word_end = &card_table [cardw_end];
27978
27979         while (card_word < card_word_end)
27980         {
27981             if ((*card_word) != 0)
27982             {
27983                 cardw = (card_word - &card_table [0]);
27984                 return TRUE;
27985             }
27986
27987             card_word++;
27988         }
27989         return FALSE;
27990
27991     }
27992
27993 }
27994
27995 #endif //CARD_BUNDLE
27996
27997 // Find cards that are set between two points in a card table.
27998 // Parameters
27999 //     card_table    : The card table.
28000 //     card          : [in/out] As input, the card to start searching from.
28001 //                              As output, the first card that's set.
28002 //     card_word_end : The card word at which to stop looking.
28003 //     end_card      : [out] The last card which is set.
28004 BOOL gc_heap::find_card(uint32_t* card_table,
28005                         size_t&   card,
28006                         size_t    card_word_end,
28007                         size_t&   end_card)
28008 {
28009     uint32_t* last_card_word;
28010     uint32_t card_word_value;
28011     uint32_t bit_position;
28012     
28013     // Find the first card which is set
28014     last_card_word = &card_table [card_word (card)];
28015     bit_position = card_bit (card);
28016     card_word_value = (*last_card_word) >> bit_position;
28017     if (!card_word_value)
28018     {
28019         bit_position = 0;
28020 #ifdef CARD_BUNDLE
28021         // Using the card bundle, go through the remaining card words between here and 
28022         // card_word_end until we find one that is non-zero.
28023         size_t lcw = card_word(card) + 1;
28024         if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE)
28025         {
28026             return FALSE;
28027         }
28028         else
28029         {
28030             last_card_word = &card_table [lcw];
28031             card_word_value = *last_card_word;
28032         }
28033
28034 #else //CARD_BUNDLE
28035         // Go through the remaining card words between here and card_word_end until we find
28036         // one that is non-zero.
28037         do
28038         {
28039             ++last_card_word;
28040         }
28041
28042         while ((last_card_word < &card_table [card_word_end]) && !(*last_card_word));
28043         if (last_card_word < &card_table [card_word_end])
28044         {
28045             card_word_value = *last_card_word;
28046         }
28047         else
28048         {
28049             // We failed to find any non-zero card words before we got to card_word_end
28050             return FALSE;
28051         }
28052 #endif //CARD_BUNDLE
28053     }
28054
28055
28056     // Look for the lowest bit set
28057     if (card_word_value)
28058     {
28059         while (!(card_word_value & 1))
28060         {
28061             bit_position++;
28062             card_word_value = card_word_value / 2;
28063         }
28064     }
28065     
28066     // card is the card word index * card size + the bit index within the card
28067     card = (last_card_word - &card_table[0]) * card_word_width + bit_position;
28068
28069     do
28070     {
28071         // Keep going until we get to an un-set card.
28072         bit_position++;
28073         card_word_value = card_word_value / 2;
28074
28075         // If we reach the end of the card word and haven't hit a 0 yet, start going
28076         // card word by card word until we get to one that's not fully set (0xFFFF...)
28077         // or we reach card_word_end.
28078         if ((bit_position == card_word_width) && (last_card_word < &card_table [card_word_end]))
28079         {
28080             do
28081             {
28082                 card_word_value = *(++last_card_word);
28083             } while ((last_card_word < &card_table [card_word_end]) &&
28084
28085 #ifdef _MSC_VER
28086                      (card_word_value == (1 << card_word_width)-1)
28087 #else
28088                      // if left shift count >= width of type,
28089                      // gcc reports error.
28090                      (card_word_value == ~0u)
28091 #endif // _MSC_VER
28092                 );
28093             bit_position = 0;
28094         }
28095     } while (card_word_value & 1);
28096
28097     end_card = (last_card_word - &card_table [0])* card_word_width + bit_position;
28098     
28099     //dprintf (3, ("find_card: [%Ix, %Ix[ set", card, end_card));
28100     dprintf (3, ("fc: [%Ix, %Ix[", card, end_card));
28101     return TRUE;
28102 }
28103
28104
28105     //because of heap expansion, computing end is complicated.
28106 uint8_t* compute_next_end (heap_segment* seg, uint8_t* low)
28107 {
28108     if ((low >=  heap_segment_mem (seg)) &&
28109         (low < heap_segment_allocated (seg)))
28110         return low;
28111     else
28112         return heap_segment_allocated (seg);
28113 }
28114
28115 uint8_t*
28116 gc_heap::compute_next_boundary (uint8_t* low, int gen_number,
28117                                 BOOL relocating)
28118 {
28119     UNREFERENCED_PARAMETER(low);
28120
28121     //when relocating, the fault line is the plan start of the younger
28122     //generation because the generation is promoted.
28123     if (relocating && (gen_number == (settings.condemned_generation + 1)))
28124     {
28125         generation* gen = generation_of (gen_number - 1);
28126         uint8_t* gen_alloc = generation_plan_allocation_start (gen);
28127         assert (gen_alloc);
28128         return gen_alloc;
28129     }
28130     else
28131     {
28132         assert (gen_number > settings.condemned_generation);
28133         return generation_allocation_start (generation_of (gen_number - 1 ));
28134     }
28135
28136 }
28137
28138 inline void
28139 gc_heap::keep_card_live (uint8_t* o, size_t& n_gen,
28140                          size_t& cg_pointers_found)
28141 {
28142     THREAD_FROM_HEAP;
28143     if ((gc_low <= o) && (gc_high > o))
28144     {
28145         n_gen++;
28146     }
28147 #ifdef MULTIPLE_HEAPS
28148     else if (o)
28149     {
28150         gc_heap* hp = heap_of (o);
28151         if (hp != this)
28152         {
28153             if ((hp->gc_low <= o) &&
28154                 (hp->gc_high > o))
28155             {
28156                 n_gen++;
28157             }
28158         }
28159     }
28160 #endif //MULTIPLE_HEAPS
28161     cg_pointers_found ++;
28162     dprintf (4, ("keep card live for %Ix", o));
28163 }
28164
28165 inline void
28166 gc_heap::mark_through_cards_helper (uint8_t** poo, size_t& n_gen,
28167                                     size_t& cg_pointers_found,
28168                                     card_fn fn, uint8_t* nhigh,
28169                                     uint8_t* next_boundary)
28170 {
28171     THREAD_FROM_HEAP;
28172     if ((gc_low <= *poo) && (gc_high > *poo))
28173     {
28174         n_gen++;
28175         call_fn(fn) (poo THREAD_NUMBER_ARG);
28176     }
28177 #ifdef MULTIPLE_HEAPS
28178     else if (*poo)
28179     {
28180         gc_heap* hp = heap_of_gc (*poo);
28181         if (hp != this)
28182         {
28183             if ((hp->gc_low <= *poo) &&
28184                 (hp->gc_high > *poo))
28185             {
28186                 n_gen++;
28187                 call_fn(fn) (poo THREAD_NUMBER_ARG);
28188             }
28189             if ((fn == &gc_heap::relocate_address) ||
28190                 ((hp->ephemeral_low <= *poo) &&
28191                  (hp->ephemeral_high > *poo)))
28192             {
28193                 cg_pointers_found++;
28194             }
28195         }
28196     }
28197 #endif //MULTIPLE_HEAPS
28198     if ((next_boundary <= *poo) && (nhigh > *poo))
28199     {
28200         cg_pointers_found ++;
28201         dprintf (4, ("cg pointer %Ix found, %Id so far",
28202                      (size_t)*poo, cg_pointers_found ));
28203
28204     }
28205 }
28206
28207 BOOL gc_heap::card_transition (uint8_t* po, uint8_t* end, size_t card_word_end,
28208                                size_t& cg_pointers_found, 
28209                                size_t& n_eph, size_t& n_card_set,
28210                                size_t& card, size_t& end_card,
28211                                BOOL& foundp, uint8_t*& start_address,
28212                                uint8_t*& limit, size_t& n_cards_cleared)
28213 {
28214     dprintf (3, ("pointer %Ix past card %Ix", (size_t)po, (size_t)card));
28215     dprintf (3, ("ct: %Id cg", cg_pointers_found));
28216     BOOL passed_end_card_p = FALSE;
28217     foundp = FALSE;
28218
28219     if (cg_pointers_found == 0)
28220     {
28221         //dprintf(3,(" Clearing cards [%Ix, %Ix[ ",
28222         dprintf(3,(" CC [%Ix, %Ix[ ",
28223                 (size_t)card_address(card), (size_t)po));
28224         clear_cards (card, card_of(po));
28225         n_card_set -= (card_of (po) - card);
28226         n_cards_cleared += (card_of (po) - card);
28227
28228     }
28229     n_eph +=cg_pointers_found;
28230     cg_pointers_found = 0;
28231     card = card_of (po);
28232     if (card >= end_card)
28233     {
28234         passed_end_card_p = TRUE;
28235         dprintf (3, ("card %Ix exceeding end_card %Ix",
28236                     (size_t)card, (size_t)end_card));
28237         foundp = find_card (card_table, card, card_word_end, end_card);
28238         if (foundp)
28239         {
28240             n_card_set+= end_card - card;
28241             start_address = card_address (card);
28242             dprintf (3, ("NewC: %Ix, start: %Ix, end: %Ix",
28243                         (size_t)card, (size_t)start_address,
28244                         (size_t)card_address (end_card)));
28245         }
28246         limit = min (end, card_address (end_card));
28247
28248         assert (!((limit == card_address (end_card))&&
28249                 card_set_p (end_card)));
28250     }
28251
28252     return passed_end_card_p;
28253 }
28254
28255 void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating)
28256 {
28257 #ifdef BACKGROUND_GC
28258     dprintf (3, ("current_sweep_pos is %Ix, saved_sweep_ephemeral_seg is %Ix(%Ix)",
28259                  current_sweep_pos, saved_sweep_ephemeral_seg, saved_sweep_ephemeral_start));
28260
28261     heap_segment* soh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
28262     PREFIX_ASSUME(soh_seg != NULL);
28263
28264     while (soh_seg)
28265     {
28266         dprintf (3, ("seg %Ix, bgc_alloc: %Ix, alloc: %Ix", 
28267             soh_seg, 
28268             heap_segment_background_allocated (soh_seg),
28269             heap_segment_allocated (soh_seg)));
28270
28271         soh_seg = heap_segment_next_rw (soh_seg);
28272     }
28273 #endif //BACKGROUND_GC
28274
28275     uint8_t* low = gc_low;
28276     uint8_t* high = gc_high;
28277     size_t end_card = 0;
28278
28279     generation*   oldest_gen        = generation_of (max_generation);
28280     int           curr_gen_number   = max_generation;
28281     uint8_t*      gen_boundary      = generation_allocation_start(generation_of(curr_gen_number - 1));
28282     uint8_t*      next_boundary     = compute_next_boundary(gc_low, curr_gen_number, relocating);
28283     
28284     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
28285     PREFIX_ASSUME(seg != NULL);
28286
28287     uint8_t*      beg               = generation_allocation_start (oldest_gen);
28288     uint8_t*      end               = compute_next_end (seg, low);
28289     uint8_t*      last_object       = beg;
28290
28291     size_t  cg_pointers_found = 0;
28292
28293     size_t  card_word_end = (card_of (align_on_card_word (end)) / card_word_width);
28294
28295     size_t        n_eph             = 0;
28296     size_t        n_gen             = 0;
28297     size_t        n_card_set        = 0;
28298     uint8_t*      nhigh             = (relocating ?
28299                                        heap_segment_plan_allocated (ephemeral_heap_segment) : high);
28300
28301     BOOL          foundp            = FALSE;
28302     uint8_t*      start_address     = 0;
28303     uint8_t*      limit             = 0;
28304     size_t        card              = card_of (beg);
28305 #ifdef BACKGROUND_GC
28306     BOOL consider_bgc_mark_p        = FALSE;
28307     BOOL check_current_sweep_p      = FALSE;
28308     BOOL check_saved_sweep_p        = FALSE;
28309     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
28310 #endif //BACKGROUND_GC
28311
28312     dprintf(3, ("CMs: %Ix->%Ix", (size_t)beg, (size_t)end));
28313     size_t total_cards_cleared = 0;
28314
28315     while (1)
28316     {
28317         if (card_of(last_object) > card)
28318         {
28319             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
28320             if (cg_pointers_found == 0)
28321             {
28322                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)last_object));
28323                 clear_cards (card, card_of(last_object));
28324                 n_card_set -= (card_of (last_object) - card);
28325                 total_cards_cleared += (card_of (last_object) - card);
28326             }
28327
28328             n_eph += cg_pointers_found;
28329             cg_pointers_found = 0;
28330             card = card_of (last_object);
28331         }
28332
28333         if (card >= end_card)
28334         {
28335             foundp = find_card (card_table, card, card_word_end, end_card);
28336             if (foundp)
28337             {
28338                 n_card_set += end_card - card;
28339                 start_address = max (beg, card_address (card));
28340             }
28341             limit = min (end, card_address (end_card));
28342         }
28343         if (!foundp || (last_object >= end) || (card_address (card) >= end))
28344         {
28345             if (foundp && (cg_pointers_found == 0))
28346             {
28347                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
28348                            (size_t)end));
28349                 clear_cards (card, card_of (end));
28350                 n_card_set -= (card_of (end) - card);
28351                 total_cards_cleared += (card_of (end) - card);
28352             }
28353             n_eph += cg_pointers_found;
28354             cg_pointers_found = 0;
28355             if ((seg = heap_segment_next_in_range (seg)) != 0)
28356             {
28357 #ifdef BACKGROUND_GC
28358                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
28359 #endif //BACKGROUND_GC
28360                 beg = heap_segment_mem (seg);
28361                 end = compute_next_end (seg, low);
28362                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
28363                 card = card_of (beg);
28364                 last_object = beg;
28365                 end_card = 0;
28366                 continue;
28367             }
28368             else
28369             {
28370                 break;
28371             }
28372         }
28373
28374         assert (card_set_p (card));
28375         {
28376             uint8_t* o = last_object;
28377
28378             o = find_first_object (start_address, last_object);
28379             // Never visit an object twice.
28380             assert (o >= last_object);
28381
28382             //dprintf(3,("Considering card %Ix start object: %Ix, %Ix[ boundary: %Ix",
28383             dprintf(3, ("c: %Ix, o: %Ix, l: %Ix[ boundary: %Ix",
28384                    card, (size_t)o, (size_t)limit, (size_t)gen_boundary));
28385
28386             while (o < limit)
28387             {
28388                 assert (Align (size (o)) >= Align (min_obj_size));
28389                 size_t s = size (o);
28390
28391                 uint8_t* next_o =  o + Align (s);
28392                 Prefetch (next_o);
28393
28394                 if ((o >= gen_boundary) &&
28395                     (seg == ephemeral_heap_segment))
28396                 {
28397                     dprintf (3, ("switching gen boundary %Ix", (size_t)gen_boundary));
28398                     curr_gen_number--;
28399                     assert ((curr_gen_number > 0));
28400                     gen_boundary = generation_allocation_start
28401                         (generation_of (curr_gen_number - 1));
28402                     next_boundary = (compute_next_boundary
28403                                      (low, curr_gen_number, relocating));
28404                 }
28405
28406                 dprintf (4, ("|%Ix|", (size_t)o));
28407
28408                 if (next_o < start_address)
28409                 {
28410                     goto end_object;
28411                 }
28412
28413 #ifdef BACKGROUND_GC
28414                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
28415                 {
28416                     goto end_object;
28417                 }
28418 #endif //BACKGROUND_GC
28419
28420 #ifdef COLLECTIBLE_CLASS
28421                 if (is_collectible(o))
28422                 {
28423                     BOOL passed_end_card_p = FALSE;
28424
28425                     if (card_of (o) > card)
28426                     {
28427                         passed_end_card_p = card_transition (o, end, card_word_end,
28428                             cg_pointers_found, 
28429                             n_eph, n_card_set,
28430                             card, end_card,
28431                             foundp, start_address,
28432                             limit, total_cards_cleared);
28433                     }
28434
28435                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
28436                     {
28437                         // card is valid and it covers the head of the object
28438                         if (fn == &gc_heap::relocate_address)
28439                         {
28440                             keep_card_live (o, n_gen, cg_pointers_found);
28441                         }
28442                         else
28443                         {
28444                             uint8_t* class_obj = get_class_object (o);
28445                             mark_through_cards_helper (&class_obj, n_gen,
28446                                                     cg_pointers_found, fn,
28447                                                     nhigh, next_boundary);
28448                         }
28449                     }
28450
28451                     if (passed_end_card_p)
28452                     {
28453                         if (foundp && (card_address (card) < next_o))
28454                         {
28455                             goto go_through_refs;
28456                         }
28457                         else if (foundp && (start_address < limit))
28458                         {
28459                             next_o = find_first_object (start_address, o);
28460                             goto end_object;
28461                         }
28462                         else
28463                             goto end_limit;                            
28464                     }
28465                 }
28466
28467 go_through_refs:
28468 #endif //COLLECTIBLE_CLASS
28469
28470                 if (contain_pointers (o))
28471                 {
28472                     dprintf(3,("Going through %Ix start_address: %Ix", (size_t)o, (size_t)start_address));
28473
28474                     {
28475                         dprintf (4, ("normal object path"));
28476                         go_through_object
28477                             (method_table(o), o, s, poo,
28478                              start_address, use_start, (o + s),
28479                              {
28480                                  dprintf (4, ("<%Ix>:%Ix", (size_t)poo, (size_t)*poo));
28481                                  if (card_of ((uint8_t*)poo) > card)
28482                                  {
28483                                     BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
28484                                             card_word_end,
28485                                             cg_pointers_found, 
28486                                             n_eph, n_card_set,
28487                                             card, end_card,
28488                                             foundp, start_address,
28489                                             limit, total_cards_cleared);
28490
28491                                      if (passed_end_card_p)
28492                                      {
28493                                         if (foundp && (card_address (card) < next_o))
28494                                         {
28495                                              //new_start();
28496                                              {
28497                                                  if (ppstop <= (uint8_t**)start_address)
28498                                                      {break;}
28499                                                  else if (poo < (uint8_t**)start_address)
28500                                                      {poo = (uint8_t**)start_address;}
28501                                              }
28502                                         }
28503                                         else if (foundp && (start_address < limit))
28504                                         {
28505                                             next_o = find_first_object (start_address, o);
28506                                             goto end_object;
28507                                         }
28508                                          else
28509                                             goto end_limit;
28510                                      }
28511                                  }
28512
28513                                  mark_through_cards_helper (poo, n_gen,
28514                                                             cg_pointers_found, fn,
28515                                                             nhigh, next_boundary);
28516                              }
28517                             );
28518                     }
28519                 }
28520
28521             end_object:
28522                 if (((size_t)next_o / brick_size) != ((size_t) o / brick_size))
28523                 {
28524                     if (brick_table [brick_of (o)] <0)
28525                         fix_brick_to_highest (o, next_o);
28526                 }
28527                 o = next_o;
28528             }
28529         end_limit:
28530             last_object = o;
28531         }
28532     }
28533     // compute the efficiency ratio of the card table
28534     if (!relocating)
28535     {
28536         generation_skip_ratio = ((n_eph > 400)? (int)(((float)n_gen / (float)n_eph) * 100) : 100);
28537         dprintf (3, ("Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
28538             n_eph, n_gen , n_card_set, total_cards_cleared, generation_skip_ratio));
28539     }
28540     else
28541     {
28542         dprintf (3, ("R: Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
28543             n_gen, n_eph, n_card_set, total_cards_cleared, generation_skip_ratio));
28544     }
28545 }
28546
28547 #ifdef SEG_REUSE_STATS
28548 size_t gc_heap::dump_buckets (size_t* ordered_indices, int count, size_t* total_size)
28549 {
28550     size_t total_items = 0;
28551     *total_size = 0;
28552     for (int i = 0; i < count; i++)
28553     {
28554         total_items += ordered_indices[i];
28555         *total_size += ordered_indices[i] << (MIN_INDEX_POWER2 + i);
28556         dprintf (SEG_REUSE_LOG_0, ("[%d]%4d 2^%2d", heap_number, ordered_indices[i], (MIN_INDEX_POWER2 + i)));
28557     } 
28558     dprintf (SEG_REUSE_LOG_0, ("[%d]Total %d items, total size is 0x%Ix", heap_number, total_items, *total_size));
28559     return total_items;
28560 }
28561 #endif // SEG_REUSE_STATS
28562
28563 void gc_heap::count_plug (size_t last_plug_size, uint8_t*& last_plug)
28564 {
28565     // detect pinned plugs
28566     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
28567     {
28568         deque_pinned_plug();
28569         update_oldest_pinned_plug();
28570         dprintf (3, ("dequed pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
28571     }
28572     else
28573     {
28574         size_t plug_size = last_plug_size + Align(min_obj_size);
28575         BOOL is_padded = FALSE;
28576
28577 #ifdef SHORT_PLUGS
28578         plug_size += Align (min_obj_size);
28579         is_padded = TRUE;
28580 #endif //SHORT_PLUGS
28581
28582 #ifdef RESPECT_LARGE_ALIGNMENT
28583         plug_size += switch_alignment_size (is_padded);
28584 #endif //RESPECT_LARGE_ALIGNMENT
28585
28586         total_ephemeral_plugs += plug_size;
28587         size_t plug_size_power2 = round_up_power2 (plug_size);
28588         ordered_plug_indices[relative_index_power2_plug (plug_size_power2)]++;
28589         dprintf (SEG_REUSE_LOG_1, ("[%d]count_plug: adding 0x%Ix - %Id (2^%d) to ordered plug array", 
28590             heap_number, 
28591             last_plug, 
28592             plug_size, 
28593             (relative_index_power2_plug (plug_size_power2) + MIN_INDEX_POWER2)));
28594     }
28595 }
28596
28597 void gc_heap::count_plugs_in_brick (uint8_t* tree, uint8_t*& last_plug)
28598 {
28599     assert ((tree != NULL));
28600     if (node_left_child (tree))
28601     {
28602         count_plugs_in_brick (tree + node_left_child (tree), last_plug);
28603     }
28604
28605     if (last_plug != 0)
28606     {
28607         uint8_t*  plug = tree;
28608         size_t gap_size = node_gap_size (plug);
28609         uint8_t*   gap = (plug - gap_size);
28610         uint8_t*  last_plug_end = gap;
28611         size_t  last_plug_size = (last_plug_end - last_plug);
28612         dprintf (3, ("tree: %Ix, last plug: %Ix, gap size: %Ix, gap: %Ix, last plug size: %Ix",
28613             tree, last_plug, gap_size, gap, last_plug_size));
28614
28615         if (tree == oldest_pinned_plug)
28616         {
28617             dprintf (3, ("tree %Ix is pinned, last plug is %Ix, size is %Ix",
28618                 tree, last_plug, last_plug_size));
28619             mark* m = oldest_pin();
28620             if (m->has_pre_plug_info())
28621             {
28622                 last_plug_size += sizeof (gap_reloc_pair);
28623                 dprintf (3, ("pin %Ix has pre plug, adjusting plug size to %Ix", tree, last_plug_size));
28624             }
28625         }
28626         // Can't assert here - if it's a pinned plug it can be less.
28627         //assert (last_plug_size >= Align (min_obj_size));
28628
28629         count_plug (last_plug_size, last_plug);
28630     }
28631
28632     last_plug = tree;
28633
28634     if (node_right_child (tree))
28635     {
28636         count_plugs_in_brick (tree + node_right_child (tree), last_plug);
28637     }
28638 }
28639
28640 void gc_heap::build_ordered_plug_indices ()
28641 {
28642     memset (ordered_plug_indices, 0, sizeof(ordered_plug_indices));
28643     memset (saved_ordered_plug_indices, 0, sizeof(saved_ordered_plug_indices));
28644
28645     uint8_t*  start_address = generation_limit (max_generation);
28646     uint8_t* end_address = heap_segment_allocated (ephemeral_heap_segment);
28647     size_t  current_brick = brick_of (start_address);
28648     size_t  end_brick = brick_of (end_address - 1);
28649     uint8_t* last_plug = 0;
28650
28651     //Look for the right pinned plug to start from.
28652     reset_pinned_queue_bos();
28653     while (!pinned_plug_que_empty_p())
28654     {
28655         mark* m = oldest_pin();
28656         if ((m->first >= start_address) && (m->first < end_address))
28657         {
28658             dprintf (3, ("found a pin %Ix between %Ix and %Ix", m->first, start_address, end_address));
28659
28660             break;
28661         }
28662         else
28663             deque_pinned_plug();
28664     }
28665     
28666     update_oldest_pinned_plug();
28667
28668     while (current_brick <= end_brick)
28669     {
28670         int brick_entry =  brick_table [ current_brick ];
28671         if (brick_entry >= 0)
28672         {
28673             count_plugs_in_brick (brick_address (current_brick) + brick_entry -1, last_plug);
28674         }
28675
28676         current_brick++;
28677     }
28678
28679     if (last_plug !=0)
28680     {
28681         count_plug (end_address - last_plug, last_plug);
28682     }
28683
28684     // we need to make sure that after fitting all the existing plugs, we
28685     // have big enough free space left to guarantee that the next allocation
28686     // will succeed.
28687     size_t extra_size = END_SPACE_AFTER_GC + Align (min_obj_size);
28688     total_ephemeral_plugs += extra_size;
28689     dprintf (SEG_REUSE_LOG_0, ("Making sure we can fit a large object after fitting all plugs"));
28690     ordered_plug_indices[relative_index_power2_plug (round_up_power2 (extra_size))]++;
28691     
28692     memcpy (saved_ordered_plug_indices, ordered_plug_indices, sizeof(ordered_plug_indices));
28693
28694 #ifdef SEG_REUSE_STATS
28695     dprintf (SEG_REUSE_LOG_0, ("Plugs:"));
28696     size_t total_plug_power2 = 0;
28697     dump_buckets (ordered_plug_indices, MAX_NUM_BUCKETS, &total_plug_power2);
28698     dprintf (SEG_REUSE_LOG_0, ("plugs: 0x%Ix (rounded up to 0x%Ix (%d%%))", 
28699                 total_ephemeral_plugs, 
28700                 total_plug_power2, 
28701                 (total_ephemeral_plugs ? 
28702                     (total_plug_power2 * 100 / total_ephemeral_plugs) :
28703                     0)));
28704     dprintf (SEG_REUSE_LOG_0, ("-------------------"));
28705 #endif // SEG_REUSE_STATS
28706 }
28707
28708 void gc_heap::init_ordered_free_space_indices ()
28709 {
28710     memset (ordered_free_space_indices, 0, sizeof(ordered_free_space_indices));
28711     memset (saved_ordered_free_space_indices, 0, sizeof(saved_ordered_free_space_indices));
28712 }
28713
28714 void gc_heap::trim_free_spaces_indices ()
28715 {
28716     trimmed_free_space_index = -1;
28717     size_t max_count = max_free_space_items - 1;
28718     size_t count = 0;
28719     int i = 0;
28720     for (i = (MAX_NUM_BUCKETS - 1); i >= 0; i--)
28721     {
28722         count += ordered_free_space_indices[i];
28723
28724         if (count >= max_count)
28725         {
28726             break;
28727         }
28728     }
28729
28730     ptrdiff_t extra_free_space_items = count - max_count;
28731
28732     if (extra_free_space_items > 0)
28733     {
28734         ordered_free_space_indices[i] -= extra_free_space_items;
28735         free_space_items = max_count;
28736         trimmed_free_space_index = i;
28737     }
28738     else
28739     {
28740         free_space_items = count;
28741     }
28742
28743     if (i == -1)
28744     {
28745         i = 0;
28746     }
28747
28748     free_space_buckets = MAX_NUM_BUCKETS - i;
28749
28750     for (--i; i >= 0; i--)
28751     {
28752         ordered_free_space_indices[i] = 0;
28753     }
28754
28755     memcpy (saved_ordered_free_space_indices, 
28756             ordered_free_space_indices,
28757             sizeof(ordered_free_space_indices));
28758 }
28759
28760 // We fit as many plugs as we can and update the number of plugs left and the number
28761 // of free spaces left.
28762 BOOL gc_heap::can_fit_in_spaces_p (size_t* ordered_blocks, int small_index, size_t* ordered_spaces, int big_index)
28763 {
28764     assert (small_index <= big_index);
28765     assert (big_index < MAX_NUM_BUCKETS);
28766
28767     size_t small_blocks = ordered_blocks[small_index];
28768
28769     if (small_blocks == 0)
28770     {
28771         return TRUE;
28772     }
28773
28774     size_t big_spaces = ordered_spaces[big_index];
28775
28776     if (big_spaces == 0)
28777     {
28778         return FALSE;
28779     }
28780
28781     dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting %Id 2^%d plugs into %Id 2^%d free spaces", 
28782         heap_number,
28783         small_blocks, (small_index + MIN_INDEX_POWER2),
28784         big_spaces, (big_index + MIN_INDEX_POWER2)));
28785
28786     size_t big_to_small = big_spaces << (big_index - small_index);
28787
28788     ptrdiff_t extra_small_spaces = big_to_small - small_blocks;
28789     dprintf (SEG_REUSE_LOG_1, ("[%d]%d 2^%d spaces can fit %d 2^%d blocks", 
28790         heap_number,
28791         big_spaces, (big_index + MIN_INDEX_POWER2), big_to_small, (small_index + MIN_INDEX_POWER2)));
28792     BOOL can_fit = (extra_small_spaces >= 0);
28793
28794     if (can_fit) 
28795     {
28796         dprintf (SEG_REUSE_LOG_1, ("[%d]Can fit with %d 2^%d extras blocks", 
28797             heap_number,
28798             extra_small_spaces, (small_index + MIN_INDEX_POWER2)));
28799     }
28800
28801     int i = 0;
28802
28803     dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d spaces to 0", heap_number, (big_index + MIN_INDEX_POWER2)));
28804     ordered_spaces[big_index] = 0;
28805     if (extra_small_spaces > 0)
28806     {
28807         dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d blocks to 0", heap_number, (small_index + MIN_INDEX_POWER2)));
28808         ordered_blocks[small_index] = 0;
28809         for (i = small_index; i < big_index; i++)
28810         {
28811             if (extra_small_spaces & 1)
28812             {
28813                 dprintf (SEG_REUSE_LOG_1, ("[%d]Increasing # of 2^%d spaces from %d to %d", 
28814                     heap_number,
28815                     (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + 1)));
28816                 ordered_spaces[i] += 1;
28817             }
28818             extra_small_spaces >>= 1;
28819         }
28820
28821         dprintf (SEG_REUSE_LOG_1, ("[%d]Finally increasing # of 2^%d spaces from %d to %d", 
28822             heap_number,
28823             (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + extra_small_spaces)));
28824         ordered_spaces[i] += extra_small_spaces;
28825     }
28826     else
28827     {
28828         dprintf (SEG_REUSE_LOG_1, ("[%d]Decreasing # of 2^%d blocks from %d to %d", 
28829             heap_number,
28830             (small_index + MIN_INDEX_POWER2), 
28831             ordered_blocks[small_index], 
28832             (ordered_blocks[small_index] - big_to_small)));
28833         ordered_blocks[small_index] -= big_to_small;
28834     }
28835
28836 #ifdef SEG_REUSE_STATS
28837     size_t temp;
28838     dprintf (SEG_REUSE_LOG_1, ("[%d]Plugs became:", heap_number));
28839     dump_buckets (ordered_blocks, MAX_NUM_BUCKETS, &temp);
28840
28841     dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces became:", heap_number));
28842     dump_buckets (ordered_spaces, MAX_NUM_BUCKETS, &temp);
28843 #endif //SEG_REUSE_STATS
28844
28845     return can_fit;
28846 }
28847
28848 // space_index gets updated to the biggest available space index.
28849 BOOL gc_heap::can_fit_blocks_p (size_t* ordered_blocks, int block_index, size_t* ordered_spaces, int* space_index)
28850 {
28851     assert (*space_index >= block_index);
28852
28853     while (!can_fit_in_spaces_p (ordered_blocks, block_index, ordered_spaces, *space_index))
28854     {
28855         (*space_index)--;
28856         if (*space_index < block_index)
28857         {
28858             return FALSE;
28859         }
28860     }
28861
28862     return TRUE;
28863 }
28864
28865 BOOL gc_heap::can_fit_all_blocks_p (size_t* ordered_blocks, size_t* ordered_spaces, int count)
28866 {
28867 #ifdef FEATURE_STRUCTALIGN
28868     // BARTOKTODO (4841): reenable when can_fit_in_spaces_p takes alignment requirements into account
28869     return FALSE;
28870 #endif // FEATURE_STRUCTALIGN
28871     int space_index = count - 1;
28872     for (int block_index = (count - 1); block_index >= 0; block_index--)
28873     {
28874         if (!can_fit_blocks_p (ordered_blocks, block_index, ordered_spaces, &space_index))
28875         {
28876             return FALSE;
28877         }
28878     }
28879
28880     return TRUE;
28881 }
28882
28883 void gc_heap::build_ordered_free_spaces (heap_segment* seg)
28884 {
28885     assert (bestfit_seg);
28886
28887     //bestfit_seg->add_buckets (MAX_NUM_BUCKETS - free_space_buckets + MIN_INDEX_POWER2, 
28888     //                    ordered_free_space_indices + (MAX_NUM_BUCKETS - free_space_buckets), 
28889     //                    free_space_buckets, 
28890     //                    free_space_items);
28891
28892     bestfit_seg->add_buckets (MIN_INDEX_POWER2, 
28893                         ordered_free_space_indices, 
28894                         MAX_NUM_BUCKETS, 
28895                         free_space_items);
28896
28897     assert (settings.condemned_generation == max_generation);
28898
28899     uint8_t* first_address = heap_segment_mem (seg);
28900     uint8_t* end_address   = heap_segment_reserved (seg);
28901     //look through the pinned plugs for relevant ones.
28902     //Look for the right pinned plug to start from.
28903     reset_pinned_queue_bos();
28904     mark* m = 0;
28905     // See comment in can_expand_into_p why we need (max_generation + 1).
28906     size_t eph_gen_starts = (Align (min_obj_size)) * (max_generation + 1);
28907     BOOL has_fit_gen_starts = FALSE;
28908
28909     while (!pinned_plug_que_empty_p())
28910     {
28911         m = oldest_pin();
28912         if ((pinned_plug (m) >= first_address) && 
28913             (pinned_plug (m) < end_address) &&
28914             (pinned_len (m) >= eph_gen_starts))
28915         {
28916
28917             assert ((pinned_plug (m) - pinned_len (m)) == bestfit_first_pin);
28918             break;
28919         }
28920         else
28921         {
28922             deque_pinned_plug();
28923         }
28924     }
28925
28926     if (!pinned_plug_que_empty_p())
28927     {
28928         bestfit_seg->add ((void*)m, TRUE, TRUE);
28929         deque_pinned_plug();
28930         m = oldest_pin();
28931         has_fit_gen_starts = TRUE;
28932     }
28933
28934     while (!pinned_plug_que_empty_p() &&
28935             ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28936     {
28937         bestfit_seg->add ((void*)m, TRUE, FALSE);
28938         deque_pinned_plug();
28939         m = oldest_pin();
28940     }
28941
28942     if (commit_end_of_seg)
28943     {
28944         if (!has_fit_gen_starts)
28945         {
28946             assert (bestfit_first_pin == heap_segment_plan_allocated (seg));
28947         }
28948         bestfit_seg->add ((void*)seg, FALSE, (!has_fit_gen_starts));
28949     }
28950
28951 #ifdef _DEBUG
28952     bestfit_seg->check();
28953 #endif //_DEBUG
28954 }
28955
28956 BOOL gc_heap::try_best_fit (BOOL end_of_segment_p)
28957 {
28958     if (!end_of_segment_p)
28959     {
28960         trim_free_spaces_indices ();
28961     }
28962
28963     BOOL can_bestfit = can_fit_all_blocks_p (ordered_plug_indices, 
28964                                              ordered_free_space_indices, 
28965                                              MAX_NUM_BUCKETS);
28966
28967     return can_bestfit;
28968 }
28969
28970 BOOL gc_heap::best_fit (size_t free_space, 
28971                         size_t largest_free_space, 
28972                         size_t additional_space, 
28973                         BOOL* use_additional_space)
28974 {
28975     dprintf (SEG_REUSE_LOG_0, ("gen%d: trying best fit mechanism", settings.condemned_generation));
28976
28977     assert (!additional_space || (additional_space && use_additional_space));
28978     if (use_additional_space)
28979     {
28980         *use_additional_space = FALSE;
28981     }
28982
28983     if (ordered_plug_indices_init == FALSE)
28984     {
28985         total_ephemeral_plugs = 0;
28986         build_ordered_plug_indices();
28987         ordered_plug_indices_init = TRUE;
28988     }
28989     else
28990     {
28991         memcpy (ordered_plug_indices, saved_ordered_plug_indices, sizeof(ordered_plug_indices));
28992     }
28993
28994     if (total_ephemeral_plugs == (END_SPACE_AFTER_GC + Align (min_obj_size)))
28995     {
28996         dprintf (SEG_REUSE_LOG_0, ("No ephemeral plugs to realloc, done"));
28997         size_t empty_eph = (END_SPACE_AFTER_GC + Align (min_obj_size) + (Align (min_obj_size)) * (max_generation + 1));
28998         BOOL can_fit_empty_eph = (largest_free_space >= empty_eph);
28999         if (!can_fit_empty_eph)
29000         {
29001             can_fit_empty_eph = (additional_space >= empty_eph);
29002
29003             if (can_fit_empty_eph)
29004             {
29005                 *use_additional_space = TRUE;
29006             }
29007         }
29008
29009         return can_fit_empty_eph;
29010     }
29011
29012     if ((total_ephemeral_plugs + approximate_new_allocation()) >= (free_space + additional_space))
29013     {
29014         dprintf (SEG_REUSE_LOG_0, ("We won't have enough free space left in this segment after fitting, done"));
29015         return FALSE;
29016     }
29017
29018     if ((free_space + additional_space) == 0)
29019     {
29020         dprintf (SEG_REUSE_LOG_0, ("No free space in this segment, done"));
29021         return FALSE;
29022     }
29023
29024 #ifdef SEG_REUSE_STATS
29025     dprintf (SEG_REUSE_LOG_0, ("Free spaces:"));
29026     size_t total_free_space_power2 = 0;
29027     size_t total_free_space_items = 
29028         dump_buckets (ordered_free_space_indices, 
29029                       MAX_NUM_BUCKETS,
29030                       &total_free_space_power2);
29031     dprintf (SEG_REUSE_LOG_0, ("currently max free spaces is %Id", max_free_space_items));
29032
29033     dprintf (SEG_REUSE_LOG_0, ("Ephemeral plugs: 0x%Ix, free space: 0x%Ix (rounded down to 0x%Ix (%Id%%)), additional free_space: 0x%Ix",
29034                 total_ephemeral_plugs, 
29035                 free_space, 
29036                 total_free_space_power2, 
29037                 (free_space ? (total_free_space_power2 * 100 / free_space) : 0),
29038                 additional_space));
29039
29040     size_t saved_all_free_space_indices[MAX_NUM_BUCKETS];
29041     memcpy (saved_all_free_space_indices, 
29042             ordered_free_space_indices, 
29043             sizeof(saved_all_free_space_indices));
29044
29045 #endif // SEG_REUSE_STATS
29046
29047     if (total_ephemeral_plugs > (free_space + additional_space))
29048     {
29049         return FALSE;
29050     }
29051
29052     use_bestfit = try_best_fit(FALSE);
29053
29054     if (!use_bestfit && additional_space)
29055     {
29056         int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (additional_space));
29057
29058         if (relative_free_space_index != -1)
29059         {
29060             int relative_plug_index = 0;
29061             size_t plugs_to_fit = 0;
29062
29063             for (relative_plug_index = (MAX_NUM_BUCKETS - 1); relative_plug_index >= 0; relative_plug_index--)
29064             {
29065                 plugs_to_fit = ordered_plug_indices[relative_plug_index];
29066                 if (plugs_to_fit != 0)
29067                 {
29068                     break;
29069                 }
29070             }
29071
29072             if ((relative_plug_index > relative_free_space_index) ||
29073                 ((relative_plug_index == relative_free_space_index) &&
29074                 (plugs_to_fit > 1)))
29075             {
29076 #ifdef SEG_REUSE_STATS
29077                 dprintf (SEG_REUSE_LOG_0, ("additional space is 2^%d but we stopped at %d 2^%d plug(s)",
29078                             (relative_free_space_index + MIN_INDEX_POWER2),
29079                             plugs_to_fit,
29080                             (relative_plug_index + MIN_INDEX_POWER2)));
29081 #endif // SEG_REUSE_STATS
29082                 goto adjust;
29083             }
29084             
29085             dprintf (SEG_REUSE_LOG_0, ("Adding end of segment (2^%d)", (relative_free_space_index + MIN_INDEX_POWER2)));
29086             ordered_free_space_indices[relative_free_space_index]++;
29087             use_bestfit = try_best_fit(TRUE);
29088             if (use_bestfit)
29089             {
29090                 free_space_items++;
29091                 // Since we might've trimmed away some of the free spaces we had, we should see
29092                 // if we really need to use end of seg space - if it's the same or smaller than
29093                 // the largest space we trimmed we can just add that one back instead of 
29094                 // using end of seg.
29095                 if (relative_free_space_index > trimmed_free_space_index)
29096                 {
29097                     *use_additional_space = TRUE;
29098                 }
29099                 else 
29100                 {
29101                     // If the addition space is <= than the last trimmed space, we
29102                     // should just use that last trimmed space instead.
29103                     saved_ordered_free_space_indices[trimmed_free_space_index]++;
29104                 }
29105             }
29106         }
29107     }
29108
29109 adjust:
29110
29111     if (!use_bestfit)
29112     {
29113         dprintf (SEG_REUSE_LOG_0, ("couldn't fit..."));
29114
29115 #ifdef SEG_REUSE_STATS
29116         size_t saved_max = max_free_space_items;
29117         BOOL temp_bestfit = FALSE;
29118
29119         dprintf (SEG_REUSE_LOG_0, ("----Starting experiment process----"));
29120         dprintf (SEG_REUSE_LOG_0, ("----Couldn't fit with max free items %Id", max_free_space_items));
29121
29122         // TODO: need to take the end of segment into consideration.
29123         while (max_free_space_items <= total_free_space_items)
29124         {
29125             max_free_space_items += max_free_space_items / 2;
29126             dprintf (SEG_REUSE_LOG_0, ("----Temporarily increasing max free spaces to %Id", max_free_space_items));
29127             memcpy (ordered_free_space_indices, 
29128                     saved_all_free_space_indices,
29129                     sizeof(ordered_free_space_indices));
29130             if (try_best_fit(FALSE))
29131             {
29132                 temp_bestfit = TRUE;
29133                 break;
29134             }
29135         }
29136
29137         if (temp_bestfit)
29138         {
29139             dprintf (SEG_REUSE_LOG_0, ("----With %Id max free spaces we could fit", max_free_space_items));
29140         }
29141         else
29142         {
29143             dprintf (SEG_REUSE_LOG_0, ("----Tried all free spaces and still couldn't fit, lost too much space"));
29144         }
29145
29146         dprintf (SEG_REUSE_LOG_0, ("----Restoring max free spaces to %Id", saved_max));
29147         max_free_space_items = saved_max;
29148 #endif // SEG_REUSE_STATS
29149         if (free_space_items)
29150         {
29151             max_free_space_items = min (MAX_NUM_FREE_SPACES, free_space_items * 2);
29152             max_free_space_items = max (max_free_space_items, MIN_NUM_FREE_SPACES);
29153         }
29154         else
29155         {
29156             max_free_space_items = MAX_NUM_FREE_SPACES;
29157         }
29158     }
29159
29160     dprintf (SEG_REUSE_LOG_0, ("Adjusted number of max free spaces to %Id", max_free_space_items));
29161     dprintf (SEG_REUSE_LOG_0, ("------End of best fitting process------\n"));
29162
29163     return use_bestfit;
29164 }
29165
29166 BOOL gc_heap::process_free_space (heap_segment* seg, 
29167                          size_t free_space,
29168                          size_t min_free_size, 
29169                          size_t min_cont_size,
29170                          size_t* total_free_space,
29171                          size_t* largest_free_space)
29172 {
29173     *total_free_space += free_space;
29174     *largest_free_space = max (*largest_free_space, free_space);
29175
29176 #ifdef SIMPLE_DPRINTF
29177     dprintf (SEG_REUSE_LOG_1, ("free space len: %Ix, total free space: %Ix, largest free space: %Ix", 
29178                 free_space, *total_free_space, *largest_free_space));
29179 #endif //SIMPLE_DPRINTF
29180
29181     if ((*total_free_space >= min_free_size) && (*largest_free_space >= min_cont_size))
29182     {
29183 #ifdef SIMPLE_DPRINTF
29184         dprintf (SEG_REUSE_LOG_0, ("(gen%d)total free: %Ix(min: %Ix), largest free: %Ix(min: %Ix). Found segment %Ix to reuse without bestfit", 
29185             settings.condemned_generation,
29186             *total_free_space, min_free_size, *largest_free_space, min_cont_size,
29187             (size_t)seg));
29188 #else
29189         UNREFERENCED_PARAMETER(seg);
29190 #endif //SIMPLE_DPRINTF
29191         return TRUE;
29192     }
29193
29194     int free_space_index = relative_index_power2_free_space (round_down_power2 (free_space));
29195     if (free_space_index != -1)
29196     {
29197         ordered_free_space_indices[free_space_index]++;
29198     }
29199     return FALSE;
29200 }
29201
29202 BOOL gc_heap::expand_reused_seg_p()
29203 {
29204     BOOL reused_seg = FALSE;
29205     int heap_expand_mechanism = gc_data_per_heap.get_mechanism (gc_heap_expand);
29206     if ((heap_expand_mechanism == expand_reuse_bestfit) || 
29207         (heap_expand_mechanism == expand_reuse_normal))
29208     {
29209         reused_seg = TRUE;
29210     }
29211
29212     return reused_seg;
29213 }
29214
29215 BOOL gc_heap::can_expand_into_p (heap_segment* seg, size_t min_free_size, size_t min_cont_size,
29216                                  allocator* gen_allocator)
29217 {
29218     min_cont_size += END_SPACE_AFTER_GC;
29219     use_bestfit = FALSE;
29220     commit_end_of_seg = FALSE;
29221     bestfit_first_pin = 0;
29222     uint8_t* first_address = heap_segment_mem (seg);
29223     uint8_t* end_address   = heap_segment_reserved (seg);
29224     size_t end_extra_space = end_space_after_gc();
29225
29226     if ((heap_segment_reserved (seg) - end_extra_space) <= heap_segment_plan_allocated (seg))
29227     {
29228         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: can't use segment [%Ix %Ix, has less than %d bytes at the end",
29229                                    first_address, end_address, end_extra_space));
29230         return FALSE;
29231     }
29232
29233     end_address -= end_extra_space;
29234
29235     dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p(gen%d): min free: %Ix, min continuous: %Ix", 
29236         settings.condemned_generation, min_free_size, min_cont_size));
29237     size_t eph_gen_starts = eph_gen_starts_size;
29238
29239     if (settings.condemned_generation == max_generation)
29240     {
29241         size_t free_space = 0;
29242         size_t largest_free_space = free_space;
29243         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen2: testing segment [%Ix %Ix", first_address, end_address));
29244         //Look through the pinned plugs for relevant ones and Look for the right pinned plug to start from. 
29245         //We are going to allocate the generation starts in the 1st free space,
29246         //so start from the first free space that's big enough for gen starts and a min object size.
29247         // If we see a free space that is >= gen starts but < gen starts + min obj size we just don't use it - 
29248         // we could use it by allocating the last generation start a bit bigger but 
29249         // the complexity isn't worth the effort (those plugs are from gen2 
29250         // already anyway).
29251         reset_pinned_queue_bos();
29252         mark* m = 0;
29253         BOOL has_fit_gen_starts = FALSE;
29254
29255         init_ordered_free_space_indices ();
29256         while (!pinned_plug_que_empty_p())
29257         {
29258             m = oldest_pin();
29259             if ((pinned_plug (m) >= first_address) && 
29260                 (pinned_plug (m) < end_address) &&
29261                 (pinned_len (m) >= (eph_gen_starts + Align (min_obj_size))))
29262             {
29263                 break;
29264             }
29265             else
29266             {
29267                 deque_pinned_plug();
29268             }
29269         }
29270
29271         if (!pinned_plug_que_empty_p())
29272         {
29273             bestfit_first_pin = pinned_plug (m) - pinned_len (m);
29274
29275             if (process_free_space (seg, 
29276                                     pinned_len (m) - eph_gen_starts, 
29277                                     min_free_size, min_cont_size, 
29278                                     &free_space, &largest_free_space))
29279             {
29280                 return TRUE;
29281             }
29282
29283             deque_pinned_plug();
29284             m = oldest_pin();
29285             has_fit_gen_starts = TRUE;
29286         }
29287
29288         dprintf (3, ("first pin is %Ix", pinned_plug (m)));
29289
29290         //tally up free space
29291         while (!pinned_plug_que_empty_p() &&
29292                ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
29293         {
29294             dprintf (3, ("looking at pin %Ix", pinned_plug (m)));
29295             if (process_free_space (seg, 
29296                                     pinned_len (m), 
29297                                     min_free_size, min_cont_size, 
29298                                     &free_space, &largest_free_space))
29299             {
29300                 return TRUE;
29301             }
29302
29303             deque_pinned_plug();
29304             m = oldest_pin();
29305         }
29306
29307         //try to find space at the end of the segment. 
29308         size_t end_space = (end_address - heap_segment_plan_allocated (seg)); 
29309         size_t additional_space = ((min_free_size > free_space) ? (min_free_size - free_space) : 0); 
29310         dprintf (SEG_REUSE_LOG_0, ("end space: %Ix; additional: %Ix", end_space, additional_space));
29311         if (end_space >= additional_space)
29312         {
29313             BOOL can_fit = TRUE;
29314             commit_end_of_seg = TRUE;
29315
29316             if (largest_free_space < min_cont_size)
29317             {
29318                 if (end_space >= min_cont_size)
29319                 {
29320                     additional_space = max (min_cont_size, additional_space);
29321                     dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg for eph", 
29322                         seg));
29323                 }
29324                 else 
29325                 {
29326                     if (settings.concurrent)
29327                     {
29328                         can_fit = FALSE;
29329                         commit_end_of_seg = FALSE;
29330                     }
29331                     else
29332                     {
29333                         size_t additional_space_bestfit = additional_space;
29334                         if (!has_fit_gen_starts)
29335                         {
29336                             if (additional_space_bestfit < (eph_gen_starts + Align (min_obj_size)))
29337                             {
29338                                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, gen starts not allocated yet and end space is too small: %Id",
29339                                         additional_space_bestfit));
29340                                 return FALSE;
29341                             }
29342
29343                             bestfit_first_pin = heap_segment_plan_allocated (seg);
29344                             additional_space_bestfit -= eph_gen_starts;
29345                         }
29346
29347                         can_fit = best_fit (free_space, 
29348                                             largest_free_space,
29349                                             additional_space_bestfit, 
29350                                             &commit_end_of_seg);
29351
29352                         if (can_fit)
29353                         {
29354                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse with bestfit, %s committing end of seg", 
29355                                 seg, (commit_end_of_seg ? "with" : "without")));
29356                         }
29357                         else
29358                         {
29359                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
29360                         }
29361                     }
29362                 }
29363             }
29364             else
29365             {
29366                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg", seg));
29367             }
29368
29369             assert (additional_space <= end_space);
29370             if (commit_end_of_seg)
29371             {
29372                 if (!grow_heap_segment (seg, heap_segment_plan_allocated (seg) + additional_space))
29373                 {
29374                     dprintf (2, ("Couldn't commit end of segment?!"));
29375                     use_bestfit = FALSE;
29376  
29377                     return FALSE;
29378                 }
29379
29380                 if (use_bestfit)
29381                 {
29382                     // We increase the index here because growing heap segment could create a discrepency with 
29383                     // the additional space we used (could be bigger).
29384                     size_t free_space_end_of_seg = 
29385                         heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
29386                     int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (free_space_end_of_seg));
29387                     saved_ordered_free_space_indices[relative_free_space_index]++;
29388                 }
29389             }
29390         
29391             if (use_bestfit)
29392             {
29393                 memcpy (ordered_free_space_indices, 
29394                         saved_ordered_free_space_indices, 
29395                         sizeof(ordered_free_space_indices));
29396                 max_free_space_items = max (MIN_NUM_FREE_SPACES, free_space_items * 3 / 2);
29397                 max_free_space_items = min (MAX_NUM_FREE_SPACES, max_free_space_items);
29398                 dprintf (SEG_REUSE_LOG_0, ("could fit! %Id free spaces, %Id max", free_space_items, max_free_space_items));
29399             }
29400
29401             return can_fit;
29402         }
29403
29404         dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
29405         return FALSE;
29406     }
29407     else
29408     {
29409         assert (settings.condemned_generation == (max_generation-1));
29410         size_t free_space = (end_address - heap_segment_plan_allocated (seg));
29411         size_t largest_free_space = free_space;
29412         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen1: testing segment [%Ix %Ix", first_address, end_address));
29413         //find the first free list in range of the current segment
29414         size_t sz_list = gen_allocator->first_bucket_size();
29415         unsigned int a_l_idx = 0;
29416         uint8_t* free_list = 0;
29417         for (; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
29418         {
29419             if ((eph_gen_starts <= sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
29420             {
29421                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
29422                 while (free_list)
29423                 {
29424                     if ((free_list >= first_address) && 
29425                         (free_list < end_address) && 
29426                         (unused_array_size (free_list) >= eph_gen_starts))
29427                     {
29428                         goto next;
29429                     }
29430                     else
29431                     {
29432                         free_list = free_list_slot (free_list);
29433                     }
29434                 }
29435             }
29436         }
29437 next:
29438         if (free_list)
29439         {
29440             init_ordered_free_space_indices ();
29441             if (process_free_space (seg, 
29442                                     unused_array_size (free_list) - eph_gen_starts + Align (min_obj_size), 
29443                                     min_free_size, min_cont_size, 
29444                                     &free_space, &largest_free_space))
29445             {
29446                 return TRUE;
29447             }
29448
29449             free_list = free_list_slot (free_list);
29450         }
29451         else
29452         {
29453             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, no free list"));
29454             return FALSE;
29455         }
29456
29457        //tally up free space
29458
29459         while (1)
29460         {
29461             while (free_list)
29462             {
29463                 if ((free_list >= first_address) && (free_list < end_address) &&
29464                     process_free_space (seg, 
29465                                         unused_array_size (free_list), 
29466                                         min_free_size, min_cont_size, 
29467                                         &free_space, &largest_free_space))
29468                 {
29469                     return TRUE;
29470                 }
29471
29472                 free_list = free_list_slot (free_list);
29473             }
29474             a_l_idx++;
29475             if (a_l_idx < gen_allocator->number_of_buckets())
29476             {
29477                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
29478             }
29479             else
29480                 break;
29481         } 
29482
29483         dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
29484         return FALSE;
29485
29486         /*
29487         BOOL can_fit = best_fit (free_space, 0, NULL);
29488         if (can_fit)
29489         {
29490             dprintf (SEG_REUSE_LOG_0, ("(gen1)Found segment %Ix to reuse with bestfit", seg));
29491         }
29492         else
29493         {
29494             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
29495         }
29496
29497         return can_fit;
29498         */
29499     }
29500 }
29501
29502 void gc_heap::realloc_plug (size_t last_plug_size, uint8_t*& last_plug,
29503                             generation* gen, uint8_t* start_address,
29504                             unsigned int& active_new_gen_number,
29505                             uint8_t*& last_pinned_gap, BOOL& leftp,
29506                             BOOL shortened_p
29507 #ifdef SHORT_PLUGS
29508                             , mark* pinned_plug_entry
29509 #endif //SHORT_PLUGS
29510                             )
29511 {
29512     // detect generation boundaries
29513     // make sure that active_new_gen_number is not the youngest generation.
29514     // because the generation_limit wouldn't return the right thing in this case.
29515     if (!use_bestfit)
29516     {
29517         if ((active_new_gen_number > 1) &&
29518             (last_plug >= generation_limit (active_new_gen_number)))
29519         {
29520             assert (last_plug >= start_address);
29521             active_new_gen_number--;
29522             realloc_plan_generation_start (generation_of (active_new_gen_number), gen);
29523             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
29524             leftp = FALSE;
29525         }
29526     }
29527
29528     // detect pinned plugs
29529     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
29530     {
29531         size_t  entry = deque_pinned_plug();
29532         mark*  m = pinned_plug_of (entry);
29533
29534         size_t saved_pinned_len = pinned_len(m);
29535         pinned_len(m) = last_plug - last_pinned_gap;
29536         //dprintf (3,("Adjusting pinned gap: [%Ix, %Ix[", (size_t)last_pinned_gap, (size_t)last_plug));
29537
29538         if (m->has_post_plug_info())
29539         {
29540             last_plug_size += sizeof (gap_reloc_pair);
29541             dprintf (3, ("ra pinned %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
29542         }
29543
29544         last_pinned_gap = last_plug + last_plug_size;
29545         dprintf (3, ("ra found pin %Ix, len: %Ix->%Ix, last_p: %Ix, last_p_size: %Ix",
29546             pinned_plug (m), saved_pinned_len, pinned_len (m), last_plug, last_plug_size));
29547         leftp = FALSE;
29548
29549         //we are creating a generation fault. set the cards.
29550         {
29551             size_t end_card = card_of (align_on_card (last_plug + last_plug_size));
29552             size_t card = card_of (last_plug);
29553             while (card != end_card)
29554             {
29555                 set_card (card);
29556                 card++;
29557             }
29558         }
29559     }
29560     else if (last_plug >= start_address)
29561     {
29562 #ifdef FEATURE_STRUCTALIGN
29563         int requiredAlignment;
29564         ptrdiff_t pad;
29565         node_aligninfo (last_plug, requiredAlignment, pad);
29566
29567         // from how we previously aligned the plug's destination address,
29568         // compute the actual alignment offset.
29569         uint8_t* reloc_plug = last_plug + node_relocation_distance (last_plug);
29570         ptrdiff_t alignmentOffset = ComputeStructAlignPad(reloc_plug, requiredAlignment, 0);
29571         if (!alignmentOffset)
29572         {
29573             // allocate_in_expanded_heap doesn't expect alignmentOffset to be zero.
29574             alignmentOffset = requiredAlignment;
29575         }
29576
29577         //clear the alignment info because we are reallocating
29578         clear_node_aligninfo (last_plug);
29579 #else // FEATURE_STRUCTALIGN
29580         //clear the realignment flag because we are reallocating
29581         clear_node_realigned (last_plug);
29582 #endif // FEATURE_STRUCTALIGN
29583         BOOL adjacentp = FALSE;
29584         BOOL set_padding_on_saved_p = FALSE;
29585
29586         if (shortened_p)
29587         {
29588             last_plug_size += sizeof (gap_reloc_pair);
29589
29590 #ifdef SHORT_PLUGS
29591             assert (pinned_plug_entry != NULL);
29592             if (last_plug_size <= sizeof (plug_and_gap))
29593             {
29594                 set_padding_on_saved_p = TRUE;
29595             }
29596 #endif //SHORT_PLUGS
29597
29598             dprintf (3, ("ra plug %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
29599         }
29600
29601 #ifdef SHORT_PLUGS
29602         clear_padding_in_expand (last_plug, set_padding_on_saved_p, pinned_plug_entry);
29603 #endif //SHORT_PLUGS
29604
29605         uint8_t* new_address = allocate_in_expanded_heap(gen, last_plug_size, adjacentp, last_plug,
29606 #ifdef SHORT_PLUGS
29607                                      set_padding_on_saved_p,
29608                                      pinned_plug_entry,
29609 #endif //SHORT_PLUGS
29610                                      TRUE, active_new_gen_number REQD_ALIGN_AND_OFFSET_ARG);
29611
29612         dprintf (3, ("ra NA: [%Ix, %Ix[: %Ix", new_address, (new_address + last_plug_size), last_plug_size));
29613         assert (new_address);
29614         set_node_relocation_distance (last_plug, new_address - last_plug);
29615 #ifdef FEATURE_STRUCTALIGN
29616         if (leftp && node_alignpad (last_plug) == 0)
29617 #else // FEATURE_STRUCTALIGN
29618         if (leftp && !node_realigned (last_plug))
29619 #endif // FEATURE_STRUCTALIGN
29620         {
29621             // TODO - temporarily disable L optimization because of a bug in it.
29622             //set_node_left (last_plug);
29623         }
29624         dprintf (3,(" Re-allocating %Ix->%Ix len %Id", (size_t)last_plug, (size_t)new_address, last_plug_size));
29625         leftp = adjacentp;
29626     }
29627 }
29628
29629 void gc_heap::realloc_in_brick (uint8_t* tree, uint8_t*& last_plug,
29630                                 uint8_t* start_address,
29631                                 generation* gen,
29632                                 unsigned int& active_new_gen_number,
29633                                 uint8_t*& last_pinned_gap, BOOL& leftp)
29634 {
29635     assert (tree != NULL);
29636     int   left_node = node_left_child (tree);
29637     int   right_node = node_right_child (tree);
29638
29639     dprintf (3, ("ra: tree: %Ix, last_pin_gap: %Ix, last_p: %Ix, L: %d, R: %d", 
29640         tree, last_pinned_gap, last_plug, left_node, right_node));
29641
29642     if (left_node)
29643     {
29644         dprintf (3, ("LN: realloc %Ix(%Ix)", (tree + left_node), last_plug));
29645         realloc_in_brick ((tree + left_node), last_plug, start_address,
29646                           gen, active_new_gen_number, last_pinned_gap,
29647                           leftp);
29648     }
29649
29650     if (last_plug != 0)
29651     {
29652         uint8_t*  plug = tree;
29653
29654         BOOL has_pre_plug_info_p = FALSE;
29655         BOOL has_post_plug_info_p = FALSE;
29656         mark* pinned_plug_entry = get_next_pinned_entry (tree, 
29657                                                          &has_pre_plug_info_p,
29658                                                          &has_post_plug_info_p, 
29659                                                          FALSE);
29660
29661         // We only care about the pre plug info 'cause that's what decides if the last plug is shortened.
29662         // The pinned plugs are handled in realloc_plug.
29663         size_t gap_size = node_gap_size (plug);
29664         uint8_t*   gap = (plug - gap_size);
29665         uint8_t*  last_plug_end = gap;
29666         size_t  last_plug_size = (last_plug_end - last_plug);
29667         // Cannot assert this - a plug could be less than that due to the shortened ones.
29668         //assert (last_plug_size >= Align (min_obj_size));
29669         dprintf (3, ("ra: plug %Ix, gap size: %Ix, last_pin_gap: %Ix, last_p: %Ix, last_p_end: %Ix, shortened: %d",
29670             plug, gap_size, last_pinned_gap, last_plug, last_plug_end, (has_pre_plug_info_p ? 1 : 0)));
29671         realloc_plug (last_plug_size, last_plug, gen, start_address,
29672                       active_new_gen_number, last_pinned_gap,
29673                       leftp, has_pre_plug_info_p
29674 #ifdef SHORT_PLUGS
29675                       , pinned_plug_entry
29676 #endif //SHORT_PLUGS
29677                       );
29678     }
29679
29680     last_plug = tree;
29681
29682     if (right_node)
29683     {
29684         dprintf (3, ("RN: realloc %Ix(%Ix)", (tree + right_node), last_plug));
29685         realloc_in_brick ((tree + right_node), last_plug, start_address,
29686                           gen, active_new_gen_number, last_pinned_gap,
29687                           leftp);
29688     }
29689 }
29690
29691 void
29692 gc_heap::realloc_plugs (generation* consing_gen, heap_segment* seg,
29693                         uint8_t* start_address, uint8_t* end_address,
29694                         unsigned active_new_gen_number)
29695 {
29696     dprintf (3, ("--- Reallocing ---"));
29697
29698     if (use_bestfit)
29699     {
29700         //make sure that every generation has a planned allocation start
29701         int  gen_number = max_generation - 1;
29702         while (gen_number >= 0)
29703         {
29704             generation* gen = generation_of (gen_number);
29705             if (0 == generation_plan_allocation_start (gen))
29706             {
29707                 generation_plan_allocation_start (gen) = 
29708                     bestfit_first_pin + (max_generation - gen_number - 1) * Align (min_obj_size);
29709                 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
29710                 assert (generation_plan_allocation_start (gen));
29711             }
29712             gen_number--;
29713         }
29714     }
29715
29716     uint8_t* first_address = start_address;
29717     //Look for the right pinned plug to start from.
29718     reset_pinned_queue_bos();
29719     uint8_t* planned_ephemeral_seg_end = heap_segment_plan_allocated (seg);
29720     while (!pinned_plug_que_empty_p())
29721     {
29722         mark* m = oldest_pin();
29723         if ((pinned_plug (m) >= planned_ephemeral_seg_end) && (pinned_plug (m) < end_address))
29724         {
29725             if (pinned_plug (m) < first_address)
29726             {
29727                 first_address = pinned_plug (m);
29728             }
29729             break;
29730         }
29731         else
29732             deque_pinned_plug();
29733     }
29734
29735     size_t  current_brick = brick_of (first_address);
29736     size_t  end_brick = brick_of (end_address-1);
29737     uint8_t*  last_plug = 0;
29738
29739     uint8_t* last_pinned_gap = heap_segment_plan_allocated (seg);
29740     BOOL leftp = FALSE;
29741
29742     dprintf (3, ("start addr: %Ix, first addr: %Ix, current oldest pin: %Ix",
29743         start_address, first_address, pinned_plug (oldest_pin())));
29744
29745     while (current_brick <= end_brick)
29746     {
29747         int   brick_entry =  brick_table [ current_brick ];
29748         if (brick_entry >= 0)
29749         {
29750             realloc_in_brick ((brick_address (current_brick) + brick_entry - 1),
29751                               last_plug, start_address, consing_gen,
29752                               active_new_gen_number, last_pinned_gap,
29753                               leftp);
29754         }
29755         current_brick++;
29756     }
29757
29758     if (last_plug != 0)
29759     {
29760         realloc_plug (end_address - last_plug, last_plug, consing_gen,
29761                       start_address,
29762                       active_new_gen_number, last_pinned_gap,
29763                       leftp, FALSE
29764 #ifdef SHORT_PLUGS
29765                       , NULL
29766 #endif //SHORT_PLUGS
29767                       );
29768     }
29769
29770     //Fix the old segment allocated size
29771     assert (last_pinned_gap >= heap_segment_mem (seg));
29772     assert (last_pinned_gap <= heap_segment_committed (seg));
29773     heap_segment_plan_allocated (seg) = last_pinned_gap;
29774 }
29775
29776 void gc_heap::verify_no_pins (uint8_t* start, uint8_t* end)
29777 {
29778 #ifdef VERIFY_HEAP
29779     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
29780     {
29781         BOOL contains_pinned_plugs = FALSE;
29782         size_t mi = 0;
29783         mark* m = 0;
29784         while (mi != mark_stack_tos)
29785         {
29786             m = pinned_plug_of (mi);
29787             if ((pinned_plug (m) >= start) && (pinned_plug (m) < end))
29788             {
29789                 contains_pinned_plugs = TRUE;
29790                 break;
29791             }
29792             else
29793                 mi++;
29794         }
29795
29796         if (contains_pinned_plugs)
29797         {
29798             FATAL_GC_ERROR();
29799         }
29800     }
29801 #endif //VERIFY_HEAP
29802 }
29803
29804 void gc_heap::set_expand_in_full_gc (int condemned_gen_number)
29805 {
29806     if (!should_expand_in_full_gc)
29807     {
29808         if ((condemned_gen_number != max_generation) && 
29809             (settings.pause_mode != pause_low_latency) &&
29810             (settings.pause_mode != pause_sustained_low_latency))
29811         {
29812             should_expand_in_full_gc = TRUE;
29813         }
29814     }
29815 }
29816
29817 void gc_heap::save_ephemeral_generation_starts()
29818 {
29819     for (int ephemeral_generation = 0; ephemeral_generation < max_generation; ephemeral_generation++)
29820     {
29821         saved_ephemeral_plan_start[ephemeral_generation] = 
29822             generation_plan_allocation_start (generation_of (ephemeral_generation));
29823         saved_ephemeral_plan_start_size[ephemeral_generation] = 
29824             generation_plan_allocation_start_size (generation_of (ephemeral_generation));
29825     }
29826 }
29827
29828 generation* gc_heap::expand_heap (int condemned_generation,
29829                                   generation* consing_gen,
29830                                   heap_segment* new_heap_segment)
29831 {
29832     UNREFERENCED_PARAMETER(condemned_generation);
29833     assert (condemned_generation >= (max_generation -1));
29834     unsigned int active_new_gen_number = max_generation; //Set one too high to get generation gap
29835     uint8_t*  start_address = generation_limit (max_generation);
29836     uint8_t*  end_address = heap_segment_allocated (ephemeral_heap_segment);
29837     BOOL should_promote_ephemeral = FALSE;
29838     ptrdiff_t eph_size = total_ephemeral_size;
29839 #ifdef BACKGROUND_GC
29840     dprintf(2,("%s: ---- Heap Expansion ----", (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")));
29841 #endif //BACKGROUND_GC
29842     settings.heap_expansion = TRUE;
29843
29844 #ifdef BACKGROUND_GC
29845     if (cm_in_progress)
29846     {
29847         if (!expanded_in_fgc)
29848         {
29849             expanded_in_fgc = TRUE;
29850         }
29851     }
29852 #endif //BACKGROUND_GC
29853
29854     //reset the elevation state for next time.
29855     dprintf (2, ("Elevation: elevation = el_none"));
29856     if (settings.should_lock_elevation && !expand_reused_seg_p())
29857         settings.should_lock_elevation = FALSE;
29858
29859     heap_segment* new_seg = new_heap_segment;
29860
29861     if (!new_seg)
29862         return consing_gen;
29863
29864     //copy the card and brick tables
29865     if (g_gc_card_table!= card_table)
29866         copy_brick_card_table();
29867
29868     BOOL new_segment_p = (heap_segment_next (new_seg) == 0);
29869     dprintf (2, ("new_segment_p %Ix", (size_t)new_segment_p));
29870
29871     assert (generation_plan_allocation_start (generation_of (max_generation-1)));
29872     assert (generation_plan_allocation_start (generation_of (max_generation-1)) >=
29873             heap_segment_mem (ephemeral_heap_segment));
29874     assert (generation_plan_allocation_start (generation_of (max_generation-1)) <=
29875             heap_segment_committed (ephemeral_heap_segment));
29876
29877     assert (generation_plan_allocation_start (youngest_generation));
29878     assert (generation_plan_allocation_start (youngest_generation) <
29879             heap_segment_plan_allocated (ephemeral_heap_segment));
29880
29881     if (settings.pause_mode == pause_no_gc)
29882     {
29883         // We don't reuse for no gc, so the size used on the new eph seg is eph_size.
29884         if ((size_t)(heap_segment_reserved (new_seg) - heap_segment_mem (new_seg)) < (eph_size + soh_allocation_no_gc))
29885             should_promote_ephemeral = TRUE;
29886     }
29887     else
29888     {
29889         if (!use_bestfit)
29890         {
29891             should_promote_ephemeral = dt_low_ephemeral_space_p (tuning_deciding_promote_ephemeral);
29892         }
29893     }
29894
29895     if (should_promote_ephemeral)
29896     {
29897         ephemeral_promotion = TRUE;
29898         get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_new_seg_ep);
29899         dprintf (2, ("promoting ephemeral"));
29900         save_ephemeral_generation_starts();
29901     }
29902     else
29903     {
29904         // commit the new ephemeral segment all at once if it is a new one.
29905         if ((eph_size > 0) && new_segment_p)
29906         {
29907 #ifdef FEATURE_STRUCTALIGN
29908             // The destination may require a larger alignment padding than the source.
29909             // Assume the worst possible alignment padding.
29910             eph_size += ComputeStructAlignPad(heap_segment_mem (new_seg), MAX_STRUCTALIGN, OBJECT_ALIGNMENT_OFFSET);
29911 #endif // FEATURE_STRUCTALIGN
29912 #ifdef RESPECT_LARGE_ALIGNMENT
29913             //Since the generation start can be larger than min_obj_size
29914             //The alignment could be switched. 
29915             eph_size += switch_alignment_size(FALSE);
29916 #endif //RESPECT_LARGE_ALIGNMENT
29917             //Since the generation start can be larger than min_obj_size
29918             //Compare the alignment of the first object in gen1 
29919             if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0)
29920             {
29921                 fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE);
29922                 return consing_gen;
29923             }
29924             heap_segment_used (new_seg) = heap_segment_committed (new_seg);
29925         }
29926
29927         //Fix the end of the old ephemeral heap segment
29928         heap_segment_plan_allocated (ephemeral_heap_segment) =
29929             generation_plan_allocation_start (generation_of (max_generation-1));
29930
29931         dprintf (3, ("Old ephemeral allocated set to %Ix",
29932                     (size_t)heap_segment_plan_allocated (ephemeral_heap_segment)));
29933     }
29934
29935     if (new_segment_p)
29936     {
29937         // TODO - Is this really necessary? We should think about it.
29938         //initialize the first brick
29939         size_t first_brick = brick_of (heap_segment_mem (new_seg));
29940         set_brick (first_brick,
29941                 heap_segment_mem (new_seg) - brick_address (first_brick));
29942     }
29943
29944     //From this point on, we cannot run out of memory
29945
29946     //reset the allocation of the consing generation back to the end of the
29947     //old ephemeral segment
29948     generation_allocation_limit (consing_gen) =
29949         heap_segment_plan_allocated (ephemeral_heap_segment);
29950     generation_allocation_pointer (consing_gen) = generation_allocation_limit (consing_gen);
29951     generation_allocation_segment (consing_gen) = ephemeral_heap_segment;
29952
29953     //clear the generation gap for all of the ephemeral generations
29954     {
29955         int generation_num = max_generation-1;
29956         while (generation_num >= 0)
29957         {
29958             generation* gen = generation_of (generation_num);
29959             generation_plan_allocation_start (gen) = 0;
29960             generation_num--;
29961         }
29962     }
29963
29964     heap_segment* old_seg = ephemeral_heap_segment;
29965     ephemeral_heap_segment = new_seg;
29966
29967     //Note: the ephemeral segment shouldn't be threaded onto the segment chain
29968     //because the relocation and compact phases shouldn't see it
29969
29970     // set the generation members used by allocate_in_expanded_heap
29971     // and switch to ephemeral generation
29972     consing_gen = ensure_ephemeral_heap_segment (consing_gen);
29973
29974     if (!should_promote_ephemeral)
29975     {
29976         realloc_plugs (consing_gen, old_seg, start_address, end_address,
29977                     active_new_gen_number);
29978     }
29979
29980     if (!use_bestfit)
29981     {
29982         repair_allocation_in_expanded_heap (consing_gen);
29983     }
29984
29985     // assert that the generation gap for all of the ephemeral generations were allocated.
29986 #ifdef _DEBUG
29987     {
29988         int generation_num = max_generation-1;
29989         while (generation_num >= 0)
29990         {
29991             generation* gen = generation_of (generation_num);
29992             assert (generation_plan_allocation_start (gen));
29993             generation_num--;
29994         }
29995     }
29996 #endif // _DEBUG
29997
29998     if (!new_segment_p)
29999     {
30000         dprintf (2, ("Demoting ephemeral segment"));
30001         //demote the entire segment.
30002         settings.demotion = TRUE;
30003         get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
30004         demotion_low = heap_segment_mem (ephemeral_heap_segment);
30005         demotion_high = heap_segment_reserved (ephemeral_heap_segment);
30006     }
30007     else
30008     {
30009         demotion_low = MAX_PTR;
30010         demotion_high = 0;
30011 #ifndef MULTIPLE_HEAPS
30012         settings.demotion = FALSE;
30013         get_gc_data_per_heap()->clear_mechanism_bit (gc_demotion_bit);
30014 #endif //!MULTIPLE_HEAPS
30015     }
30016     ptrdiff_t eph_size1 = total_ephemeral_size;
30017     MAYBE_UNUSED_VAR(eph_size1);
30018
30019     if (!should_promote_ephemeral && new_segment_p)
30020     {
30021         assert (eph_size1 <= eph_size);
30022     }
30023
30024     if (heap_segment_mem (old_seg) == heap_segment_plan_allocated (old_seg))
30025     {
30026         // This is to catch when we accidently delete a segment that has pins.
30027         verify_no_pins (heap_segment_mem (old_seg), heap_segment_reserved (old_seg));
30028     }
30029
30030     verify_no_pins (heap_segment_plan_allocated (old_seg), heap_segment_reserved(old_seg));
30031
30032     dprintf(2,("---- End of Heap Expansion ----"));
30033     return consing_gen;
30034 }
30035
30036 void gc_heap::set_static_data()
30037 {
30038     static_data* pause_mode_sdata = static_data_table[latency_level];
30039     for (int i = 0; i < NUMBERGENERATIONS; i++)
30040     {
30041         dynamic_data* dd = dynamic_data_of (i);
30042         static_data* sdata = &pause_mode_sdata[i];
30043
30044         dd->sdata = sdata;
30045         dd->min_size = sdata->min_size;
30046
30047         dprintf (GTC_LOG, ("PM: %d, gen%d:  min: %Id, max: %Id, fr_l: %Id, fr_b: %d%%",
30048             settings.pause_mode,i, 
30049             dd->min_size, dd_max_size (dd), 
30050             sdata->fragmentation_limit, (int)(sdata->fragmentation_burden_limit * 100)));
30051     }
30052 }
30053
30054 // Initialize the values that are not const.
30055 void gc_heap::init_static_data()
30056 {
30057     size_t gen0_min_size = get_gen0_min_size();
30058
30059     size_t gen0_max_size =
30060 #ifdef MULTIPLE_HEAPS
30061         max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024));
30062 #else //MULTIPLE_HEAPS
30063         (gc_can_use_concurrent ?
30064             6*1024*1024 :
30065             max (6*1024*1024,  min ( Align(soh_segment_size/2), 200*1024*1024)));
30066 #endif //MULTIPLE_HEAPS
30067
30068     if (heap_hard_limit)
30069     {
30070         size_t gen0_max_size_seg = soh_segment_size / 4;
30071         dprintf (GTC_LOG, ("limit gen0 max %Id->%Id", gen0_max_size, gen0_max_size_seg));
30072         gen0_max_size = min (gen0_max_size, gen0_max_size_seg);
30073     }
30074
30075     size_t gen0_max_size_config = (size_t)GCConfig::GetGCGen0MaxBudget();
30076
30077     if (gen0_max_size_config)
30078     {
30079         gen0_max_size = min (gen0_max_size, gen0_max_size_config);
30080     }
30081
30082     gen0_max_size = Align (gen0_max_size);
30083
30084     gen0_min_size = min (gen0_min_size, gen0_max_size);
30085
30086     // TODO: gen0_max_size has a 200mb cap; gen1_max_size should also have a cap.
30087     size_t gen1_max_size = (size_t)
30088 #ifdef MULTIPLE_HEAPS
30089         max (6*1024*1024, Align(soh_segment_size/2));
30090 #else //MULTIPLE_HEAPS
30091         (gc_can_use_concurrent ?
30092             6*1024*1024 :
30093             max (6*1024*1024, Align(soh_segment_size/2)));
30094 #endif //MULTIPLE_HEAPS
30095
30096     dprintf (GTC_LOG, ("gen0 min: %Id, max: %Id, gen1 max: %Id",
30097         gen0_min_size, gen0_max_size, gen1_max_size));
30098
30099     for (int i = latency_level_first; i <= latency_level_last; i++)
30100     {
30101         static_data_table[i][0].min_size = gen0_min_size;
30102         static_data_table[i][0].max_size = gen0_max_size;
30103         static_data_table[i][1].max_size = gen1_max_size;
30104     }
30105 }
30106
30107 bool gc_heap::init_dynamic_data()
30108 {
30109     qpf = GCToOSInterface::QueryPerformanceFrequency();
30110
30111     uint32_t now = (uint32_t)GetHighPrecisionTimeStamp();
30112
30113     set_static_data();
30114
30115     for (int i = 0; i <= max_generation+1; i++)
30116     {
30117         dynamic_data* dd = dynamic_data_of (i);
30118         dd->gc_clock = 0;
30119         dd->time_clock = now;
30120         dd->current_size = 0;
30121         dd->promoted_size = 0;
30122         dd->collection_count = 0;
30123         dd->new_allocation = dd->min_size;
30124         dd->gc_new_allocation = dd->new_allocation;
30125         dd->desired_allocation = dd->new_allocation;
30126         dd->fragmentation = 0;
30127     }
30128
30129 #ifdef GC_CONFIG_DRIVEN
30130     if (heap_number == 0)
30131         time_init = now;
30132 #endif //GC_CONFIG_DRIVEN
30133
30134     return true;
30135 }
30136
30137 float gc_heap::surv_to_growth (float cst, float limit, float max_limit)
30138 {
30139     if (cst < ((max_limit - limit ) / (limit * (max_limit-1.0f))))
30140         return ((limit - limit*cst) / (1.0f - (cst * limit)));
30141     else
30142         return max_limit;
30143 }
30144
30145
30146 //if the allocation budget wasn't exhausted, the new budget may be wrong because the survival may 
30147 //not be correct (collection happened too soon). Correct with a linear estimation based on the previous 
30148 //value of the budget 
30149 static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation, 
30150                                        size_t previous_desired_allocation, size_t collection_count)
30151 {
30152     if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0))
30153     {
30154         dprintf (2, ("allocation fraction: %d", (int)(allocation_fraction/100.0)));
30155         new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation);
30156     }
30157 #if 0 
30158     size_t smoothing = 3; // exponential smoothing factor
30159     if (smoothing  > collection_count)
30160         smoothing  = collection_count;
30161     new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1));
30162 #else
30163     UNREFERENCED_PARAMETER(collection_count);
30164 #endif //0
30165     return new_allocation;
30166 }
30167
30168 size_t gc_heap::desired_new_allocation (dynamic_data* dd,
30169                                         size_t out, int gen_number,
30170                                         int pass)
30171 {
30172     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30173
30174     if (dd_begin_data_size (dd) == 0)
30175     {
30176         size_t new_allocation = dd_min_size (dd);
30177         current_gc_data_per_heap->gen_data[gen_number].new_allocation = new_allocation;        
30178         return new_allocation;
30179     }
30180     else
30181     {
30182         float     cst;
30183         size_t    previous_desired_allocation = dd_desired_allocation (dd);
30184         size_t    current_size = dd_current_size (dd);
30185         float     max_limit = dd_max_limit (dd);
30186         float     limit = dd_limit (dd);
30187         size_t    min_gc_size = dd_min_size (dd);
30188         float     f = 0;
30189         size_t    max_size = dd_max_size (dd);
30190         size_t    new_allocation = 0;
30191         float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd));
30192         if (gen_number >= max_generation)
30193         {
30194             size_t    new_size = 0;
30195
30196             cst = min (1.0f, float (out) / float (dd_begin_data_size (dd)));
30197
30198             f = surv_to_growth (cst, limit, max_limit);
30199             size_t max_growth_size = (size_t)(max_size / f);
30200             if (current_size >= max_growth_size)
30201             {
30202                 new_size = max_size;
30203             }
30204             else
30205             {
30206                 new_size = (size_t) min (max ( (f * current_size), min_gc_size), max_size);
30207             }
30208
30209             assert ((new_size >= current_size) || (new_size == max_size));
30210
30211             if (gen_number == max_generation)
30212             {
30213                 new_allocation  =  max((new_size - current_size), min_gc_size);
30214
30215                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
30216                                                           dd_desired_allocation (dd), dd_collection_count (dd));
30217
30218                 if ((dd_fragmentation (dd) > ((size_t)((f-1)*current_size))))
30219                 {
30220                     //reducing allocation in case of fragmentation
30221                     size_t new_allocation1 = max (min_gc_size,
30222                                                   // CAN OVERFLOW
30223                                                   (size_t)((float)new_allocation * current_size /
30224                                                            ((float)current_size + 2*dd_fragmentation (dd))));
30225                     dprintf (2, ("Reducing max_gen allocation due to fragmentation from %Id to %Id",
30226                                  new_allocation, new_allocation1));
30227                     new_allocation = new_allocation1;
30228                 }
30229             }
30230             else //large object heap
30231             {
30232                 uint32_t memory_load = 0;
30233                 uint64_t available_physical = 0;
30234                 get_memory_info (&memory_load, &available_physical);
30235 #ifdef TRACE_GC
30236                 if (heap_hard_limit)
30237                 {
30238                     size_t loh_allocated = 0;
30239                     size_t loh_committed = committed_size (true, &loh_allocated);
30240                     dprintf (1, ("GC#%Id h%d, GMI: LOH budget, LOH commit %Id (obj %Id, frag %Id), total commit: %Id (recorded: %Id)", 
30241                         (size_t)settings.gc_index, heap_number, 
30242                         loh_committed, loh_allocated,
30243                         dd_fragmentation (dynamic_data_of (max_generation + 1)),
30244                         get_total_committed_size(), (current_total_committed - current_total_committed_bookkeeping)));
30245                 }
30246 #endif //TRACE_GC
30247                 if (heap_number == 0)
30248                     settings.exit_memory_load = memory_load;
30249                 if (available_physical > 1024*1024)
30250                     available_physical -= 1024*1024;
30251
30252                 uint64_t available_free = available_physical + (uint64_t)generation_free_list_space (generation_of (gen_number));
30253                 if (available_free > (uint64_t)MAX_PTR)
30254                 {
30255                     available_free = (uint64_t)MAX_PTR;
30256                 }
30257
30258                 //try to avoid OOM during large object allocation
30259                 new_allocation = max (min(max((new_size - current_size), dd_desired_allocation (dynamic_data_of (max_generation))), 
30260                                           (size_t)available_free), 
30261                                       max ((current_size/4), min_gc_size));
30262
30263                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
30264                                                           dd_desired_allocation (dd), dd_collection_count (dd));
30265
30266             }
30267         }
30268         else
30269         {
30270             size_t survivors = out;
30271             cst = float (survivors) / float (dd_begin_data_size (dd));
30272             f = surv_to_growth (cst, limit, max_limit);
30273             new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size);
30274
30275             new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
30276                                                       dd_desired_allocation (dd), dd_collection_count (dd));
30277
30278             if (gen_number == 0)
30279             {
30280                 if (pass == 0)
30281                 {
30282
30283                     //printf ("%f, %Id\n", cst, new_allocation);
30284                     size_t free_space = generation_free_list_space (generation_of (gen_number));
30285                     // DTREVIEW - is min_gc_size really a good choice? 
30286                     // on 64-bit this will almost always be true.
30287                     dprintf (GTC_LOG, ("frag: %Id, min: %Id", free_space, min_gc_size));
30288                     if (free_space > min_gc_size)
30289                     {
30290                         settings.gen0_reduction_count = 2;
30291                     }
30292                     else
30293                     {
30294                         if (settings.gen0_reduction_count > 0)
30295                             settings.gen0_reduction_count--;
30296                     }
30297                 }
30298                 if (settings.gen0_reduction_count > 0)
30299                 {
30300                     dprintf (2, ("Reducing new allocation based on fragmentation"));
30301                     new_allocation = min (new_allocation,
30302                                           max (min_gc_size, (max_size/3)));
30303                 }
30304             }
30305         }
30306
30307         size_t new_allocation_ret = 
30308             Align (new_allocation, get_alignment_constant (!(gen_number == (max_generation+1))));
30309         int gen_data_index = gen_number;
30310         gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_data_index]);
30311         gen_data->new_allocation = new_allocation_ret;
30312
30313         dd_surv (dd) = cst;
30314
30315 #ifdef SIMPLE_DPRINTF
30316         dprintf (1, ("h%d g%d surv: %Id current: %Id alloc: %Id (%d%%) f: %d%% new-size: %Id new-alloc: %Id",
30317                     heap_number, gen_number, out, current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)),
30318                     (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
30319 #else
30320         dprintf (1,("gen: %d in: %Id out: %Id ", gen_number, generation_allocation_size (generation_of (gen_number)), out));
30321         dprintf (1,("current: %Id alloc: %Id ", current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd))));
30322         dprintf (1,(" surv: %d%% f: %d%% new-size: %Id new-alloc: %Id",
30323                     (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
30324 #endif //SIMPLE_DPRINTF
30325
30326         return new_allocation_ret;
30327     }
30328 }
30329
30330 //returns the planned size of a generation (including free list element)
30331 size_t gc_heap::generation_plan_size (int gen_number)
30332 {
30333     if (0 == gen_number)
30334         return max((heap_segment_plan_allocated (ephemeral_heap_segment) -
30335                     generation_plan_allocation_start (generation_of (gen_number))),
30336                    (int)Align (min_obj_size));
30337     else
30338     {
30339         generation* gen = generation_of (gen_number);
30340         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
30341             return (generation_plan_allocation_start (generation_of (gen_number - 1)) -
30342                     generation_plan_allocation_start (generation_of (gen_number)));
30343         else
30344         {
30345             size_t gensize = 0;
30346             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30347
30348             PREFIX_ASSUME(seg != NULL);
30349
30350             while (seg && (seg != ephemeral_heap_segment))
30351             {
30352                 gensize += heap_segment_plan_allocated (seg) -
30353                            heap_segment_mem (seg);
30354                 seg = heap_segment_next_rw (seg);
30355             }
30356             if (seg)
30357             {
30358                 gensize += (generation_plan_allocation_start (generation_of (gen_number - 1)) -
30359                             heap_segment_mem (ephemeral_heap_segment));
30360             }
30361             return gensize;
30362         }
30363     }
30364
30365 }
30366
30367 //returns the size of a generation (including free list element)
30368 size_t gc_heap::generation_size (int gen_number)
30369 {
30370     if (0 == gen_number)
30371         return max((heap_segment_allocated (ephemeral_heap_segment) -
30372                     generation_allocation_start (generation_of (gen_number))),
30373                    (int)Align (min_obj_size));
30374     else
30375     {
30376         generation* gen = generation_of (gen_number);
30377         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
30378             return (generation_allocation_start (generation_of (gen_number - 1)) -
30379                     generation_allocation_start (generation_of (gen_number)));
30380         else
30381         {
30382             size_t gensize = 0;
30383             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30384
30385             PREFIX_ASSUME(seg != NULL);
30386
30387             while (seg && (seg != ephemeral_heap_segment))
30388             {
30389                 gensize += heap_segment_allocated (seg) -
30390                            heap_segment_mem (seg);
30391                 seg = heap_segment_next_rw (seg);
30392             }
30393             if (seg)
30394             {
30395                 gensize += (generation_allocation_start (generation_of (gen_number - 1)) -
30396                             heap_segment_mem (ephemeral_heap_segment));
30397             }
30398
30399             return gensize;
30400         }
30401     }
30402
30403 }
30404
30405 size_t  gc_heap::compute_in (int gen_number)
30406 {
30407     assert (gen_number != 0);
30408     dynamic_data* dd = dynamic_data_of (gen_number);
30409
30410     size_t in = generation_allocation_size (generation_of (gen_number));
30411
30412     if (gen_number == max_generation && ephemeral_promotion)
30413     {
30414         in = 0;
30415         for (int i = 0; i <= max_generation; i++)
30416         {
30417             dynamic_data* dd = dynamic_data_of (i);
30418             in += dd_survived_size (dd);
30419             if (i != max_generation)
30420             {
30421                 generation_condemned_allocated (generation_of (gen_number)) += dd_survived_size (dd);
30422             }
30423         }
30424     }
30425
30426     dd_gc_new_allocation (dd) -= in;
30427     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30428
30429     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30430     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
30431     gen_data->in = in;
30432
30433     generation_allocation_size (generation_of (gen_number)) = 0;
30434     return in;
30435 }
30436
30437 void  gc_heap::compute_promoted_allocation (int gen_number)
30438 {
30439     compute_in (gen_number);
30440 }
30441
30442 #ifdef BIT64
30443 inline
30444 size_t gc_heap::trim_youngest_desired (uint32_t memory_load,
30445                                        size_t total_new_allocation,
30446                                        size_t total_min_allocation)
30447 {
30448     if (memory_load < MAX_ALLOWED_MEM_LOAD)
30449     {
30450         // If the total of memory load and gen0 budget exceeds 
30451         // our max memory load limit, trim the gen0 budget so the total 
30452         // is the max memory load limit.
30453         size_t remain_memory_load = (MAX_ALLOWED_MEM_LOAD - memory_load) * mem_one_percent;
30454         return min (total_new_allocation, remain_memory_load);
30455     }
30456     else
30457     {
30458         return max (mem_one_percent, total_min_allocation);
30459     }
30460 }
30461
30462 size_t gc_heap::joined_youngest_desired (size_t new_allocation)
30463 {
30464     dprintf (2, ("Entry memory load: %d; gen0 new_alloc: %Id", settings.entry_memory_load, new_allocation));
30465
30466     size_t final_new_allocation = new_allocation;
30467     if (new_allocation > MIN_YOUNGEST_GEN_DESIRED)
30468     {
30469         uint32_t num_heaps = 1;
30470
30471 #ifdef MULTIPLE_HEAPS
30472         num_heaps = gc_heap::n_heaps;
30473 #endif //MULTIPLE_HEAPS
30474
30475         size_t total_new_allocation = new_allocation * num_heaps;
30476         size_t total_min_allocation = MIN_YOUNGEST_GEN_DESIRED * num_heaps;
30477
30478         if ((settings.entry_memory_load >= MAX_ALLOWED_MEM_LOAD) ||
30479             (total_new_allocation > max (youngest_gen_desired_th, total_min_allocation)))
30480         {
30481             uint32_t memory_load = 0;
30482             get_memory_info (&memory_load);
30483             settings.exit_memory_load = memory_load;
30484             dprintf (2, ("Current emory load: %d", memory_load));
30485
30486             size_t final_total = 
30487                 trim_youngest_desired (memory_load, total_new_allocation, total_min_allocation);
30488             size_t max_new_allocation = 
30489 #ifdef MULTIPLE_HEAPS
30490                                          dd_max_size (g_heaps[0]->dynamic_data_of (0));
30491 #else //MULTIPLE_HEAPS
30492                                          dd_max_size (dynamic_data_of (0));
30493 #endif //MULTIPLE_HEAPS
30494
30495             final_new_allocation  = min (Align ((final_total / num_heaps), get_alignment_constant (TRUE)), max_new_allocation);
30496         }
30497     }
30498
30499     if (final_new_allocation < new_allocation)
30500     {
30501         settings.gen0_reduction_count = 2;
30502     }
30503
30504     return final_new_allocation;
30505 }
30506 #endif // BIT64 
30507
30508 inline
30509 gc_history_per_heap* gc_heap::get_gc_data_per_heap()
30510 {
30511 #ifdef BACKGROUND_GC
30512     return (settings.concurrent ? &bgc_data_per_heap : &gc_data_per_heap);
30513 #else
30514     return &gc_data_per_heap;
30515 #endif //BACKGROUND_GC
30516 }
30517
30518 void gc_heap::compute_new_dynamic_data (int gen_number)
30519 {
30520     PREFIX_ASSUME(gen_number >= 0);
30521     PREFIX_ASSUME(gen_number <= max_generation);
30522
30523     dynamic_data* dd = dynamic_data_of (gen_number);
30524     generation*   gen = generation_of (gen_number);
30525     size_t        in = (gen_number==0) ? 0 : compute_in (gen_number);
30526
30527     size_t total_gen_size = generation_size (gen_number);
30528     //keep track of fragmentation
30529     dd_fragmentation (dd) = generation_free_list_space (gen) + generation_free_obj_space (gen);
30530     dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
30531
30532     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30533
30534     size_t out = dd_survived_size (dd);
30535
30536     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
30537     gen_data->size_after = total_gen_size;
30538     gen_data->free_list_space_after = generation_free_list_space (gen);
30539     gen_data->free_obj_space_after = generation_free_obj_space (gen);
30540
30541     if ((settings.pause_mode == pause_low_latency) && (gen_number <= 1))
30542     {
30543         // When we are in the low latency mode, we can still be
30544         // condemning more than gen1's 'cause of induced GCs.
30545         dd_desired_allocation (dd) = low_latency_alloc;
30546     }
30547     else
30548     {
30549         if (gen_number == 0)
30550         {
30551             //compensate for dead finalizable objects promotion.
30552             //they shoudn't be counted for growth.
30553             size_t final_promoted = 0;
30554             final_promoted = min (promoted_bytes (heap_number), out);
30555             // Prefast: this is clear from above but prefast needs to be told explicitly
30556             PREFIX_ASSUME(final_promoted <= out);
30557
30558             dprintf (2, ("gen: %d final promoted: %Id", gen_number, final_promoted));
30559             dd_freach_previous_promotion (dd) = final_promoted;
30560             size_t lower_bound = desired_new_allocation  (dd, out-final_promoted, gen_number, 0);
30561
30562             if (settings.condemned_generation == 0)
30563             {
30564                 //there is no noise.
30565                 dd_desired_allocation (dd) = lower_bound;
30566             }
30567             else
30568             {
30569                 size_t higher_bound = desired_new_allocation (dd, out, gen_number, 1);
30570
30571                 // <TODO>This assert was causing AppDomains\unload\test1n\test1nrun.bat to fail</TODO>
30572                 //assert ( lower_bound <= higher_bound);
30573
30574                 //discount the noise. Change the desired allocation
30575                 //only if the previous value is outside of the range.
30576                 if (dd_desired_allocation (dd) < lower_bound)
30577                 {
30578                     dd_desired_allocation (dd) = lower_bound;
30579                 }
30580                 else if (dd_desired_allocation (dd) > higher_bound)
30581                 {
30582                     dd_desired_allocation (dd) = higher_bound;
30583                 }
30584 #if defined (BIT64) && !defined (MULTIPLE_HEAPS)
30585                 dd_desired_allocation (dd) = joined_youngest_desired (dd_desired_allocation (dd));
30586 #endif // BIT64 && !MULTIPLE_HEAPS
30587                 trim_youngest_desired_low_memory();
30588                 dprintf (2, ("final gen0 new_alloc: %Id", dd_desired_allocation (dd)));
30589             }
30590         }
30591         else
30592         {
30593             dd_desired_allocation (dd) = desired_new_allocation (dd, out, gen_number, 0);
30594         }
30595     }
30596
30597     gen_data->pinned_surv = dd_pinned_survived_size (dd);
30598     gen_data->npinned_surv = dd_survived_size (dd) - dd_pinned_survived_size (dd);
30599
30600     dd_gc_new_allocation (dd) = dd_desired_allocation (dd);
30601     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30602
30603     //update counter
30604     dd_promoted_size (dd) = out;
30605     if (gen_number == max_generation)
30606     {
30607         dd = dynamic_data_of (max_generation+1);
30608         total_gen_size = generation_size (max_generation + 1);
30609         dd_fragmentation (dd) = generation_free_list_space (large_object_generation) + 
30610                                 generation_free_obj_space (large_object_generation);
30611         dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
30612         dd_survived_size (dd) = dd_current_size (dd);
30613         in = 0;
30614         out = dd_current_size (dd);
30615         dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0);
30616         dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd),
30617                                            get_alignment_constant (FALSE));
30618         dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30619
30620         gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]);
30621         gen_data->size_after = total_gen_size;
30622         gen_data->free_list_space_after = generation_free_list_space (large_object_generation);
30623         gen_data->free_obj_space_after = generation_free_obj_space (large_object_generation);
30624         gen_data->npinned_surv = out;
30625 #ifdef BACKGROUND_GC
30626         end_loh_size = total_gen_size;
30627 #endif //BACKGROUND_GC
30628         //update counter
30629         dd_promoted_size (dd) = out;
30630     }
30631 }
30632
30633 void gc_heap::trim_youngest_desired_low_memory()
30634 {
30635     if (g_low_memory_status)
30636     {
30637         size_t committed_mem = 0;
30638         heap_segment* seg = generation_start_segment (generation_of (max_generation));
30639         while (seg)
30640         {
30641             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30642             seg = heap_segment_next (seg);
30643         }
30644         seg = generation_start_segment (generation_of (max_generation + 1));
30645         while (seg)
30646         {
30647             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30648             seg = heap_segment_next (seg);
30649         }
30650
30651         dynamic_data* dd = dynamic_data_of (0);
30652         size_t current = dd_desired_allocation (dd);
30653         size_t candidate = max (Align ((committed_mem / 10), get_alignment_constant(FALSE)), dd_min_size (dd));
30654
30655         dd_desired_allocation (dd) = min (current, candidate);
30656     }
30657 }
30658
30659 void gc_heap::decommit_ephemeral_segment_pages()
30660 {
30661     if (settings.concurrent)
30662     {
30663         return;
30664     }
30665
30666     size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30667
30668     dynamic_data* dd = dynamic_data_of (0);
30669
30670 #ifndef MULTIPLE_HEAPS
30671     size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024));
30672     size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT);
30673     size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time;
30674
30675     if (dd_desired_allocation (dd) > gc_gen0_desired_high)
30676     {
30677         gc_gen0_desired_high = dd_desired_allocation (dd) + extra_space;
30678     }
30679
30680     if (ephemeral_elapsed >= decommit_timeout)
30681     {
30682         slack_space = min (slack_space, gc_gen0_desired_high);
30683
30684         gc_last_ephemeral_decommit_time = dd_time_clock(dd);
30685         gc_gen0_desired_high = 0;
30686     }
30687 #endif //!MULTIPLE_HEAPS
30688
30689     if (settings.condemned_generation >= (max_generation-1))
30690     {
30691         size_t new_slack_space = 
30692 #ifdef BIT64
30693                     max(min(min(soh_segment_size/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd));
30694 #else
30695 #ifdef FEATURE_CORECLR
30696                     dd_desired_allocation (dd);
30697 #else
30698                     dd_max_size (dd);
30699 #endif //FEATURE_CORECLR                                    
30700 #endif // BIT64
30701
30702         slack_space = min (slack_space, new_slack_space);
30703     }
30704
30705     decommit_heap_segment_pages (ephemeral_heap_segment, slack_space);    
30706
30707     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30708     current_gc_data_per_heap->extra_gen0_committed = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30709 }
30710
30711 //This is meant to be called by decide_on_compacting.
30712
30713 size_t gc_heap::generation_fragmentation (generation* gen,
30714                                           generation* consing_gen,
30715                                           uint8_t* end)
30716 {
30717     size_t frag;
30718     uint8_t* alloc = generation_allocation_pointer (consing_gen);
30719     // If the allocation pointer has reached the ephemeral segment
30720     // fine, otherwise the whole ephemeral segment is considered
30721     // fragmentation
30722     if (in_range_for_segment (alloc, ephemeral_heap_segment))
30723         {
30724             if (alloc <= heap_segment_allocated(ephemeral_heap_segment))
30725                 frag = end - alloc;
30726             else
30727             {
30728                 // case when no survivors, allocated set to beginning
30729                 frag = 0;
30730             }
30731             dprintf (3, ("ephemeral frag: %Id", frag));
30732         }
30733     else
30734         frag = (heap_segment_allocated (ephemeral_heap_segment) -
30735                 heap_segment_mem (ephemeral_heap_segment));
30736     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30737
30738     PREFIX_ASSUME(seg != NULL);
30739
30740     while (seg != ephemeral_heap_segment)
30741     {
30742         frag += (heap_segment_allocated (seg) -
30743                  heap_segment_plan_allocated (seg));
30744         dprintf (3, ("seg: %Ix, frag: %Id", (size_t)seg,
30745                      (heap_segment_allocated (seg) -
30746                       heap_segment_plan_allocated (seg))));
30747
30748         seg = heap_segment_next_rw (seg);
30749         assert (seg);
30750     }
30751     dprintf (3, ("frag: %Id discounting pinned plugs", frag));
30752     //add the length of the dequeued plug free space
30753     size_t bos = 0;
30754     while (bos < mark_stack_bos)
30755     {
30756         frag += (pinned_len (pinned_plug_of (bos)));
30757         bos++;
30758     }
30759
30760     return frag;
30761 }
30762
30763 // for SOH this returns the total sizes of the generation and its 
30764 // younger generation(s).
30765 // for LOH this returns just LOH size.
30766 size_t gc_heap::generation_sizes (generation* gen)
30767 {
30768     size_t result = 0;
30769     if (generation_start_segment (gen ) == ephemeral_heap_segment)
30770         result = (heap_segment_allocated (ephemeral_heap_segment) -
30771                   generation_allocation_start (gen));
30772     else
30773     {
30774         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
30775
30776         PREFIX_ASSUME(seg != NULL);
30777
30778         while (seg)
30779         {
30780             result += (heap_segment_allocated (seg) -
30781                        heap_segment_mem (seg));
30782             seg = heap_segment_next_in_range (seg);
30783         }
30784     }
30785
30786     return result;
30787 }
30788
30789 size_t gc_heap::estimated_reclaim (int gen_number)
30790 {
30791     dynamic_data* dd = dynamic_data_of (gen_number);
30792     size_t gen_allocated = (dd_desired_allocation (dd) - dd_new_allocation (dd));
30793     size_t gen_total_size = gen_allocated + dd_current_size (dd);
30794     size_t est_gen_surv = (size_t)((float) (gen_total_size) * dd_surv (dd));
30795     size_t est_gen_free = gen_total_size - est_gen_surv + dd_fragmentation (dd);
30796
30797     dprintf (GTC_LOG, ("h%d gen%d total size: %Id, est dead space: %Id (s: %d, allocated: %Id), frag: %Id",
30798                 heap_number, gen_number,
30799                 gen_total_size,
30800                 est_gen_free, 
30801                 (int)(dd_surv (dd) * 100),
30802                 gen_allocated,
30803                 dd_fragmentation (dd)));
30804
30805     return est_gen_free;
30806 }
30807
30808 BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
30809                                     size_t fragmentation,
30810                                     BOOL& should_expand)
30811 {
30812     BOOL should_compact = FALSE;
30813     should_expand = FALSE;
30814     generation*   gen = generation_of (condemned_gen_number);
30815     dynamic_data* dd = dynamic_data_of (condemned_gen_number);
30816     size_t gen_sizes     = generation_sizes(gen);
30817     float  fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) :
30818                                     (float (fragmentation) / gen_sizes) );
30819
30820     dprintf (GTC_LOG, ("h%d g%d fragmentation: %Id (%d%%)", 
30821         heap_number, settings.condemned_generation, 
30822         fragmentation, (int)(fragmentation_burden * 100.0)));
30823
30824 #ifdef STRESS_HEAP
30825     // for pure GC stress runs we need compaction, for GC stress "mix"
30826     // we need to ensure a better mix of compacting and sweeping collections
30827     if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent
30828         && !g_pConfig->IsGCStressMix())
30829         should_compact = TRUE;
30830
30831 #ifdef GC_STATS
30832     // in GC stress "mix" mode, for stress induced collections make sure we 
30833     // keep sweeps and compactions relatively balanced. do not (yet) force sweeps
30834     // against the GC's determination, as it may lead to premature OOMs.
30835     if (g_pConfig->IsGCStressMix() && settings.stress_induced)
30836     {
30837         int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC;
30838         int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions;
30839         if (compactions < sweeps / 10)
30840         {
30841             should_compact = TRUE;
30842         }
30843     }
30844 #endif // GC_STATS
30845 #endif //STRESS_HEAP
30846
30847     if (GCConfig::GetForceCompact())
30848         should_compact = TRUE;
30849
30850     if ((condemned_gen_number == max_generation) && last_gc_before_oom)
30851     {
30852         should_compact = TRUE;
30853         last_gc_before_oom = FALSE;
30854         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_last_gc);
30855     }
30856
30857     if (settings.reason == reason_induced_compacting)
30858     {
30859         dprintf (2, ("induced compacting GC"));
30860         should_compact = TRUE;
30861         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting);
30862     }
30863
30864     if (settings.reason == reason_pm_full_gc)
30865     {
30866         assert (condemned_gen_number == max_generation);
30867         if (heap_number == 0)
30868         {
30869             dprintf (GTC_LOG, ("PM doing compacting full GC after a gen1"));
30870         }
30871         should_compact = TRUE;
30872     }
30873
30874     dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%",
30875                 fragmentation, (int) (100*fragmentation_burden)));
30876
30877     if (provisional_mode_triggered && (condemned_gen_number == (max_generation - 1)))
30878     {
30879         dprintf (GTC_LOG, ("gen1 in PM always compact"));
30880         should_compact = TRUE;
30881     }
30882
30883     if (!should_compact)
30884     {
30885         if (dt_low_ephemeral_space_p (tuning_deciding_compaction))
30886         {
30887             dprintf(GTC_LOG, ("compacting due to low ephemeral"));
30888             should_compact = TRUE;
30889             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_low_ephemeral);
30890         }
30891     }
30892
30893     if (should_compact)
30894     {
30895         if ((condemned_gen_number >= (max_generation - 1)))
30896         {
30897             if (dt_low_ephemeral_space_p (tuning_deciding_expansion))
30898             {
30899                 dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction"));
30900                 should_expand = TRUE;
30901             }
30902         }
30903     }
30904
30905 #ifdef BIT64
30906     BOOL high_memory = FALSE;
30907 #endif // BIT64
30908
30909     if (!should_compact)
30910     {
30911         // We are not putting this in dt_high_frag_p because it's not exactly
30912         // high fragmentation - it's just enough planned fragmentation for us to 
30913         // want to compact. Also the "fragmentation" we are talking about here
30914         // is different from anywhere else.
30915         BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) &&
30916                                 (fragmentation_burden >= dd_fragmentation_burden_limit (dd)));
30917
30918         if (frag_exceeded)
30919         {
30920 #ifdef BACKGROUND_GC
30921             // do not force compaction if this was a stress-induced GC
30922             IN_STRESS_HEAP(if (!settings.stress_induced))
30923             {
30924 #endif // BACKGROUND_GC
30925             assert (settings.concurrent == FALSE);
30926             should_compact = TRUE;
30927             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_frag);
30928 #ifdef BACKGROUND_GC
30929             }
30930 #endif // BACKGROUND_GC
30931         }
30932
30933 #ifdef BIT64
30934         // check for high memory situation
30935         if(!should_compact)
30936         {
30937             uint32_t num_heaps = 1;
30938 #ifdef MULTIPLE_HEAPS
30939             num_heaps = gc_heap::n_heaps;
30940 #endif // MULTIPLE_HEAPS
30941             
30942             ptrdiff_t reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation);
30943
30944             if((settings.entry_memory_load >= high_memory_load_th) && (settings.entry_memory_load < v_high_memory_load_th))
30945             {
30946                 if(reclaim_space > (int64_t)(min_high_fragmentation_threshold (entry_available_physical_mem, num_heaps)))
30947                 {
30948                     dprintf(GTC_LOG,("compacting due to fragmentation in high memory"));
30949                     should_compact = TRUE;
30950                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_mem_frag);
30951                 }
30952                 high_memory = TRUE;
30953             }
30954             else if(settings.entry_memory_load >= v_high_memory_load_th)
30955             {
30956                 if(reclaim_space > (ptrdiff_t)(min_reclaim_fragmentation_threshold (num_heaps)))
30957                 {
30958                     dprintf(GTC_LOG,("compacting due to fragmentation in very high memory"));
30959                     should_compact = TRUE;
30960                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_vhigh_mem_frag);
30961                 }
30962                 high_memory = TRUE;
30963             }
30964         }
30965 #endif // BIT64
30966     }
30967
30968     // The purpose of calling ensure_gap_allocation here is to make sure
30969     // that we actually are able to commit the memory to allocate generation
30970     // starts.
30971     if ((should_compact == FALSE) &&
30972         (ensure_gap_allocation (condemned_gen_number) == FALSE))
30973     {
30974         should_compact = TRUE;
30975         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_no_gaps);
30976     }
30977
30978     if (settings.condemned_generation == max_generation)
30979     {
30980         //check the progress
30981         if (
30982 #ifdef BIT64
30983             (high_memory && !should_compact) ||
30984 #endif // BIT64
30985             (generation_plan_allocation_start (generation_of (max_generation - 1)) >= 
30986                 generation_allocation_start (generation_of (max_generation - 1))))
30987         {
30988             dprintf (1, ("gen1 start %Ix->%Ix, gen2 size %Id->%Id, lock elevation",
30989                     generation_allocation_start (generation_of (max_generation - 1)),
30990                     generation_plan_allocation_start (generation_of (max_generation - 1)),
30991                      generation_size (max_generation),
30992                      generation_plan_size (max_generation)));
30993             //no progress -> lock
30994             settings.should_lock_elevation = TRUE;
30995         }
30996     }
30997
30998     if (settings.pause_mode == pause_no_gc)
30999     {
31000         should_compact = TRUE;
31001         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_plan_allocated (ephemeral_heap_segment))
31002             < soh_allocation_no_gc)
31003         {
31004             should_expand = TRUE;
31005         }
31006     }
31007
31008     dprintf (2, ("will %s(%s)", (should_compact ? "compact" : "sweep"), (should_expand ? "ex" : "")));
31009     return should_compact;
31010 }
31011
31012 size_t align_lower_good_size_allocation (size_t size)
31013 {
31014     return (size/64)*64;
31015 }
31016
31017 size_t gc_heap::approximate_new_allocation()
31018 {
31019     dynamic_data* dd0 = dynamic_data_of (0);
31020     return max (2*dd_min_size (dd0), ((dd_desired_allocation (dd0)*2)/3));
31021 }
31022
31023 BOOL gc_heap::sufficient_space_end_seg (uint8_t* start, uint8_t* seg_end, size_t end_space_required, gc_tuning_point tp)
31024 {
31025     BOOL can_fit = FALSE;
31026     size_t end_seg_space = (size_t)(seg_end - start);
31027     if (end_seg_space > end_space_required)
31028     {
31029         // If hard limit is specified, and if we attributed all that's left in commit to the ephemeral seg
31030         // so we treat that as segment end, do we have enough space.
31031         if (heap_hard_limit)
31032         {
31033             size_t left_in_commit = heap_hard_limit - current_total_committed;
31034             int num_heaps = 1;
31035 #ifdef MULTIPLE_HEAPS
31036             num_heaps = n_heaps;
31037 #endif //MULTIPLE_HEAPS
31038             left_in_commit /= num_heaps;
31039             if (left_in_commit > end_space_required)
31040             {
31041                 can_fit = TRUE;
31042             }
31043
31044             dprintf (2, ("h%d end seg %Id, but only %Id left in HARD LIMIT commit, required: %Id %s on eph (%d)",
31045                 heap_number, end_seg_space, 
31046                 left_in_commit, end_space_required, 
31047                 (can_fit ? "ok" : "short"), (int)tp));
31048         }
31049         else
31050             can_fit = TRUE;
31051     }
31052
31053     return can_fit;
31054 }
31055
31056 // After we did a GC we expect to have at least this 
31057 // much space at the end of the segment to satisfy
31058 // a reasonable amount of allocation requests.
31059 size_t gc_heap::end_space_after_gc()
31060 {
31061     return max ((dd_min_size (dynamic_data_of (0))/2), (END_SPACE_AFTER_GC + Align (min_obj_size)));
31062 }
31063
31064 BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp)
31065 {
31066     uint8_t* start = 0;
31067
31068     if ((tp == tuning_deciding_condemned_gen) ||
31069         (tp == tuning_deciding_compaction))
31070     {
31071         start = (settings.concurrent ? alloc_allocated : heap_segment_allocated (ephemeral_heap_segment));
31072         if (settings.concurrent)
31073         {
31074             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (alloc_allocated)", 
31075                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
31076         }
31077         else
31078         {
31079             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (allocated)", 
31080                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment))));
31081         }
31082     }
31083     else if (tp == tuning_deciding_expansion)
31084     {
31085         start = heap_segment_plan_allocated (ephemeral_heap_segment);
31086         dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment based on plan", 
31087             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - start)));
31088     }
31089     else
31090     {
31091         assert (tp == tuning_deciding_full_gc);
31092         dprintf (GTC_LOG, ("FGC: %Id left at the end of ephemeral segment (alloc_allocated)", 
31093             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
31094         start = alloc_allocated;
31095     }
31096     
31097     if (start == 0) // empty ephemeral generations
31098     {
31099         assert (tp == tuning_deciding_expansion);
31100         // if there are no survivors in the ephemeral segment, 
31101         // this should be the beginning of ephemeral segment.
31102         start = generation_allocation_pointer (generation_of (max_generation));
31103         assert (start == heap_segment_mem (ephemeral_heap_segment));
31104     }
31105
31106     if (tp == tuning_deciding_expansion)
31107     {
31108         assert (settings.condemned_generation >= (max_generation-1));
31109         size_t gen0size = approximate_new_allocation();
31110         size_t eph_size = gen0size;
31111         size_t gen_min_sizes = 0;
31112
31113         for (int j = 1; j <= max_generation-1; j++)
31114         {
31115             gen_min_sizes += 2*dd_min_size (dynamic_data_of(j));
31116         }
31117
31118         eph_size += gen_min_sizes;
31119
31120         dprintf (3, ("h%d deciding on expansion, need %Id (gen0: %Id, 2*min: %Id)", 
31121             heap_number, gen0size, gen_min_sizes, eph_size));
31122         
31123         // We must find room for one large object and enough room for gen0size
31124         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > eph_size)
31125         {
31126             dprintf (3, ("Enough room before end of segment"));
31127             return TRUE;
31128         }
31129         else
31130         {
31131             size_t room = align_lower_good_size_allocation
31132                 (heap_segment_reserved (ephemeral_heap_segment) - start);
31133             size_t end_seg = room;
31134
31135             //look at the plug free space
31136             size_t largest_alloc = END_SPACE_AFTER_GC + Align (min_obj_size);
31137             bool large_chunk_found = FALSE;
31138             size_t bos = 0;
31139             uint8_t* gen0start = generation_plan_allocation_start (youngest_generation);
31140             dprintf (3, ("ephemeral_gen_fit_p: gen0 plan start: %Ix", (size_t)gen0start));
31141             if (gen0start == 0)
31142                 return FALSE;
31143             dprintf (3, ("ephemeral_gen_fit_p: room before free list search %Id, needed: %Id",
31144                          room, gen0size));
31145             while ((bos < mark_stack_bos) &&
31146                    !((room >= gen0size) && large_chunk_found))
31147             {
31148                 uint8_t* plug = pinned_plug (pinned_plug_of (bos));
31149                 if (in_range_for_segment (plug, ephemeral_heap_segment))
31150                 {
31151                     if (plug >= gen0start)
31152                     {
31153                         size_t chunk = align_lower_good_size_allocation (pinned_len (pinned_plug_of (bos)));
31154                         room += chunk;
31155                         if (!large_chunk_found)
31156                         {
31157                             large_chunk_found = (chunk >= largest_alloc);
31158                         }
31159                         dprintf (3, ("ephemeral_gen_fit_p: room now %Id, large chunk: %Id",
31160                                      room, large_chunk_found));
31161                     }
31162                 }
31163                 bos++;
31164             }
31165
31166             if (room >= gen0size)
31167             {
31168                 if (large_chunk_found)
31169                 {
31170                     sufficient_gen0_space_p = TRUE;
31171
31172                     dprintf (3, ("Enough room"));
31173                     return TRUE;
31174                 }
31175                 else
31176                 {
31177                     // now we need to find largest_alloc at the end of the segment.
31178                     if (end_seg >= end_space_after_gc())
31179                     {
31180                         dprintf (3, ("Enough room (may need end of seg)"));
31181                         return TRUE;
31182                     }
31183                 }
31184             }
31185
31186             dprintf (3, ("Not enough room"));
31187                 return FALSE;
31188         }
31189     }
31190     else
31191     {
31192         size_t end_space = 0;
31193         dynamic_data* dd = dynamic_data_of (0);
31194         if ((tp == tuning_deciding_condemned_gen) ||
31195             (tp == tuning_deciding_full_gc))
31196         {
31197             end_space = max (2*dd_min_size (dd), end_space_after_gc());
31198         }
31199         else
31200         {
31201             assert (tp == tuning_deciding_compaction);
31202             end_space = approximate_new_allocation();
31203         }
31204
31205         BOOL can_fit = sufficient_space_end_seg (start, heap_segment_reserved (ephemeral_heap_segment), end_space, tp);
31206
31207         return can_fit;
31208     }
31209 }
31210
31211 CObjectHeader* gc_heap::allocate_large_object (size_t jsize, int64_t& alloc_bytes)
31212 {
31213     //create a new alloc context because gen3context is shared.
31214     alloc_context acontext;
31215     acontext.alloc_ptr = 0;
31216     acontext.alloc_limit = 0;
31217     acontext.alloc_bytes = 0;
31218 #ifdef MULTIPLE_HEAPS
31219     acontext.set_alloc_heap(vm_heap);
31220 #endif //MULTIPLE_HEAPS
31221
31222 #if BIT64
31223     size_t maxObjectSize = (INT64_MAX - 7 - Align(min_obj_size));
31224 #else
31225     size_t maxObjectSize = (INT32_MAX - 7 - Align(min_obj_size));
31226 #endif
31227
31228     if (jsize >= maxObjectSize)
31229     {
31230         if (GCConfig::GetBreakOnOOM())
31231         {
31232             GCToOSInterface::DebugBreak();
31233         }
31234         return NULL;
31235     }
31236
31237     size_t size = AlignQword (jsize);
31238     int align_const = get_alignment_constant (FALSE);
31239 #ifdef FEATURE_LOH_COMPACTION
31240     size_t pad = Align (loh_padding_obj_size, align_const);
31241 #else
31242     size_t pad = 0;
31243 #endif //FEATURE_LOH_COMPACTION
31244
31245     assert (size >= Align (min_obj_size, align_const));
31246 #ifdef _MSC_VER
31247 #pragma inline_depth(0)
31248 #endif //_MSC_VER
31249     if (! allocate_more_space (&acontext, (size + pad), max_generation+1))
31250     {
31251         return 0;
31252     }
31253
31254 #ifdef _MSC_VER
31255 #pragma inline_depth(20)
31256 #endif //_MSC_VER
31257
31258 #ifdef MARK_ARRAY
31259     uint8_t* current_lowest_address = lowest_address;
31260     uint8_t* current_highest_address = highest_address;
31261 #ifdef BACKGROUND_GC
31262     if (recursive_gc_sync::background_running_p())
31263     {
31264         current_lowest_address = background_saved_lowest_address;
31265         current_highest_address = background_saved_highest_address;
31266     }
31267 #endif //BACKGROUND_GC
31268 #endif // MARK_ARRAY
31269
31270 #ifdef FEATURE_LOH_COMPACTION
31271     // The GC allocator made a free object already in this alloc context and
31272     // adjusted the alloc_ptr accordingly.
31273 #endif //FEATURE_LOH_COMPACTION
31274
31275     uint8_t*  result = acontext.alloc_ptr;
31276
31277     assert ((size_t)(acontext.alloc_limit - acontext.alloc_ptr) == size);
31278     alloc_bytes += size;
31279
31280     CObjectHeader* obj = (CObjectHeader*)result;
31281
31282 #ifdef MARK_ARRAY
31283     if (recursive_gc_sync::background_running_p())
31284     {
31285         if ((result < current_highest_address) && (result >= current_lowest_address))
31286         {
31287             dprintf (3, ("Clearing mark bit at address %Ix",
31288                      (size_t)(&mark_array [mark_word_of (result)])));
31289
31290             mark_array_clear_marked (result);
31291         }
31292 #ifdef BACKGROUND_GC
31293         //the object has to cover one full mark uint32_t
31294         assert (size > mark_word_size);
31295         if (current_c_gc_state != c_gc_state_free)
31296         {
31297             dprintf (3, ("Concurrent allocation of a large object %Ix",
31298                         (size_t)obj));
31299             //mark the new block specially so we know it is a new object
31300             if ((result < current_highest_address) && (result >= current_lowest_address))
31301             {
31302                 dprintf (3, ("Setting mark bit at address %Ix",
31303                             (size_t)(&mark_array [mark_word_of (result)])));
31304     
31305                 mark_array_set_marked (result);
31306             }
31307         }
31308 #endif //BACKGROUND_GC
31309     }
31310 #endif //MARK_ARRAY
31311
31312     assert (obj != 0);
31313     assert ((size_t)obj == Align ((size_t)obj, align_const));
31314
31315     return obj;
31316 }
31317
31318 void reset_memory (uint8_t* o, size_t sizeo)
31319 {
31320     if (sizeo > 128 * 1024)
31321     {
31322         // We cannot reset the memory for the useful part of a free object.
31323         size_t size_to_skip = min_free_list - plug_skew;
31324
31325         size_t page_start = align_on_page ((size_t)(o + size_to_skip));
31326         size_t size = align_lower_page ((size_t)o + sizeo - size_to_skip - plug_skew) - page_start;
31327         // Note we need to compensate for an OS bug here. This bug would cause the MEM_RESET to fail
31328         // on write watched memory.
31329         if (reset_mm_p)
31330         {
31331 #ifdef MULTIPLE_HEAPS
31332             bool unlock_p = true;
31333 #else
31334             // We don't do unlock because there could be many processes using workstation GC and it's
31335             // bad perf to have many threads doing unlock at the same time.
31336             bool unlock_p = false;
31337 #endif //MULTIPLE_HEAPS
31338
31339             reset_mm_p = GCToOSInterface::VirtualReset((void*)page_start, size, unlock_p);
31340         }
31341     }
31342 }
31343
31344 void gc_heap::reset_large_object (uint8_t* o)
31345 {
31346     // If it's a large object, allow the O/S to discard the backing store for these pages.
31347     reset_memory (o, size(o));
31348 }
31349
31350 BOOL gc_heap::large_object_marked (uint8_t* o, BOOL clearp)
31351 {
31352     BOOL m = FALSE;
31353     // It shouldn't be necessary to do these comparisons because this is only used for blocking
31354     // GCs and LOH segments cannot be out of range.
31355     if ((o >= lowest_address) && (o < highest_address))
31356     {
31357         if (marked (o))
31358         {
31359             if (clearp)
31360             {
31361                 clear_marked (o);
31362                 if (pinned (o))
31363                     clear_pinned(o);
31364             }
31365             m = TRUE;
31366         }
31367         else
31368             m = FALSE;
31369     }
31370     else
31371         m = TRUE;
31372     return m;
31373 }
31374
31375 void gc_heap::walk_survivors_relocation (void* profiling_context, record_surv_fn fn)
31376 {
31377     // Now walk the portion of memory that is actually being relocated.
31378     walk_relocation (profiling_context, fn);
31379
31380 #ifdef FEATURE_LOH_COMPACTION
31381     if (loh_compacted_p)
31382     {
31383         walk_relocation_for_loh (profiling_context, fn);
31384     }
31385 #endif //FEATURE_LOH_COMPACTION
31386 }
31387
31388 void gc_heap::walk_survivors_for_loh (void* profiling_context, record_surv_fn fn)
31389 {
31390     generation* gen        = large_object_generation;
31391     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));;
31392
31393     PREFIX_ASSUME(seg != NULL);
31394
31395     uint8_t* o                = generation_allocation_start (gen);
31396     uint8_t* plug_end         = o;
31397     uint8_t* plug_start       = o;
31398
31399     while (1)
31400     {
31401         if (o >= heap_segment_allocated (seg))
31402         {
31403             seg = heap_segment_next (seg);
31404             if (seg == 0)
31405                 break;
31406             else
31407                 o = heap_segment_mem (seg);
31408         }
31409         if (large_object_marked(o, FALSE))
31410         {
31411             plug_start = o;
31412
31413             BOOL m = TRUE;
31414             while (m)
31415             {
31416                 o = o + AlignQword (size (o));
31417                 if (o >= heap_segment_allocated (seg))
31418                 {
31419                     break;
31420                 }
31421                 m = large_object_marked (o, FALSE);
31422             }
31423
31424             plug_end = o;
31425
31426             fn (plug_start, plug_end, 0, profiling_context, false, false);
31427         }
31428         else
31429         {
31430             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
31431             {
31432                 o = o + AlignQword (size (o));
31433             }
31434         }
31435     }
31436 }
31437
31438 #ifdef BACKGROUND_GC
31439
31440 BOOL gc_heap::background_object_marked (uint8_t* o, BOOL clearp)
31441 {
31442     BOOL m = FALSE;
31443     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
31444     {
31445         if (mark_array_marked (o))
31446         {
31447             if (clearp)
31448             {
31449                 mark_array_clear_marked (o);
31450                 //dprintf (3, ("mark array bit for object %Ix is cleared", o));
31451                 dprintf (3, ("CM: %Ix", o));
31452             }
31453             m = TRUE;
31454         }
31455         else
31456             m = FALSE;
31457     }
31458     else
31459         m = TRUE;
31460
31461     dprintf (3, ("o %Ix(%d) %s", o, size(o), (m ? "was bm" : "was NOT bm")));
31462     return m;
31463 }
31464
31465 void gc_heap::background_delay_delete_loh_segments()
31466 {
31467     generation* gen = large_object_generation;
31468     heap_segment* seg = heap_segment_rw (generation_start_segment (large_object_generation));
31469     heap_segment* prev_seg = 0;
31470
31471     while (seg)
31472     {
31473         heap_segment* next_seg = heap_segment_next (seg);
31474         if (seg->flags & heap_segment_flags_loh_delete)
31475         {
31476             dprintf (3, ("deleting %Ix-%Ix-%Ix", (size_t)seg, heap_segment_allocated (seg), heap_segment_reserved (seg)));
31477             delete_heap_segment (seg, (GCConfig::GetRetainVM() != 0));
31478             heap_segment_next (prev_seg) = next_seg;
31479         }
31480         else
31481         {
31482             prev_seg = seg;
31483         }
31484
31485         seg = next_seg;
31486     }
31487 }
31488
31489 uint8_t* gc_heap::background_next_end (heap_segment* seg, BOOL large_objects_p)
31490 {
31491     return
31492         (large_objects_p ? heap_segment_allocated (seg) : heap_segment_background_allocated (seg));
31493 }
31494
31495 void gc_heap::set_mem_verify (uint8_t* start, uint8_t* end, uint8_t b)
31496 {
31497 #ifdef VERIFY_HEAP
31498     if (end > start)
31499     {
31500         if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
31501            !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_MEM_FILL))
31502         {
31503             dprintf (3, ("setting mem to %c [%Ix, [%Ix", b, start, end));
31504             memset (start, b, (end - start));
31505         }
31506     }
31507 #endif //VERIFY_HEAP
31508 }
31509
31510 void gc_heap::generation_delete_heap_segment (generation* gen, 
31511                                               heap_segment* seg,
31512                                               heap_segment* prev_seg,
31513                                               heap_segment* next_seg)
31514 {
31515     dprintf (3, ("bgc sweep: deleting seg %Ix", seg));
31516     if (gen == large_object_generation)
31517     {
31518         dprintf (3, ("Preparing empty large segment %Ix for deletion", (size_t)seg));
31519
31520         // We cannot thread segs in here onto freeable_large_heap_segment because 
31521         // grow_brick_card_tables could be committing mark array which needs to read 
31522         // the seg list. So we delay it till next time we suspend EE.
31523         seg->flags |= heap_segment_flags_loh_delete;
31524         // Since we will be decommitting the seg, we need to prevent heap verification
31525         // to verify this segment.
31526         heap_segment_allocated (seg) = heap_segment_mem (seg);
31527     }
31528     else
31529     {
31530         if (seg == ephemeral_heap_segment)
31531         {
31532             FATAL_GC_ERROR();
31533         }
31534
31535         heap_segment_next (next_seg) = prev_seg;
31536
31537         dprintf (3, ("Preparing empty small segment %Ix for deletion", (size_t)seg));
31538         heap_segment_next (seg) = freeable_small_heap_segment;
31539         freeable_small_heap_segment = seg;
31540     }
31541
31542     decommit_heap_segment (seg);
31543     seg->flags |= heap_segment_flags_decommitted;
31544
31545     set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
31546 }
31547
31548 void gc_heap::process_background_segment_end (heap_segment* seg, 
31549                                           generation* gen,
31550                                           uint8_t* last_plug_end,
31551                                           heap_segment* start_seg,
31552                                           BOOL* delete_p)
31553 {
31554     *delete_p = FALSE;
31555     uint8_t* allocated = heap_segment_allocated (seg);
31556     uint8_t* background_allocated = heap_segment_background_allocated (seg);
31557     BOOL loh_p = heap_segment_loh_p (seg);
31558
31559     dprintf (3, ("Processing end of background segment [%Ix, %Ix[(%Ix[)", 
31560                 (size_t)heap_segment_mem (seg), background_allocated, allocated));
31561
31562     if (!loh_p && (allocated != background_allocated))
31563     {
31564         assert (gen != large_object_generation);
31565
31566         dprintf (3, ("Make a free object before newly promoted objects [%Ix, %Ix[", 
31567                     (size_t)last_plug_end, background_allocated));
31568         thread_gap (last_plug_end, background_allocated - last_plug_end, generation_of (max_generation));
31569
31570
31571         fix_brick_to_highest (last_plug_end, background_allocated);
31572
31573         // When we allowed fgc's during going through gaps, we could have erased the brick
31574         // that corresponds to bgc_allocated 'cause we had to update the brick there, 
31575         // recover it here.
31576         fix_brick_to_highest (background_allocated, background_allocated);
31577     }
31578     else
31579     {
31580         // by default, if allocated == background_allocated, it can't
31581         // be the ephemeral segment.
31582         if (seg == ephemeral_heap_segment)
31583         {
31584             FATAL_GC_ERROR();
31585         }
31586
31587         if (allocated == heap_segment_mem (seg))
31588         {
31589             // this can happen with LOH segments when multiple threads
31590             // allocate new segments and not all of them were needed to
31591             // satisfy allocation requests.
31592             assert (gen == large_object_generation);
31593         }
31594
31595         if (last_plug_end == heap_segment_mem (seg))
31596         {
31597             dprintf (3, ("Segment allocated is %Ix (beginning of this seg) - %s be deleted",
31598                         (size_t)allocated, (*delete_p ? "should" : "should not")));
31599
31600             if (seg != start_seg)
31601             {
31602                 *delete_p = TRUE;
31603             }
31604         }
31605         else
31606         {
31607             dprintf (3, ("Trimming seg to %Ix[", (size_t)last_plug_end));
31608             heap_segment_allocated (seg) = last_plug_end;
31609             set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
31610
31611             decommit_heap_segment_pages (seg, 0);
31612         }
31613     }
31614
31615     dprintf (3, ("verifying seg %Ix's mark array was completely cleared", seg));
31616     bgc_verify_mark_array_cleared (seg);
31617 }
31618
31619 void gc_heap::process_n_background_segments (heap_segment* seg, 
31620                                              heap_segment* prev_seg,
31621                                              generation* gen)
31622 {
31623     assert (gen != large_object_generation);
31624
31625     while (seg)
31626     {
31627         dprintf (2, ("processing seg %Ix (not seen by bgc mark)", seg));
31628         heap_segment* next_seg = heap_segment_next (seg);
31629
31630         if (heap_segment_read_only_p (seg))
31631         {
31632             prev_seg = seg;
31633         }
31634         else
31635         {
31636             if (heap_segment_allocated (seg) == heap_segment_mem (seg))
31637             {
31638                 // This can happen - if we have a LOH segment where nothing survived
31639                 // or a SOH segment allocated by a gen1 GC when BGC was going where 
31640                 // nothing survived last time we did a gen1 GC.
31641                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
31642             }
31643             else
31644             {
31645                 prev_seg = seg;
31646             }
31647         }
31648
31649         verify_soh_segment_list();
31650         seg = next_seg;
31651     }
31652 }
31653
31654 inline
31655 BOOL gc_heap::fgc_should_consider_object (uint8_t* o,
31656                                           heap_segment* seg,
31657                                           BOOL consider_bgc_mark_p, 
31658                                           BOOL check_current_sweep_p, 
31659                                           BOOL check_saved_sweep_p)
31660 {
31661     // the logic for this function must be kept in sync with the analogous function
31662     // in ToolBox\SOS\Strike\gc.cpp
31663
31664     // TRUE means we don't need to check the bgc mark bit
31665     // FALSE means we do.
31666     BOOL no_bgc_mark_p = FALSE;
31667
31668     if (consider_bgc_mark_p)
31669     {
31670         if (check_current_sweep_p && (o < current_sweep_pos))
31671         {
31672             dprintf (3, ("no bgc mark - o: %Ix < cs: %Ix", o, current_sweep_pos));
31673             no_bgc_mark_p = TRUE;
31674         }
31675
31676         if (!no_bgc_mark_p)
31677         {
31678             if(check_saved_sweep_p && (o >= saved_sweep_ephemeral_start))
31679             {
31680                 dprintf (3, ("no bgc mark - o: %Ix >= ss: %Ix", o, saved_sweep_ephemeral_start));
31681                 no_bgc_mark_p = TRUE;
31682             }
31683
31684             if (!check_saved_sweep_p)
31685             {
31686                 uint8_t* background_allocated = heap_segment_background_allocated (seg);
31687                 // if this was the saved ephemeral segment, check_saved_sweep_p 
31688                 // would've been true.
31689                 assert (heap_segment_background_allocated (seg) != saved_sweep_ephemeral_start);
31690                 // background_allocated could be 0 for the new segments acquired during bgc
31691                 // sweep and we still want no_bgc_mark_p to be true.
31692                 if (o >= background_allocated)
31693                 {
31694                     dprintf (3, ("no bgc mark - o: %Ix >= ba: %Ix", o, background_allocated));
31695                     no_bgc_mark_p = TRUE;
31696                 }
31697             }
31698         }
31699     }
31700     else
31701     {
31702         no_bgc_mark_p = TRUE;
31703     }
31704
31705     dprintf (3, ("bgc mark %Ix: %s (bm: %s)", o, (no_bgc_mark_p ? "no" : "yes"), (background_object_marked (o, FALSE) ? "yes" : "no")));
31706     return (no_bgc_mark_p ? TRUE : background_object_marked (o, FALSE));
31707 }
31708
31709 // consider_bgc_mark_p tells you if you need to care about the bgc mark bit at all
31710 // if it's TRUE, check_current_sweep_p tells you if you should consider the
31711 // current sweep position or not.
31712 void gc_heap::should_check_bgc_mark (heap_segment* seg, 
31713                                      BOOL* consider_bgc_mark_p, 
31714                                      BOOL* check_current_sweep_p,
31715                                      BOOL* check_saved_sweep_p)
31716 {
31717     // the logic for this function must be kept in sync with the analogous function
31718     // in ToolBox\SOS\Strike\gc.cpp
31719     *consider_bgc_mark_p = FALSE;
31720     *check_current_sweep_p = FALSE;
31721     *check_saved_sweep_p = FALSE;
31722
31723     if (current_c_gc_state == c_gc_state_planning)
31724     {
31725         // We are doing the current_sweep_pos comparison here because we have yet to 
31726         // turn on the swept flag for the segment but in_range_for_segment will return
31727         // FALSE if the address is the same as reserved.
31728         if ((seg->flags & heap_segment_flags_swept) || (current_sweep_pos == heap_segment_reserved (seg)))
31729         {
31730             dprintf (3, ("seg %Ix is already swept by bgc", seg));
31731         }
31732         else
31733         {
31734             *consider_bgc_mark_p = TRUE;
31735
31736             dprintf (3, ("seg %Ix hasn't been swept by bgc", seg));
31737
31738             if (seg == saved_sweep_ephemeral_seg)
31739             {
31740                 dprintf (3, ("seg %Ix is the saved ephemeral seg", seg));
31741                 *check_saved_sweep_p = TRUE;
31742             }
31743
31744             if (in_range_for_segment (current_sweep_pos, seg))
31745             {
31746                 dprintf (3, ("current sweep pos is %Ix and within seg %Ix", 
31747                               current_sweep_pos, seg));
31748                 *check_current_sweep_p = TRUE;
31749             }
31750         }
31751     }
31752 }
31753
31754 void gc_heap::background_ephemeral_sweep()
31755 {
31756     dprintf (3, ("bgc ephemeral sweep"));
31757
31758     int align_const = get_alignment_constant (TRUE);
31759
31760     saved_sweep_ephemeral_seg = ephemeral_heap_segment;
31761     saved_sweep_ephemeral_start = generation_allocation_start (generation_of (max_generation - 1));
31762
31763     // Since we don't want to interfere with gen0 allocation while we are threading gen0 free list,
31764     // we thread onto a list first then publish it when we are done.
31765     allocator youngest_free_list;
31766     size_t youngest_free_list_space = 0;
31767     size_t youngest_free_obj_space = 0;
31768
31769     youngest_free_list.clear();
31770
31771     for (int i = 0; i <= (max_generation - 1); i++)
31772     {
31773         generation* gen_to_reset = generation_of (i);
31774         assert (generation_free_list_space (gen_to_reset) == 0);
31775         // Can only assert free_list_space is 0, not free_obj_space as the allocator could have added 
31776         // something there.
31777     }
31778
31779     for (int i = (max_generation - 1); i >= 0; i--)
31780     {
31781         generation* current_gen = generation_of (i);
31782         uint8_t* o = generation_allocation_start (current_gen);
31783         //Skip the generation gap object
31784         o = o + Align(size (o), align_const);
31785         uint8_t* end = ((i > 0) ?
31786                      generation_allocation_start (generation_of (i - 1)) : 
31787                      heap_segment_allocated (ephemeral_heap_segment));
31788
31789         uint8_t* plug_end = o;
31790         uint8_t* plug_start = o;
31791         BOOL marked_p = FALSE;
31792
31793         while (o < end)
31794         {
31795             marked_p = background_object_marked (o, TRUE);
31796             if (marked_p)
31797             {
31798                 plug_start = o;
31799                 size_t plug_size = plug_start - plug_end;
31800
31801                 if (i >= 1)
31802                 {
31803                     thread_gap (plug_end, plug_size, current_gen);
31804                 }
31805                 else
31806                 {
31807                     if (plug_size > 0)
31808                     {
31809                         make_unused_array (plug_end, plug_size);
31810                         if (plug_size >= min_free_list)
31811                         {
31812                             youngest_free_list_space += plug_size;
31813                             youngest_free_list.thread_item (plug_end, plug_size);
31814                         }
31815                         else
31816                         {
31817                             youngest_free_obj_space += plug_size;
31818                         }
31819                     }
31820                 }
31821
31822                 fix_brick_to_highest (plug_end, plug_start);
31823                 fix_brick_to_highest (plug_start, plug_start);
31824
31825                 BOOL m = TRUE;
31826                 while (m)
31827                 {
31828                     o = o + Align (size (o), align_const);
31829                     if (o >= end)
31830                     {
31831                         break;
31832                     }
31833
31834                     m = background_object_marked (o, TRUE);
31835                 }
31836                 plug_end = o;
31837                 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31838             }
31839             else
31840             {
31841                 while ((o < end) && !background_object_marked (o, FALSE))
31842                 {
31843                     o = o + Align (size (o), align_const);
31844                 }
31845             }
31846         }
31847
31848         if (plug_end != end)
31849         {
31850             if (i >= 1)
31851             {
31852                 thread_gap (plug_end, end - plug_end, current_gen);
31853                 fix_brick_to_highest (plug_end, end);
31854             }
31855             else
31856             {
31857                 heap_segment_allocated (ephemeral_heap_segment) = plug_end;
31858                 // the following line is temporary.
31859                 heap_segment_saved_bg_allocated (ephemeral_heap_segment) = plug_end;
31860 #ifdef VERIFY_HEAP
31861                 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
31862                 {
31863                     make_unused_array (plug_end, (end - plug_end));
31864                 }
31865 #endif //VERIFY_HEAP
31866             }
31867         }
31868
31869         dd_fragmentation (dynamic_data_of (i)) = 
31870             generation_free_list_space (current_gen) + generation_free_obj_space (current_gen);
31871     }
31872
31873     generation* youngest_gen = generation_of (0);
31874     generation_free_list_space (youngest_gen) = youngest_free_list_space;
31875     generation_free_obj_space (youngest_gen) = youngest_free_obj_space;
31876     dd_fragmentation (dynamic_data_of (0)) = youngest_free_list_space + youngest_free_obj_space;
31877     generation_allocator (youngest_gen)->copy_with_no_repair (&youngest_free_list);
31878 }
31879
31880 void gc_heap::background_sweep()
31881 {
31882     generation* gen         = generation_of (max_generation);
31883     dynamic_data* dd        = dynamic_data_of (max_generation);
31884     // For SOH segments we go backwards.
31885     heap_segment* start_seg = ephemeral_heap_segment;
31886     PREFIX_ASSUME(start_seg != NULL);
31887     heap_segment* fseg      = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31888     heap_segment* seg       = start_seg;
31889     uint8_t* o                 = heap_segment_mem (seg);
31890
31891     heap_segment* prev_seg = heap_segment_next (seg);
31892     int align_const        = get_alignment_constant (TRUE);
31893     if (seg == fseg)
31894     {
31895         assert (o == generation_allocation_start (generation_of (max_generation)));
31896         o = o + Align(size (o), align_const);
31897     }
31898
31899     uint8_t* plug_end      = o;
31900     uint8_t* plug_start    = o;
31901     next_sweep_obj         = o;
31902     current_sweep_pos      = o;
31903
31904     //uint8_t* end              = background_next_end (seg, (gen == large_object_generation));
31905     uint8_t* end              = heap_segment_background_allocated (seg);
31906     BOOL delete_p          = FALSE;
31907
31908     //concurrent_print_time_delta ("finished with mark and start with sweep");
31909     concurrent_print_time_delta ("Sw");
31910     dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31911
31912     //block concurrent allocation for large objects
31913     dprintf (3, ("lh state: planning"));
31914     if (gc_lh_block_event.IsValid())
31915     {
31916         gc_lh_block_event.Reset();
31917     }
31918
31919     for (int i = 0; i <= (max_generation + 1); i++)
31920     {
31921         generation* gen_to_reset = generation_of (i);
31922         generation_allocator (gen_to_reset)->clear();
31923         generation_free_list_space (gen_to_reset) = 0;
31924         generation_free_obj_space (gen_to_reset) = 0;
31925         generation_free_list_allocated (gen_to_reset) = 0;
31926         generation_end_seg_allocated (gen_to_reset) = 0;
31927         generation_condemned_allocated (gen_to_reset) = 0; 
31928         //reset the allocation so foreground gc can allocate into older generation
31929         generation_allocation_pointer (gen_to_reset)= 0;
31930         generation_allocation_limit (gen_to_reset) = 0;
31931         generation_allocation_segment (gen_to_reset) = heap_segment_rw (generation_start_segment (gen_to_reset));
31932     }
31933
31934     FIRE_EVENT(BGC2ndNonConEnd);
31935
31936     loh_alloc_thread_count = 0;
31937     current_bgc_state = bgc_sweep_soh;
31938     verify_soh_segment_list();
31939
31940 #ifdef FEATURE_BASICFREEZE
31941     if ((generation_start_segment (gen) != ephemeral_heap_segment) &&
31942         ro_segments_in_range)
31943     {
31944         sweep_ro_segments (generation_start_segment (gen));
31945     }
31946 #endif // FEATURE_BASICFREEZE
31947
31948     //TODO BACKGROUND_GC: can we move this to where we switch to the LOH?
31949     if (current_c_gc_state != c_gc_state_planning)
31950     {
31951         current_c_gc_state = c_gc_state_planning;
31952     }
31953
31954     concurrent_print_time_delta ("Swe");
31955
31956     heap_segment* loh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation + 1)));
31957     PREFIX_ASSUME(loh_seg  != NULL);
31958     while (loh_seg )
31959     {
31960         loh_seg->flags &= ~heap_segment_flags_swept;
31961         heap_segment_background_allocated (loh_seg) = heap_segment_allocated (loh_seg);
31962         loh_seg = heap_segment_next_rw (loh_seg);
31963     }
31964
31965 #ifdef MULTIPLE_HEAPS
31966     bgc_t_join.join(this, gc_join_restart_ee);
31967     if (bgc_t_join.joined())
31968 #endif //MULTIPLE_HEAPS 
31969     {
31970 #ifdef MULTIPLE_HEAPS
31971         dprintf(2, ("Starting BGC threads for resuming EE"));
31972         bgc_t_join.restart();
31973 #endif //MULTIPLE_HEAPS
31974     }
31975
31976     if (heap_number == 0)
31977     {
31978         restart_EE ();
31979     }
31980
31981     FIRE_EVENT(BGC2ndConBegin);
31982
31983     background_ephemeral_sweep();
31984
31985     concurrent_print_time_delta ("Swe eph");
31986
31987 #ifdef MULTIPLE_HEAPS
31988     bgc_t_join.join(this, gc_join_after_ephemeral_sweep);
31989     if (bgc_t_join.joined())
31990 #endif //MULTIPLE_HEAPS
31991     {
31992 #ifdef FEATURE_EVENT_TRACE
31993         bgc_heap_walk_for_etw_p = GCEventStatus::IsEnabled(GCEventProvider_Default, 
31994                                                            GCEventKeyword_GCHeapSurvivalAndMovement, 
31995                                                            GCEventLevel_Information);
31996 #endif //FEATURE_EVENT_TRACE
31997
31998         leave_spin_lock (&gc_lock);
31999
32000 #ifdef MULTIPLE_HEAPS
32001         dprintf(2, ("Starting BGC threads for BGC sweeping"));
32002         bgc_t_join.restart();
32003 #endif //MULTIPLE_HEAPS
32004     }
32005
32006     disable_preemptive (true);
32007
32008     dprintf (2, ("bgs: sweeping gen2 objects"));
32009     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
32010                     (size_t)heap_segment_mem (seg),
32011                     (size_t)heap_segment_allocated (seg),
32012                     (size_t)heap_segment_background_allocated (seg)));
32013
32014     int num_objs = 256;
32015     int current_num_objs = 0;
32016     heap_segment* next_seg = 0;
32017
32018     while (1)
32019     {
32020         if (o >= end)
32021         {
32022             if (gen == large_object_generation)
32023             {
32024                 next_seg = heap_segment_next (seg);
32025             }
32026             else
32027             {
32028                 next_seg = heap_segment_prev (fseg, seg);
32029             }
32030
32031             delete_p = FALSE;
32032
32033             if (!heap_segment_read_only_p (seg))
32034             {
32035                 if (gen == large_object_generation)
32036                 {
32037                     // we can treat all LOH segments as in the bgc domain
32038                     // regardless of whether we saw in bgc mark or not
32039                     // because we don't allow LOH allocations during bgc
32040                     // sweep anyway - the LOH segments can't change.
32041                     process_background_segment_end (seg, gen, plug_end, 
32042                                                     start_seg, &delete_p);
32043                 }
32044                 else
32045                 {
32046                     assert (heap_segment_background_allocated (seg) != 0);
32047                     process_background_segment_end (seg, gen, plug_end, 
32048                                                     start_seg, &delete_p);
32049
32050                     assert (next_seg || !delete_p);
32051                 }
32052             }
32053
32054             if (delete_p)
32055             {
32056                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
32057             }
32058             else
32059             {
32060                 prev_seg = seg;
32061                 dprintf (2, ("seg %Ix has been swept", seg));
32062                 seg->flags |= heap_segment_flags_swept;
32063             }
32064
32065             verify_soh_segment_list();
32066
32067             seg = next_seg;
32068
32069             dprintf (GTC_LOG, ("seg: %Ix, next_seg: %Ix, prev_seg: %Ix", seg, next_seg, prev_seg));
32070             
32071             if (seg == 0)
32072             {
32073                 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
32074
32075                 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
32076
32077                 if (gen != large_object_generation)
32078                 {
32079                     dprintf (2, ("bgs: sweeping gen3 objects"));
32080                     concurrent_print_time_delta ("Swe SOH");
32081                     FIRE_EVENT(BGC1stSweepEnd, 0);
32082
32083                     enter_spin_lock (&more_space_lock_loh);
32084                     add_saved_spinlock_info (true, me_acquire, mt_bgc_loh_sweep);
32085
32086                     concurrent_print_time_delta ("Swe LOH took msl");
32087
32088                     // We wait till all allocating threads are completely done.
32089                     int spin_count = yp_spin_count_unit;
32090                     while (loh_alloc_thread_count)
32091                     {
32092                         spin_and_switch (spin_count, (loh_alloc_thread_count == 0));
32093                     }
32094
32095                     current_bgc_state = bgc_sweep_loh;
32096                     gen = generation_of (max_generation+1);
32097                     start_seg = heap_segment_rw (generation_start_segment (gen));
32098
32099                     PREFIX_ASSUME(start_seg != NULL);
32100
32101                     seg = start_seg;
32102                     prev_seg = 0;
32103                     o = generation_allocation_start (gen);
32104                     assert (method_table (o) == g_gc_pFreeObjectMethodTable);
32105                     align_const = get_alignment_constant (FALSE);
32106                     o = o + Align(size (o), align_const);
32107                     plug_end = o;
32108                     end = heap_segment_allocated (seg);
32109                     dprintf (2, ("sweeping gen3 objects"));
32110                     generation_free_obj_space (gen) = 0;
32111                     generation_allocator (gen)->clear();
32112                     generation_free_list_space (gen) = 0;
32113
32114                     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
32115                                     (size_t)heap_segment_mem (seg),
32116                                     (size_t)heap_segment_allocated (seg),
32117                                     (size_t)heap_segment_background_allocated (seg)));
32118                 }
32119                 else
32120                     break;
32121             }
32122             else
32123             {
32124                 o = heap_segment_mem (seg);
32125                 if (seg == fseg)
32126                 {
32127                     assert (gen != large_object_generation);
32128                     assert (o == generation_allocation_start (generation_of (max_generation)));
32129                     align_const = get_alignment_constant (TRUE);
32130                     o = o + Align(size (o), align_const);
32131                 }
32132
32133                 plug_end = o;
32134                 current_sweep_pos = o;
32135                 next_sweep_obj = o;
32136                 
32137                 allow_fgc();
32138                 end = background_next_end (seg, (gen == large_object_generation));
32139                 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
32140                                 (size_t)heap_segment_mem (seg),
32141                                 (size_t)heap_segment_allocated (seg),
32142                                 (size_t)heap_segment_background_allocated (seg)));
32143             }
32144         }
32145
32146         if ((o < end) && background_object_marked (o, TRUE))
32147         {
32148             plug_start = o;
32149             if (gen == large_object_generation)
32150             {
32151                 dprintf (2, ("loh fr: [%Ix-%Ix[(%Id)", plug_end, plug_start, plug_start-plug_end));
32152             }
32153
32154             thread_gap (plug_end, plug_start-plug_end, gen);
32155             if (gen != large_object_generation)
32156             {
32157                 add_gen_free (max_generation, plug_start-plug_end);
32158                 fix_brick_to_highest (plug_end, plug_start);
32159                 // we need to fix the brick for the next plug here 'cause an FGC can
32160                 // happen and can't read a stale brick.
32161                 fix_brick_to_highest (plug_start, plug_start);
32162             }
32163
32164             BOOL m = TRUE;
32165
32166             while (m)
32167             {
32168                 next_sweep_obj = o + Align(size (o), align_const);
32169                 current_num_objs++;
32170                 if (current_num_objs >= num_objs)
32171                 {
32172                     current_sweep_pos = next_sweep_obj;
32173
32174                     allow_fgc();
32175                     current_num_objs = 0;
32176                 }
32177
32178                 o = next_sweep_obj;
32179                 if (o >= end)
32180                 {
32181                     break;
32182                 }
32183
32184                 m = background_object_marked (o, TRUE);
32185             }
32186             plug_end = o;
32187             if (gen != large_object_generation)
32188             {
32189                 add_gen_plug (max_generation, plug_end-plug_start);
32190                 dd_survived_size (dd) += (plug_end - plug_start);
32191             }
32192             dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
32193         }
32194         else
32195         {
32196             while ((o < end) && !background_object_marked (o, FALSE))
32197             {
32198                 next_sweep_obj = o + Align(size (o), align_const);;
32199                 current_num_objs++;
32200                 if (current_num_objs >= num_objs)
32201                 {
32202                     current_sweep_pos = plug_end;
32203                     dprintf (1234, ("f: swept till %Ix", current_sweep_pos));
32204                     allow_fgc();
32205                     current_num_objs = 0;
32206                 }
32207
32208                 o = next_sweep_obj;
32209             }
32210         }
32211     }
32212
32213     size_t total_loh_size = generation_size (max_generation + 1);
32214     size_t total_soh_size = generation_sizes (generation_of (max_generation));
32215
32216     dprintf (GTC_LOG, ("h%d: S: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
32217
32218     dprintf (GTC_LOG, ("end of bgc sweep: gen2 FL: %Id, FO: %Id", 
32219         generation_free_list_space (generation_of (max_generation)),
32220         generation_free_obj_space (generation_of (max_generation))));
32221     dprintf (GTC_LOG, ("h%d: end of bgc sweep: gen3 FL: %Id, FO: %Id", 
32222         heap_number,
32223         generation_free_list_space (generation_of (max_generation + 1)),
32224         generation_free_obj_space (generation_of (max_generation + 1))));
32225
32226     FIRE_EVENT(BGC2ndConEnd);
32227     concurrent_print_time_delta ("background sweep");
32228     
32229     heap_segment* reset_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
32230     PREFIX_ASSUME(reset_seg != NULL);
32231
32232     while (reset_seg)
32233     {
32234         heap_segment_saved_bg_allocated (reset_seg) = heap_segment_background_allocated (reset_seg);
32235         heap_segment_background_allocated (reset_seg) = 0;
32236         reset_seg = heap_segment_next_rw (reset_seg);
32237     }
32238
32239     generation* loh_gen = generation_of (max_generation + 1);
32240     generation_allocation_segment (loh_gen) = heap_segment_rw (generation_start_segment (loh_gen));
32241
32242     // We calculate dynamic data here because if we wait till we signal the lh event, 
32243     // the allocation thread can change the fragmentation and we may read an intermediate
32244     // value (which can be greater than the generation size). Plus by that time it won't 
32245     // be accurate.
32246     compute_new_dynamic_data (max_generation);
32247
32248     enable_preemptive ();
32249
32250 #ifdef MULTIPLE_HEAPS
32251     bgc_t_join.join(this, gc_join_set_state_free);
32252     if (bgc_t_join.joined())
32253 #endif //MULTIPLE_HEAPS
32254     {
32255         // TODO: We are using this join just to set the state. Should
32256         // look into eliminating it - check to make sure things that use 
32257         // this state can live with per heap state like should_check_bgc_mark.
32258         current_c_gc_state = c_gc_state_free;
32259
32260 #ifdef MULTIPLE_HEAPS
32261         dprintf(2, ("Starting BGC threads after background sweep phase"));
32262         bgc_t_join.restart();
32263 #endif //MULTIPLE_HEAPS
32264     }
32265
32266     disable_preemptive (true);
32267
32268     if (gc_lh_block_event.IsValid())
32269     {
32270         gc_lh_block_event.Set();
32271     }
32272
32273     add_saved_spinlock_info (true, me_release, mt_bgc_loh_sweep);
32274     leave_spin_lock (&more_space_lock_loh);
32275
32276     //dprintf (GTC_LOG, ("---- (GC%d)End Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
32277     dprintf (GTC_LOG, ("---- (GC%d)ESw ----", VolatileLoad(&settings.gc_index)));
32278 }
32279 #endif //BACKGROUND_GC
32280
32281 void gc_heap::sweep_large_objects ()
32282 {
32283     //this min value is for the sake of the dynamic tuning.
32284     //so we know that we are not starting even if we have no
32285     //survivors.
32286     generation* gen        = large_object_generation;
32287     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
32288
32289     PREFIX_ASSUME(start_seg != NULL);
32290
32291     heap_segment* seg      = start_seg;
32292     heap_segment* prev_seg = 0;
32293     uint8_t* o             = generation_allocation_start (gen);
32294     int align_const        = get_alignment_constant (FALSE);
32295
32296     //Skip the generation gap object
32297     o = o + Align(size (o), align_const);
32298
32299     uint8_t* plug_end         = o;
32300     uint8_t* plug_start       = o;
32301
32302     generation_allocator (gen)->clear();
32303     generation_free_list_space (gen) = 0;
32304     generation_free_obj_space (gen) = 0;
32305
32306
32307     dprintf (3, ("sweeping large objects"));
32308     dprintf (3, ("seg: %Ix, [%Ix, %Ix[, starting from %Ix", 
32309                  (size_t)seg,
32310                  (size_t)heap_segment_mem (seg),
32311                  (size_t)heap_segment_allocated (seg),
32312                  o));
32313
32314     while (1)
32315     {
32316         if (o >= heap_segment_allocated (seg))
32317         {
32318             heap_segment* next_seg = heap_segment_next (seg);
32319             //delete the empty segment if not the only one
32320             if ((plug_end == heap_segment_mem (seg)) &&
32321                 (seg != start_seg) && !heap_segment_read_only_p (seg))
32322             {
32323                 //prepare for deletion
32324                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
32325                 assert (prev_seg);
32326                 heap_segment_next (prev_seg) = next_seg;
32327                 heap_segment_next (seg) = freeable_large_heap_segment;
32328                 freeable_large_heap_segment = seg;
32329             }
32330             else
32331             {
32332                 if (!heap_segment_read_only_p (seg))
32333                 {
32334                     dprintf (3, ("Trimming seg to %Ix[", (size_t)plug_end));
32335                     heap_segment_allocated (seg) = plug_end;
32336                     decommit_heap_segment_pages (seg, 0);
32337                 }
32338                 prev_seg = seg;
32339             }
32340             seg = next_seg;
32341             if (seg == 0)
32342                 break;
32343             else
32344             {
32345                 o = heap_segment_mem (seg);
32346                 plug_end = o;
32347                 dprintf (3, ("seg: %Ix, [%Ix, %Ix[", (size_t)seg,
32348                              (size_t)heap_segment_mem (seg),
32349                              (size_t)heap_segment_allocated (seg)));
32350             }
32351         }
32352         if (large_object_marked(o, TRUE))
32353         {
32354             plug_start = o;
32355             //everything between plug_end and plug_start is free
32356             thread_gap (plug_end, plug_start-plug_end, gen);
32357
32358             BOOL m = TRUE;
32359             while (m)
32360             {
32361                 o = o + AlignQword (size (o));
32362                 if (o >= heap_segment_allocated (seg))
32363                 {
32364                     break;
32365                 }
32366                 m = large_object_marked (o, TRUE);
32367             }
32368             plug_end = o;
32369             dprintf (3, ("plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
32370         }
32371         else
32372         {
32373             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
32374             {
32375                 o = o + AlignQword (size (o));
32376             }
32377         }
32378     }
32379
32380     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
32381
32382     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
32383 }
32384
32385 void gc_heap::relocate_in_large_objects ()
32386 {
32387     relocate_args args;
32388     args.low = gc_low;
32389     args.high = gc_high;
32390     args.last_plug = 0;
32391
32392     generation* gen = large_object_generation;
32393
32394     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32395
32396     PREFIX_ASSUME(seg != NULL);
32397
32398     uint8_t* o = generation_allocation_start (gen);
32399
32400     while (1)
32401     {
32402         if (o >= heap_segment_allocated (seg))
32403         {
32404             seg = heap_segment_next_rw (seg);
32405             if (seg == 0)
32406                 break;
32407             else
32408             {
32409                 o = heap_segment_mem (seg);
32410             }
32411         }
32412         while (o < heap_segment_allocated (seg))
32413         {
32414             check_class_object_demotion (o);
32415             if (contain_pointers (o))
32416             {
32417                 dprintf(3, ("Relocating through large object %Ix", (size_t)o));
32418                 go_through_object_nostart (method_table (o), o, size(o), pval,
32419                         {
32420                             reloc_survivor_helper (pval);
32421                         });
32422             }
32423             o = o + AlignQword (size (o));
32424         }
32425     }
32426 }
32427
32428 void gc_heap::mark_through_cards_for_large_objects (card_fn fn,
32429                                                     BOOL relocating)
32430 {
32431     uint8_t*      low               = gc_low;
32432     size_t        end_card          = 0;
32433     generation*   oldest_gen        = generation_of (max_generation+1);
32434     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
32435
32436     PREFIX_ASSUME(seg != NULL);
32437
32438     uint8_t*      beg               = generation_allocation_start (oldest_gen);
32439     uint8_t*      end               = heap_segment_allocated (seg);
32440
32441     size_t  cg_pointers_found = 0;
32442
32443     size_t  card_word_end = (card_of (align_on_card_word (end)) /
32444                              card_word_width);
32445
32446     size_t      n_eph             = 0;
32447     size_t      n_gen             = 0;
32448     size_t      n_card_set        = 0;
32449     uint8_t*    next_boundary = (relocating ?
32450                               generation_plan_allocation_start (generation_of (max_generation -1)) :
32451                               ephemeral_low);
32452
32453     uint8_t*    nhigh         = (relocating ?
32454                               heap_segment_plan_allocated (ephemeral_heap_segment) :
32455                               ephemeral_high);
32456
32457     BOOL          foundp            = FALSE;
32458     uint8_t*      start_address     = 0;
32459     uint8_t*      limit             = 0;
32460     size_t        card              = card_of (beg);
32461     uint8_t*      o                 = beg;
32462 #ifdef BACKGROUND_GC
32463     BOOL consider_bgc_mark_p        = FALSE;
32464     BOOL check_current_sweep_p      = FALSE;
32465     BOOL check_saved_sweep_p        = FALSE;
32466     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32467 #endif //BACKGROUND_GC
32468
32469     size_t total_cards_cleared = 0;
32470
32471     //dprintf(3,( "scanning large objects from %Ix to %Ix", (size_t)beg, (size_t)end));
32472     dprintf(3, ("CMl: %Ix->%Ix", (size_t)beg, (size_t)end));
32473     while (1)
32474     {
32475         if ((o < end) && (card_of(o) > card))
32476         {
32477             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
32478             if (cg_pointers_found == 0)
32479             {
32480                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)o));
32481                 clear_cards (card, card_of((uint8_t*)o));
32482                 total_cards_cleared += (card_of((uint8_t*)o) - card);
32483             }
32484             n_eph +=cg_pointers_found;
32485             cg_pointers_found = 0;
32486             card = card_of ((uint8_t*)o);
32487         }
32488         if ((o < end) &&(card >= end_card))
32489         {
32490             foundp = find_card (card_table, card, card_word_end, end_card);
32491             if (foundp)
32492             {
32493                 n_card_set+= end_card - card;
32494                 start_address = max (beg, card_address (card));
32495             }
32496             limit = min (end, card_address (end_card));
32497         }
32498         if ((!foundp) || (o >= end) || (card_address (card) >= end))
32499         {
32500             if ((foundp) && (cg_pointers_found == 0))
32501             {
32502                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
32503                            (size_t)card_address(card+1)));
32504                 clear_cards (card, card+1);
32505                 total_cards_cleared += 1;
32506             }
32507             n_eph +=cg_pointers_found;
32508             cg_pointers_found = 0;
32509             if ((seg = heap_segment_next_rw (seg)) != 0)
32510             {
32511 #ifdef BACKGROUND_GC
32512                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32513 #endif //BACKGROUND_GC
32514                 beg = heap_segment_mem (seg);
32515                 end = compute_next_end (seg, low);
32516                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
32517                 card = card_of (beg);
32518                 o  = beg;
32519                 end_card = 0;
32520                 continue;
32521             }
32522             else
32523             {
32524                 break;
32525             }
32526         }
32527
32528         assert (card_set_p (card));
32529         {
32530             dprintf(3,("card %Ix: o: %Ix, l: %Ix[ ",
32531                        card, (size_t)o, (size_t)limit));
32532
32533             assert (Align (size (o)) >= Align (min_obj_size));
32534             size_t s = size (o);
32535             uint8_t* next_o =  o + AlignQword (s);
32536             Prefetch (next_o);
32537
32538             while (o < limit)
32539             {
32540                 s = size (o);
32541                 assert (Align (s) >= Align (min_obj_size));
32542                 next_o =  o + AlignQword (s);
32543                 Prefetch (next_o);
32544
32545                 dprintf (4, ("|%Ix|", (size_t)o));
32546                 if (next_o < start_address)
32547                 {
32548                     goto end_object;
32549                 }
32550
32551 #ifdef BACKGROUND_GC
32552                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
32553                 {
32554                     goto end_object;
32555                 }
32556 #endif //BACKGROUND_GC
32557
32558 #ifdef COLLECTIBLE_CLASS
32559                 if (is_collectible(o))
32560                 {
32561                     BOOL passed_end_card_p = FALSE;
32562
32563                     if (card_of (o) > card)
32564                     {
32565                         passed_end_card_p = card_transition (o, end, card_word_end,
32566                             cg_pointers_found, 
32567                             n_eph, n_card_set,
32568                             card, end_card,
32569                             foundp, start_address,
32570                             limit, total_cards_cleared);
32571                     }
32572
32573                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
32574                     {
32575                         // card is valid and it covers the head of the object
32576                         if (fn == &gc_heap::relocate_address)
32577                         {
32578                             keep_card_live (o, n_gen, cg_pointers_found);
32579                         }
32580                         else
32581                         {
32582                             uint8_t* class_obj = get_class_object (o);
32583                             mark_through_cards_helper (&class_obj, n_gen,
32584                                                     cg_pointers_found, fn,
32585                                                     nhigh, next_boundary);
32586                         }
32587                     }
32588
32589                     if (passed_end_card_p)
32590                     {
32591                         if (foundp && (card_address (card) < next_o))
32592                         {
32593                             goto go_through_refs;
32594                         }
32595                         else 
32596                         {
32597                             goto end_object;
32598                         }
32599                     }
32600                 }
32601
32602 go_through_refs:
32603 #endif //COLLECTIBLE_CLASS
32604
32605                 if (contain_pointers (o))
32606                 {
32607                     dprintf(3,("Going through %Ix", (size_t)o));
32608
32609                     go_through_object (method_table(o), o, s, poo,
32610                                        start_address, use_start, (o + s),
32611                        {
32612                            if (card_of ((uint8_t*)poo) > card)
32613                            {
32614                                 BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
32615                                         card_word_end,
32616                                         cg_pointers_found, 
32617                                         n_eph, n_card_set,
32618                                         card, end_card,
32619                                         foundp, start_address,
32620                                         limit, total_cards_cleared);
32621
32622                                 if (passed_end_card_p)
32623                                 {
32624                                     if (foundp && (card_address (card) < next_o))
32625                                     {
32626                                         //new_start();
32627                                         {
32628                                             if (ppstop <= (uint8_t**)start_address)
32629                                             {break;}
32630                                             else if (poo < (uint8_t**)start_address)
32631                                             {poo = (uint8_t**)start_address;}
32632                                         }
32633                                     }
32634                                     else
32635                                     {
32636                                         goto end_object;
32637                                     }
32638                                 }
32639                             }
32640
32641                            mark_through_cards_helper (poo, n_gen,
32642                                                       cg_pointers_found, fn,
32643                                                       nhigh, next_boundary);
32644                        }
32645                         );
32646                 }
32647
32648             end_object:
32649                 o = next_o;
32650             }
32651
32652         }
32653     }
32654
32655     // compute the efficiency ratio of the card table
32656     if (!relocating)
32657     {
32658         generation_skip_ratio = min (((n_eph > 800) ?
32659                                       (int)(((float)n_gen / (float)n_eph) * 100) : 100),
32660                                      generation_skip_ratio);
32661
32662         dprintf (3, ("Mloh: cross: %Id, useful: %Id, cards cleared: %Id, cards set: %Id, ratio: %d", 
32663              n_eph, n_gen, total_cards_cleared, n_card_set, generation_skip_ratio));
32664     }
32665     else
32666     {
32667         dprintf (3, ("R: Mloh: cross: %Id, useful: %Id, cards set: %Id, ratio: %d", 
32668              n_eph, n_gen, n_card_set, generation_skip_ratio));
32669     }
32670 }
32671
32672 void gc_heap::descr_segment (heap_segment* seg )
32673 {
32674 #ifdef TRACE_GC
32675     uint8_t*  x = heap_segment_mem (seg);
32676     while (x < heap_segment_allocated (seg))
32677     {
32678         dprintf(2, ( "%Ix: %d ", (size_t)x, size (x)));
32679         x = x + Align(size (x));
32680     }
32681 #else // TRACE_GC
32682     UNREFERENCED_PARAMETER(seg);
32683 #endif // TRACE_GC
32684 }
32685
32686 void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context)
32687 {
32688 #ifdef MULTIPLE_HEAPS
32689     int n_heaps = g_theGCHeap->GetNumberOfHeaps ();
32690     for (int i = 0; i < n_heaps; i++)
32691     {
32692         gc_heap* hp = GCHeap::GetHeap(i)->pGenGCHeap;
32693 #else //MULTIPLE_HEAPS
32694     {
32695         gc_heap* hp = NULL;
32696 #ifdef _PREFAST_
32697         // prefix complains about us dereferencing hp in wks build even though we only access static members
32698         // this way. not sure how to shut it up except for this ugly workaround:
32699         PREFIX_ASSUME(hp != NULL);
32700 #endif // _PREFAST_
32701 #endif //MULTIPLE_HEAPS
32702
32703         int curr_gen_number0 = max_generation+1;
32704         while (curr_gen_number0 >= 0)
32705         {
32706             generation* gen = hp->generation_of (curr_gen_number0);
32707             heap_segment* seg = generation_start_segment (gen);
32708             while (seg && (seg != hp->ephemeral_heap_segment))
32709             {
32710                 assert (curr_gen_number0 > 0);
32711
32712                 // report bounds from heap_segment_mem (seg) to
32713                 // heap_segment_allocated (seg);
32714                 // for generation # curr_gen_number0
32715                 // for heap # heap_no
32716
32717                 fn(context, curr_gen_number0, heap_segment_mem (seg),
32718                                               heap_segment_allocated (seg),
32719                                               curr_gen_number0 == max_generation+1 ? heap_segment_reserved (seg) : heap_segment_allocated (seg));
32720
32721                 seg = heap_segment_next (seg);
32722             }
32723             if (seg)
32724             {
32725                 assert (seg == hp->ephemeral_heap_segment);
32726                 assert (curr_gen_number0 <= max_generation);
32727                 //
32728                 if (curr_gen_number0 == max_generation)
32729                 {
32730                     if (heap_segment_mem (seg) < generation_allocation_start (hp->generation_of (max_generation-1)))
32731                     {
32732                         // report bounds from heap_segment_mem (seg) to
32733                         // generation_allocation_start (generation_of (max_generation-1))
32734                         // for heap # heap_number
32735
32736                         fn(context, curr_gen_number0, heap_segment_mem (seg),
32737                                                       generation_allocation_start (hp->generation_of (max_generation-1)),
32738                                                       generation_allocation_start (hp->generation_of (max_generation-1)) );
32739                     }
32740                 }
32741                 else if (curr_gen_number0 != 0)
32742                 {
32743                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32744                     // to generation_allocation_start (generation_of (curr_gen_number0-1))
32745                     // for heap # heap_number
32746
32747                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32748                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)),
32749                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)));
32750                 }
32751                 else
32752                 {
32753                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32754                     // to heap_segment_allocated (ephemeral_heap_segment);
32755                     // for heap # heap_number
32756
32757                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32758                                                   heap_segment_allocated (hp->ephemeral_heap_segment),
32759                                                   heap_segment_reserved (hp->ephemeral_heap_segment) );
32760                 }
32761             }
32762             curr_gen_number0--;
32763         }
32764     }
32765 }
32766
32767 #ifdef TRACE_GC
32768 // Note that when logging is on it can take a long time to go through the free items.
32769 void gc_heap::print_free_list (int gen, heap_segment* seg)
32770 {
32771     UNREFERENCED_PARAMETER(gen);
32772     UNREFERENCED_PARAMETER(seg);
32773 /*
32774     if (settings.concurrent == FALSE)
32775     {
32776         uint8_t* seg_start = heap_segment_mem (seg);
32777         uint8_t* seg_end = heap_segment_allocated (seg);
32778
32779         dprintf (3, ("Free list in seg %Ix:", seg_start));
32780
32781         size_t total_free_item = 0;
32782
32783         allocator* gen_allocator = generation_allocator (generation_of (gen));
32784         for (unsigned int b = 0; b < gen_allocator->number_of_buckets(); b++)
32785         {
32786             uint8_t* fo = gen_allocator->alloc_list_head_of (b);
32787             while (fo)
32788             {
32789                 if (fo >= seg_start && fo < seg_end)
32790                 {
32791                     total_free_item++;
32792
32793                     size_t free_item_len = size(fo);
32794
32795                     dprintf (3, ("[%Ix, %Ix[:%Id",
32796                                  (size_t)fo,
32797                                  (size_t)(fo + free_item_len),
32798                                  free_item_len));
32799                 }
32800
32801                 fo = free_list_slot (fo);
32802             }
32803         }
32804
32805         dprintf (3, ("total %Id free items", total_free_item));
32806     }
32807 */
32808 }
32809 #endif //TRACE_GC
32810
32811 void gc_heap::descr_generations (BOOL begin_gc_p)
32812 {
32813     UNREFERENCED_PARAMETER(begin_gc_p);
32814 #ifdef STRESS_LOG
32815     if (StressLog::StressLogOn(LF_GC, LL_INFO10))
32816     {
32817         gc_heap* hp = 0;
32818 #ifdef MULTIPLE_HEAPS
32819         hp= this;
32820 #endif //MULTIPLE_HEAPS
32821
32822         STRESS_LOG1(LF_GC, LL_INFO10, "GC Heap %p\n", hp);
32823         for (int n = max_generation; n >= 0; --n)
32824         {
32825             STRESS_LOG4(LF_GC, LL_INFO10, "    Generation %d [%p, %p] cur = %p\n",
32826                     n,
32827                     generation_allocation_start(generation_of(n)),
32828                     generation_allocation_limit(generation_of(n)),
32829                     generation_allocation_pointer(generation_of(n)));
32830
32831             heap_segment* seg = generation_start_segment(generation_of(n));
32832             while (seg)
32833             {
32834                 STRESS_LOG4(LF_GC, LL_INFO10, "        Segment mem %p alloc = %p used %p committed %p\n",
32835                         heap_segment_mem(seg),
32836                         heap_segment_allocated(seg),
32837                         heap_segment_used(seg),
32838                         heap_segment_committed(seg));
32839                 seg = heap_segment_next(seg);
32840             }
32841         }
32842     }
32843 #endif  // STRESS_LOG
32844
32845 #ifdef TRACE_GC
32846     dprintf (2, ("lowest_address: %Ix highest_address: %Ix",
32847              (size_t) lowest_address, (size_t) highest_address));
32848 #ifdef BACKGROUND_GC
32849     dprintf (2, ("bgc lowest_address: %Ix bgc highest_address: %Ix",
32850              (size_t) background_saved_lowest_address, (size_t) background_saved_highest_address));
32851 #endif //BACKGROUND_GC
32852
32853     if (heap_number == 0)
32854     {
32855         dprintf (1, ("total heap size: %Id, commit size: %Id", get_total_heap_size(), get_total_committed_size()));
32856     }
32857
32858     int curr_gen_number = max_generation+1;
32859     while (curr_gen_number >= 0)
32860     {
32861         size_t total_gen_size = generation_size (curr_gen_number);
32862 #ifdef SIMPLE_DPRINTF
32863         dprintf (GTC_LOG, ("[%s][g%d]gen %d:, size: %Id, frag: %Id(L: %Id, O: %Id), f: %d%% %s %s %s",
32864                       (begin_gc_p ? "BEG" : "END"),
32865                       settings.condemned_generation,
32866                       curr_gen_number,
32867                       total_gen_size,
32868                       dd_fragmentation (dynamic_data_of (curr_gen_number)),
32869                       generation_free_list_space (generation_of (curr_gen_number)),
32870                       generation_free_obj_space (generation_of (curr_gen_number)),
32871                       (total_gen_size ? 
32872                         (int)(((double)dd_fragmentation (dynamic_data_of (curr_gen_number)) / (double)total_gen_size) * 100) :
32873                         0),
32874                       (begin_gc_p ? ("") : (settings.compaction ? "(compact)" : "(sweep)")),
32875                       (settings.heap_expansion ? "(EX)" : " "),
32876                       (settings.promotion ? "Promotion" : "NoPromotion")));
32877 #else
32878         dprintf (2, ( "Generation %d: gap size: %d, generation size: %Id, fragmentation: %Id",
32879                       curr_gen_number,
32880                       size (generation_allocation_start (generation_of (curr_gen_number))),
32881                       total_gen_size,
32882                       dd_fragmentation (dynamic_data_of (curr_gen_number))));
32883 #endif //SIMPLE_DPRINTF
32884
32885         generation* gen = generation_of (curr_gen_number);
32886         heap_segment* seg = generation_start_segment (gen);
32887         while (seg && (seg != ephemeral_heap_segment))
32888         {
32889             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[-%Ix[ (%Id) (%Id)",
32890                         curr_gen_number,
32891                         (size_t)heap_segment_mem (seg),
32892                         (size_t)heap_segment_allocated (seg),
32893                         (size_t)heap_segment_committed (seg),
32894                         (size_t)(heap_segment_allocated (seg) - heap_segment_mem (seg)),
32895                         (size_t)(heap_segment_committed (seg) - heap_segment_allocated (seg))));
32896             print_free_list (curr_gen_number, seg);
32897             seg = heap_segment_next (seg);
32898         }
32899         if (seg && (seg != generation_start_segment (gen)))
32900         {
32901             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32902                          curr_gen_number,
32903                          (size_t)heap_segment_mem (seg),
32904                          (size_t)generation_allocation_start (generation_of (curr_gen_number-1))));
32905             print_free_list (curr_gen_number, seg);
32906
32907         }
32908         else if (seg)
32909         {
32910             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32911                          curr_gen_number,
32912                          (size_t)generation_allocation_start (generation_of (curr_gen_number)),
32913                          (size_t)(((curr_gen_number == 0)) ?
32914                                   (heap_segment_allocated
32915                                    (generation_start_segment
32916                                     (generation_of (curr_gen_number)))) :
32917                                   (generation_allocation_start
32918                                    (generation_of (curr_gen_number - 1))))
32919                          ));
32920             print_free_list (curr_gen_number, seg);
32921         }
32922         curr_gen_number--;
32923     }
32924
32925 #endif //TRACE_GC
32926 }
32927
32928 #undef TRACE_GC
32929
32930 //#define TRACE_GC
32931
32932 //-----------------------------------------------------------------------------
32933 //
32934 //                                  VM Specific support
32935 //
32936 //-----------------------------------------------------------------------------
32937
32938
32939 #ifdef TRACE_GC
32940
32941  unsigned int PromotedObjectCount  = 0;
32942  unsigned int CreatedObjectCount       = 0;
32943  unsigned int AllocDuration            = 0;
32944  unsigned int AllocCount               = 0;
32945  unsigned int AllocBigCount            = 0;
32946  unsigned int AllocSmallCount      = 0;
32947  unsigned int AllocStart             = 0;
32948 #endif //TRACE_GC
32949
32950 //Static member variables.
32951 VOLATILE(BOOL)    GCHeap::GcInProgress            = FALSE;
32952 //GCTODO
32953 //CMCSafeLock*      GCHeap::fGcLock;
32954 GCEvent            *GCHeap::WaitForGCEvent         = NULL;
32955 //GCTODO
32956 #ifdef TRACE_GC
32957 unsigned int       GCHeap::GcDuration;
32958 #endif //TRACE_GC
32959 unsigned            GCHeap::GcCondemnedGeneration   = 0;
32960 size_t              GCHeap::totalSurvivedSize       = 0;
32961 #ifdef FEATURE_PREMORTEM_FINALIZATION
32962 CFinalize*          GCHeap::m_Finalize              = 0;
32963 BOOL                GCHeap::GcCollectClasses        = FALSE;
32964 VOLATILE(int32_t)      GCHeap::m_GCFLock               = 0;
32965
32966 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32967 #ifdef STRESS_HEAP
32968 #ifdef BACKGROUND_GC
32969 int                 GCHeap::gc_stress_fgcs_in_bgc   = 0;
32970 #endif // BACKGROUND_GC
32971 #ifndef MULTIPLE_HEAPS
32972 OBJECTHANDLE        GCHeap::m_StressObjs[NUM_HEAP_STRESS_OBJS];
32973 int                 GCHeap::m_CurStressObj          = 0;
32974 #endif // !MULTIPLE_HEAPS
32975 #endif // STRESS_HEAP
32976 #endif // FEATURE_REDHAWK
32977
32978 #endif //FEATURE_PREMORTEM_FINALIZATION
32979
32980 class NoGCRegionLockHolder
32981 {
32982 public:
32983     NoGCRegionLockHolder()
32984     {
32985         enter_spin_lock_noinstru(&g_no_gc_lock);
32986     }
32987
32988     ~NoGCRegionLockHolder()
32989     {
32990         leave_spin_lock_noinstru(&g_no_gc_lock);
32991     }
32992 };
32993
32994 // An explanation of locking for finalization:
32995 //
32996 // Multiple threads allocate objects.  During the allocation, they are serialized by
32997 // the AllocLock above.  But they release that lock before they register the object
32998 // for finalization.  That's because there is much contention for the alloc lock, but
32999 // finalization is presumed to be a rare case.
33000 //
33001 // So registering an object for finalization must be protected by the FinalizeLock.
33002 //
33003 // There is another logical queue that involves finalization.  When objects registered
33004 // for finalization become unreachable, they are moved from the "registered" queue to
33005 // the "unreachable" queue.  Note that this only happens inside a GC, so no other
33006 // threads can be manipulating either queue at that time.  Once the GC is over and
33007 // threads are resumed, the Finalizer thread will dequeue objects from the "unreachable"
33008 // queue and call their finalizers.  This dequeue operation is also protected with
33009 // the finalize lock.
33010 //
33011 // At first, this seems unnecessary.  Only one thread is ever enqueuing or dequeuing
33012 // on the unreachable queue (either the GC thread during a GC or the finalizer thread
33013 // when a GC is not in progress).  The reason we share a lock with threads enqueuing
33014 // on the "registered" queue is that the "registered" and "unreachable" queues are
33015 // interrelated.
33016 //
33017 // They are actually two regions of a longer list, which can only grow at one end.
33018 // So to enqueue an object to the "registered" list, you actually rotate an unreachable
33019 // object at the boundary between the logical queues, out to the other end of the
33020 // unreachable queue -- where all growing takes place.  Then you move the boundary
33021 // pointer so that the gap we created at the boundary is now on the "registered"
33022 // side rather than the "unreachable" side.  Now the object can be placed into the
33023 // "registered" side at that point.  This is much more efficient than doing moves
33024 // of arbitrarily long regions, but it causes the two queues to require a shared lock.
33025 //
33026 // Notice that Enter/LeaveFinalizeLock is not a GC-aware spin lock.  Instead, it relies
33027 // on the fact that the lock will only be taken for a brief period and that it will
33028 // never provoke or allow a GC while the lock is held.  This is critical.  If the
33029 // FinalizeLock used enter_spin_lock (and thus sometimes enters preemptive mode to
33030 // allow a GC), then the Alloc client would have to GC protect a finalizable object
33031 // to protect against that eventuality.  That is too slow!
33032
33033
33034
33035 BOOL IsValidObject99(uint8_t *pObject)
33036 {
33037 #ifdef VERIFY_HEAP
33038     if (!((CObjectHeader*)pObject)->IsFree())
33039         ((CObjectHeader *) pObject)->Validate();
33040 #endif //VERIFY_HEAP
33041     return(TRUE);
33042 }
33043
33044 #ifdef BACKGROUND_GC 
33045 BOOL gc_heap::bgc_mark_array_range (heap_segment* seg, 
33046                                     BOOL whole_seg_p,
33047                                     uint8_t** range_beg,
33048                                     uint8_t** range_end)
33049 {
33050     uint8_t* seg_start = heap_segment_mem (seg);
33051     uint8_t* seg_end = (whole_seg_p ? heap_segment_reserved (seg) : align_on_mark_word (heap_segment_allocated (seg)));
33052
33053     if ((seg_start < background_saved_highest_address) &&
33054         (seg_end > background_saved_lowest_address))
33055     {
33056         *range_beg = max (seg_start, background_saved_lowest_address);
33057         *range_end = min (seg_end, background_saved_highest_address);
33058         return TRUE;
33059     }
33060     else
33061     {
33062         return FALSE;
33063     }
33064 }
33065
33066 void gc_heap::bgc_verify_mark_array_cleared (heap_segment* seg)
33067 {
33068 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
33069     if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
33070     {
33071         uint8_t* range_beg = 0;
33072         uint8_t* range_end = 0;
33073
33074         if (bgc_mark_array_range (seg, TRUE, &range_beg, &range_end))
33075         {
33076             size_t  markw = mark_word_of (range_beg);
33077             size_t  markw_end = mark_word_of (range_end);
33078             while (markw < markw_end)
33079             {
33080                 if (mark_array [markw])
33081                 {
33082                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
33083                                     markw, mark_array [markw], mark_word_address (markw)));
33084                     FATAL_GC_ERROR();
33085                 }
33086                 markw++;
33087             }
33088             uint8_t* p = mark_word_address (markw_end);
33089             while (p < range_end)
33090             {
33091                 assert (!(mark_array_marked (p)));
33092                 p++;
33093             }
33094         }
33095     }
33096 #endif //VERIFY_HEAP && MARK_ARRAY
33097 }
33098
33099 void gc_heap::verify_mark_bits_cleared (uint8_t* obj, size_t s)
33100 {
33101 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
33102     size_t start_mark_bit = mark_bit_of (obj) + 1;
33103     size_t end_mark_bit = mark_bit_of (obj + s);
33104     unsigned int startbit = mark_bit_bit (start_mark_bit);
33105     unsigned int endbit = mark_bit_bit (end_mark_bit);
33106     size_t startwrd = mark_bit_word (start_mark_bit);
33107     size_t endwrd = mark_bit_word (end_mark_bit);
33108     unsigned int result = 0;
33109
33110     unsigned int firstwrd = ~(lowbits (~0, startbit));
33111     unsigned int lastwrd = ~(highbits (~0, endbit));
33112
33113     if (startwrd == endwrd)
33114     {
33115         unsigned int wrd = firstwrd & lastwrd;
33116         result = mark_array[startwrd] & wrd;
33117         if (result)
33118         {
33119             FATAL_GC_ERROR();
33120         }
33121         return;
33122     }
33123
33124     // verify the first mark word is cleared.
33125     if (startbit)
33126     {
33127         result = mark_array[startwrd] & firstwrd;
33128         if (result)
33129         {
33130             FATAL_GC_ERROR();
33131         }
33132         startwrd++;
33133     }
33134
33135     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
33136     {
33137         result = mark_array[wrdtmp];
33138         if (result)
33139         {
33140             FATAL_GC_ERROR();
33141         }
33142     }
33143
33144     // set the last mark word.
33145     if (endbit)
33146     {
33147         result = mark_array[endwrd] & lastwrd;
33148         if (result)
33149         {
33150             FATAL_GC_ERROR();
33151         }
33152     }
33153 #endif //VERIFY_HEAP && MARK_ARRAY
33154 }
33155
33156 void gc_heap::clear_all_mark_array()
33157 {
33158 #ifdef MARK_ARRAY
33159     //size_t num_dwords_written = 0;
33160     //size_t begin_time = GetHighPrecisionTimeStamp();
33161
33162     generation* gen = generation_of (max_generation);
33163     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33164     
33165     while (1)
33166     {
33167         if (seg == 0)
33168         {
33169             if (gen != large_object_generation)
33170             {
33171                 gen = generation_of (max_generation+1);
33172                 seg = heap_segment_rw (generation_start_segment (gen));
33173             }
33174             else
33175             {
33176                 break;
33177             }
33178         }
33179
33180         uint8_t* range_beg = 0;
33181         uint8_t* range_end = 0;
33182
33183         if (bgc_mark_array_range (seg, (seg == ephemeral_heap_segment), &range_beg, &range_end))
33184         { 
33185             size_t markw = mark_word_of (range_beg);
33186             size_t markw_end = mark_word_of (range_end);
33187             size_t size_total = (markw_end - markw) * sizeof (uint32_t);
33188             //num_dwords_written = markw_end - markw;
33189             size_t size = 0;
33190             size_t size_left = 0;
33191
33192             assert (((size_t)&mark_array[markw] & (sizeof(PTR_PTR)-1)) == 0);
33193
33194             if ((size_total & (sizeof(PTR_PTR) - 1)) != 0)
33195             {
33196                 size = (size_total & ~(sizeof(PTR_PTR) - 1));
33197                 size_left = size_total - size;
33198                 assert ((size_left & (sizeof (uint32_t) - 1)) == 0);
33199             }
33200             else
33201             {
33202                 size = size_total;
33203             }
33204
33205             memclr ((uint8_t*)&mark_array[markw], size);
33206
33207             if (size_left != 0)
33208             {
33209                 uint32_t* markw_to_clear = &mark_array[markw + size / sizeof (uint32_t)];
33210                 for (size_t i = 0; i < (size_left / sizeof (uint32_t)); i++)
33211                 {
33212                     *markw_to_clear = 0;
33213                     markw_to_clear++;
33214                 }
33215             }
33216         }
33217
33218         seg = heap_segment_next_rw (seg);
33219     }
33220
33221     //size_t end_time = GetHighPrecisionTimeStamp() - begin_time; 
33222
33223     //printf ("took %Id ms to clear %Id bytes\n", end_time, num_dwords_written*sizeof(uint32_t));
33224
33225 #endif //MARK_ARRAY
33226 }
33227
33228 #endif //BACKGROUND_GC 
33229
33230 void gc_heap::verify_mark_array_cleared (heap_segment* seg)
33231 {
33232 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
33233     assert (card_table == g_gc_card_table);
33234     size_t  markw = mark_word_of (heap_segment_mem (seg));
33235     size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
33236
33237     while (markw < markw_end)
33238     {
33239         if (mark_array [markw])
33240         {
33241             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
33242                             markw, mark_array [markw], mark_word_address (markw)));
33243             FATAL_GC_ERROR();
33244         }
33245         markw++;
33246     }
33247 #endif //VERIFY_HEAP && MARK_ARRAY
33248 }
33249
33250 void gc_heap::verify_mark_array_cleared ()
33251 {
33252 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
33253     if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
33254     {
33255         generation* gen = generation_of (max_generation);
33256         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33257         
33258         while (1)
33259         {
33260             if (seg == 0)
33261             {
33262                 if (gen != large_object_generation)
33263                 {
33264                     gen = generation_of (max_generation+1);
33265                     seg = heap_segment_rw (generation_start_segment (gen));
33266                 }
33267                 else
33268                 {
33269                     break;
33270                 }
33271             }
33272
33273             bgc_verify_mark_array_cleared (seg);
33274             seg = heap_segment_next_rw (seg);
33275         }
33276     }
33277 #endif //VERIFY_HEAP && MARK_ARRAY
33278 }
33279
33280 void gc_heap::verify_seg_end_mark_array_cleared()
33281 {
33282 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
33283     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
33284     {
33285         generation* gen = generation_of (max_generation);
33286         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33287         
33288         while (1)
33289         {
33290             if (seg == 0)
33291             {
33292                 if (gen != large_object_generation)
33293                 {
33294                     gen = generation_of (max_generation+1);
33295                     seg = heap_segment_rw (generation_start_segment (gen));
33296                 }
33297                 else
33298                 {
33299                     break;
33300                 }
33301             }
33302
33303             // We already cleared all mark array bits for ephemeral generations
33304             // at the beginning of bgc sweep
33305             uint8_t* from = ((seg == ephemeral_heap_segment) ?
33306                           generation_allocation_start (generation_of (max_generation - 1)) :
33307                           heap_segment_allocated (seg));
33308             size_t  markw = mark_word_of (align_on_mark_word (from));
33309             size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
33310
33311             while (from < mark_word_address (markw))
33312             {
33313                 if (is_mark_bit_set (from))
33314                 {
33315                     dprintf (3, ("mark bit for %Ix was not cleared", from));
33316                     FATAL_GC_ERROR();
33317                 }
33318
33319                 from += mark_bit_pitch;
33320             }
33321
33322             while (markw < markw_end)
33323             {
33324                 if (mark_array [markw])
33325                 {
33326                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
33327                                     markw, mark_array [markw], mark_word_address (markw)));
33328                     FATAL_GC_ERROR();
33329                 }
33330                 markw++;
33331             }
33332             seg = heap_segment_next_rw (seg);
33333         }
33334     }
33335 #endif //VERIFY_HEAP && MARK_ARRAY
33336 }
33337
33338 // This function is called to make sure we don't mess up the segment list
33339 // in SOH. It's called by:
33340 // 1) begin and end of ephemeral GCs
33341 // 2) during bgc sweep when we switch segments.
33342 void gc_heap::verify_soh_segment_list()
33343 {
33344 #ifdef VERIFY_HEAP
33345     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
33346     {
33347         generation* gen = generation_of (max_generation);
33348         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33349         heap_segment* last_seg = 0;
33350         while (seg)
33351         {
33352             last_seg = seg;
33353             seg = heap_segment_next_rw (seg);
33354         }
33355         if (last_seg != ephemeral_heap_segment)
33356         {
33357             FATAL_GC_ERROR();
33358         }
33359     }
33360 #endif //VERIFY_HEAP
33361 }
33362
33363 // This function can be called at any foreground GCs or blocking GCs. For background GCs,
33364 // it can be called at the end of the final marking; and at any point during background
33365 // sweep.
33366 // NOTE - to be able to call this function during background sweep, we need to temporarily 
33367 // NOT clear the mark array bits as we go.
33368 void gc_heap::verify_partial ()
33369 {
33370 #ifdef BACKGROUND_GC
33371     //printf ("GC#%d: Verifying loh during sweep\n", settings.gc_index);
33372     //generation* gen = large_object_generation;
33373     generation* gen = generation_of (max_generation);
33374     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33375     int align_const = get_alignment_constant (gen != large_object_generation);
33376
33377     uint8_t* o = 0;
33378     uint8_t* end = 0;
33379     size_t s = 0;
33380
33381     // Different ways to fail.
33382     BOOL mark_missed_p = FALSE;
33383     BOOL bad_ref_p = FALSE;
33384     BOOL free_ref_p = FALSE;
33385
33386     while (1)
33387     {
33388         if (seg == 0)
33389         {
33390             if (gen != large_object_generation)
33391             {
33392                 //switch to LOH
33393                 gen = large_object_generation;
33394                 align_const = get_alignment_constant (gen != large_object_generation);
33395                 seg = heap_segment_rw (generation_start_segment (gen));
33396                 continue;
33397             }
33398             else
33399             {
33400                 break;
33401             }
33402         }
33403
33404         o = heap_segment_mem (seg);
33405         end  = heap_segment_allocated (seg);
33406         //printf ("validating [%Ix-[%Ix\n", o, end);
33407         while (o < end)
33408         {
33409             s = size (o);
33410
33411             BOOL marked_p = background_object_marked (o, FALSE);
33412
33413             if (marked_p)
33414             {
33415                 go_through_object_cl (method_table (o), o, s, oo,
33416                     {
33417                         if (*oo)
33418                         {
33419                             //dprintf (3, ("VOM: verifying member %Ix in obj %Ix", (size_t)*oo, o));
33420                             MethodTable *pMT = method_table (*oo);
33421
33422                             if (pMT == g_gc_pFreeObjectMethodTable)
33423                             {
33424                                 free_ref_p = TRUE;
33425                                 FATAL_GC_ERROR();
33426                             }
33427
33428                             if (!pMT->SanityCheck()) 
33429                             {
33430                                 bad_ref_p = TRUE;
33431                                 dprintf (3, ("Bad member of %Ix %Ix",
33432                                             (size_t)oo, (size_t)*oo));
33433                                 FATAL_GC_ERROR();
33434                             }
33435
33436                             if (current_bgc_state == bgc_final_marking)
33437                             {
33438                                 if (marked_p && !background_object_marked (*oo, FALSE))
33439                                 {
33440                                     mark_missed_p = TRUE;
33441                                     FATAL_GC_ERROR();
33442                                 }
33443                             }
33444                         }
33445                     }
33446                                     );
33447             }
33448
33449             o = o + Align(s, align_const);
33450         }
33451         seg = heap_segment_next_rw (seg);
33452     }
33453
33454     //printf ("didn't find any large object large enough...\n");
33455     //printf ("finished verifying loh\n");
33456 #endif //BACKGROUND_GC 
33457 }
33458
33459 #ifdef VERIFY_HEAP
33460
33461 void 
33462 gc_heap::verify_free_lists ()
33463 {
33464     for (int gen_num = 0; gen_num <= max_generation+1; gen_num++)
33465     {
33466         dprintf (3, ("Verifying free list for gen:%d", gen_num));
33467         allocator* gen_alloc = generation_allocator (generation_of (gen_num));
33468         size_t sz = gen_alloc->first_bucket_size();
33469         bool verify_undo_slot = (gen_num != 0) && (gen_num != max_generation+1) && !gen_alloc->discard_if_no_fit_p();
33470
33471         for (unsigned int a_l_number = 0; a_l_number < gen_alloc->number_of_buckets(); a_l_number++)
33472         {
33473             uint8_t* free_list = gen_alloc->alloc_list_head_of (a_l_number);
33474             uint8_t* prev = 0;
33475             while (free_list)
33476             {
33477                 if (!((CObjectHeader*)free_list)->IsFree())
33478                 {
33479                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't a free object)",
33480                                  (size_t)free_list));
33481                     FATAL_GC_ERROR();
33482                 }
33483                 if (((a_l_number < (gen_alloc->number_of_buckets()-1))&& (unused_array_size (free_list) >= sz))
33484                     || ((a_l_number != 0) && (unused_array_size (free_list) < sz/2)))
33485                 {
33486                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't in the right bucket",
33487                                  (size_t)free_list));
33488                     FATAL_GC_ERROR();
33489                 }
33490                 if (verify_undo_slot && (free_list_undo (free_list) != UNDO_EMPTY))
33491                 {
33492                     dprintf (3, ("Verifiying Heap: curr free list item %Ix has non empty undo slot",
33493                                  (size_t)free_list));
33494                     FATAL_GC_ERROR();
33495                 }
33496                 if ((gen_num != max_generation+1)&&(object_gennum (free_list)!= gen_num))
33497                 {
33498                     dprintf (3, ("Verifiying Heap: curr free list item %Ix is in the wrong generation free list",
33499                                  (size_t)free_list));
33500                     FATAL_GC_ERROR();
33501                 }
33502                     
33503                 prev = free_list;
33504                 free_list = free_list_slot (free_list);
33505             }
33506             //verify the sanity of the tail 
33507             uint8_t* tail = gen_alloc->alloc_list_tail_of (a_l_number);
33508             if (!((tail == 0) || (tail == prev)))
33509             {
33510                 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
33511                 FATAL_GC_ERROR();
33512             }
33513             if (tail == 0)
33514             {
33515                 uint8_t* head = gen_alloc->alloc_list_head_of (a_l_number);
33516                 if ((head != 0) && (free_list_slot (head) != 0))
33517                 {
33518                     dprintf (3, ("Verifying Heap: tail of free list is not correct"));
33519                     FATAL_GC_ERROR();
33520                 }
33521             }
33522
33523             sz *=2;
33524         }
33525     }
33526 }
33527
33528 void
33529 gc_heap::verify_heap (BOOL begin_gc_p)
33530 {
33531     int             heap_verify_level = static_cast<int>(GCConfig::GetHeapVerifyLevel());
33532     size_t          last_valid_brick = 0;
33533     BOOL            bCurrentBrickInvalid = FALSE;
33534     BOOL            large_brick_p = TRUE;
33535     size_t          curr_brick = 0;
33536     size_t          prev_brick = (size_t)-1;
33537     int             curr_gen_num = max_generation+1;    
33538     heap_segment*   seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num ) ));
33539
33540     PREFIX_ASSUME(seg != NULL);
33541
33542     uint8_t*        curr_object = heap_segment_mem (seg);
33543     uint8_t*        prev_object = 0;
33544     uint8_t*        begin_youngest = generation_allocation_start(generation_of(0));
33545     uint8_t*        end_youngest = heap_segment_allocated (ephemeral_heap_segment);
33546     uint8_t*        next_boundary = generation_allocation_start (generation_of (max_generation - 1));
33547     int             align_const = get_alignment_constant (FALSE);
33548     size_t          total_objects_verified = 0;
33549     size_t          total_objects_verified_deep = 0;
33550
33551 #ifdef BACKGROUND_GC
33552     BOOL consider_bgc_mark_p    = FALSE;
33553     BOOL check_current_sweep_p  = FALSE;
33554     BOOL check_saved_sweep_p    = FALSE;
33555     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33556 #endif //BACKGROUND_GC
33557
33558 #ifdef MULTIPLE_HEAPS
33559     t_join* current_join = &gc_t_join;
33560 #ifdef BACKGROUND_GC
33561     if (settings.concurrent && (bgc_thread_id.IsCurrentThread()))
33562     {
33563         // We always call verify_heap on entry of GC on the SVR GC threads.
33564         current_join = &bgc_t_join;
33565     }
33566 #endif //BACKGROUND_GC
33567 #endif //MULTIPLE_HEAPS
33568
33569     UNREFERENCED_PARAMETER(begin_gc_p);
33570 #ifdef BACKGROUND_GC 
33571     dprintf (2,("[%s]GC#%d(%s): Verifying heap - begin", 
33572         (begin_gc_p ? "BEG" : "END"),
33573         VolatileLoad(&settings.gc_index), 
33574         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
33575 #else
33576     dprintf (2,("[%s]GC#%d: Verifying heap - begin", 
33577                 (begin_gc_p ? "BEG" : "END"), VolatileLoad(&settings.gc_index)));
33578 #endif //BACKGROUND_GC 
33579
33580 #ifndef MULTIPLE_HEAPS
33581     if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
33582         (ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
33583     {
33584         FATAL_GC_ERROR();
33585     }
33586 #endif //MULTIPLE_HEAPS
33587
33588 #ifdef BACKGROUND_GC
33589     //don't touch the memory because the program is allocating from it.
33590     if (!settings.concurrent)
33591 #endif //BACKGROUND_GC
33592     {
33593         if (!(heap_verify_level & GCConfig::HEAPVERIFY_NO_MEM_FILL))
33594         {
33595             //uninit the unused portions of segments.
33596             generation* gen1 = large_object_generation;
33597             heap_segment* seg1 = heap_segment_rw (generation_start_segment (gen1));
33598             PREFIX_ASSUME(seg1 != NULL);
33599
33600             while (1)
33601             {
33602                 if (seg1)
33603                 {
33604                     uint8_t* clear_start = heap_segment_allocated (seg1) - plug_skew;
33605                     if (heap_segment_used (seg1) > clear_start)
33606                     {
33607                         dprintf (3, ("setting end of seg %Ix: [%Ix-[%Ix to 0xaa", 
33608                                     heap_segment_mem (seg1),
33609                                     clear_start ,
33610                                     heap_segment_used (seg1)));
33611                         memset (heap_segment_allocated (seg1) - plug_skew, 0xaa,
33612                             (heap_segment_used (seg1) - clear_start));
33613                     }
33614                     seg1 = heap_segment_next_rw (seg1);
33615                 }
33616                 else
33617                 {
33618                     if (gen1 == large_object_generation)
33619                     {
33620                         gen1 = generation_of (max_generation);
33621                         seg1 = heap_segment_rw (generation_start_segment (gen1));
33622                         PREFIX_ASSUME(seg1 != NULL);
33623                     }
33624                     else
33625                     {
33626                         break;
33627                     }
33628                 }
33629             }
33630         }
33631     }
33632
33633 #ifdef MULTIPLE_HEAPS
33634     current_join->join(this, gc_join_verify_copy_table);
33635     if (current_join->joined())
33636     {
33637         // in concurrent GC, new segment could be allocated when GC is working so the card brick table might not be updated at this point
33638         for (int i = 0; i < n_heaps; i++)
33639         {
33640             //copy the card and brick tables
33641             if (g_gc_card_table != g_heaps[i]->card_table)
33642             {
33643                 g_heaps[i]->copy_brick_card_table();
33644             }
33645         }
33646
33647         current_join->restart();
33648     }
33649 #else
33650         if (g_gc_card_table != card_table)
33651             copy_brick_card_table();
33652 #endif //MULTIPLE_HEAPS
33653
33654     //verify that the generation structures makes sense
33655     {
33656         generation* gen = generation_of (max_generation);
33657
33658         assert (generation_allocation_start (gen) ==
33659                 heap_segment_mem (heap_segment_rw (generation_start_segment (gen))));
33660         int gen_num = max_generation-1;
33661         generation* prev_gen = gen;
33662         while (gen_num >= 0)
33663         {
33664             gen = generation_of (gen_num);
33665             assert (generation_allocation_segment (gen) == ephemeral_heap_segment);
33666             assert (generation_allocation_start (gen) >= heap_segment_mem (ephemeral_heap_segment));
33667             assert (generation_allocation_start (gen) < heap_segment_allocated (ephemeral_heap_segment));
33668
33669             if (generation_start_segment (prev_gen ) ==
33670                 generation_start_segment (gen))
33671             {
33672                 assert (generation_allocation_start (prev_gen) <
33673                         generation_allocation_start (gen));
33674             }
33675             prev_gen = gen;
33676             gen_num--;
33677         }
33678     }
33679
33680     while (1)
33681     {
33682         // Handle segment transitions
33683         if (curr_object >= heap_segment_allocated (seg))
33684         {
33685             if (curr_object > heap_segment_allocated(seg))
33686             {
33687                 dprintf (3, ("Verifiying Heap: curr_object: %Ix > heap_segment_allocated (seg: %Ix)",
33688                         (size_t)curr_object, (size_t)seg));
33689                 FATAL_GC_ERROR();
33690             }
33691             seg = heap_segment_next_in_range (seg);
33692             if (seg)
33693             {
33694 #ifdef BACKGROUND_GC
33695                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33696 #endif //BACKGROUND_GC
33697                 curr_object = heap_segment_mem(seg);
33698                 prev_object = 0;
33699                 continue;
33700             }
33701             else
33702             {
33703                 if (curr_gen_num == (max_generation+1))
33704                 {
33705                     curr_gen_num--;
33706                     seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num)));
33707
33708                     PREFIX_ASSUME(seg != NULL);
33709
33710 #ifdef BACKGROUND_GC
33711                     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33712 #endif //BACKGROUND_GC
33713                     curr_object = heap_segment_mem (seg);
33714                     prev_object = 0;
33715                     large_brick_p = FALSE;
33716                     align_const = get_alignment_constant (TRUE);
33717                 }
33718                 else
33719                     break;  // Done Verifying Heap -- no more segments
33720             }
33721         }
33722
33723         // Are we at the end of the youngest_generation?
33724         if (seg == ephemeral_heap_segment)
33725         {
33726             if (curr_object >= end_youngest)
33727             {
33728                 // prev_object length is too long if we hit this int3
33729                 if (curr_object > end_youngest)
33730                 {
33731                     dprintf (3, ("Verifiying Heap: curr_object: %Ix > end_youngest: %Ix",
33732                             (size_t)curr_object, (size_t)end_youngest));
33733                     FATAL_GC_ERROR();
33734                 }
33735                 break;
33736             }
33737             
33738             if ((curr_object >= next_boundary) && (curr_gen_num > 0))
33739             {
33740                 curr_gen_num--;
33741                 if (curr_gen_num > 0)
33742                 {
33743                     next_boundary = generation_allocation_start (generation_of (curr_gen_num - 1));
33744                 }
33745             }
33746         }
33747
33748          //if (is_mark_set (curr_object))
33749          //{
33750          //        printf ("curr_object: %Ix is marked!",(size_t)curr_object);
33751          //        FATAL_GC_ERROR();
33752          //}
33753
33754         size_t s = size (curr_object);
33755         dprintf (3, ("o: %Ix, s: %d", (size_t)curr_object, s));
33756         if (s == 0)
33757         {
33758             dprintf (3, ("Verifying Heap: size of current object %Ix == 0", curr_object));
33759             FATAL_GC_ERROR();
33760         }
33761
33762         // If object is not in the youngest generation, then lets
33763         // verify that the brick table is correct....
33764         if (((seg != ephemeral_heap_segment) ||
33765              (brick_of(curr_object) < brick_of(begin_youngest))))
33766         {
33767             curr_brick = brick_of(curr_object);
33768
33769             // Brick Table Verification...
33770             //
33771             // On brick transition
33772             //     if brick is negative
33773             //          verify that brick indirects to previous valid brick
33774             //     else
33775             //          set current brick invalid flag to be flipped if we
33776             //          encounter an object at the correct place
33777             //
33778             if (curr_brick != prev_brick)
33779             {
33780                 // If the last brick we were examining had positive
33781                 // entry but we never found the matching object, then
33782                 // we have a problem
33783                 // If prev_brick was the last one of the segment
33784                 // it's ok for it to be invalid because it is never looked at
33785                 if (bCurrentBrickInvalid &&
33786                     (curr_brick != brick_of (heap_segment_mem (seg))) &&
33787                     !heap_segment_read_only_p (seg))
33788                 {
33789                     dprintf (3, ("curr brick %Ix invalid", curr_brick));
33790                     FATAL_GC_ERROR();
33791                 }
33792
33793                 if (large_brick_p)
33794                 {
33795                     //large objects verify the table only if they are in
33796                     //range.
33797                     if ((heap_segment_reserved (seg) <= highest_address) &&
33798                         (heap_segment_mem (seg) >= lowest_address) &&
33799                         brick_table [curr_brick] != 0)
33800                     {
33801                         dprintf (3, ("curr_brick %Ix for large object %Ix not set to -32768",
33802                                 curr_brick, (size_t)curr_object));
33803                         FATAL_GC_ERROR();
33804                     }
33805                     else
33806                     {
33807                         bCurrentBrickInvalid = FALSE;
33808                     }
33809                 }
33810                 else
33811                 {
33812                     // If the current brick contains a negative value make sure
33813                     // that the indirection terminates at the last  valid brick
33814                     if (brick_table [curr_brick] <= 0)
33815                     {
33816                         if (brick_table [curr_brick] == 0)
33817                         {
33818                             dprintf(3, ("curr_brick %Ix for object %Ix set to 0",
33819                                     curr_brick, (size_t)curr_object));
33820                             FATAL_GC_ERROR();
33821                         }
33822                         ptrdiff_t i = curr_brick;
33823                         while ((i >= ((ptrdiff_t) brick_of (heap_segment_mem (seg)))) &&
33824                                (brick_table[i] < 0))
33825                         {
33826                             i = i + brick_table[i];
33827                         }
33828                         if (i <  ((ptrdiff_t)(brick_of (heap_segment_mem (seg))) - 1))
33829                         {
33830                             dprintf (3, ("ptrdiff i: %Ix < brick_of (heap_segment_mem (seg)):%Ix - 1. curr_brick: %Ix",
33831                                     i, brick_of (heap_segment_mem (seg)),
33832                                     curr_brick));
33833                             FATAL_GC_ERROR();
33834                         }
33835                         // if (i != last_valid_brick)
33836                         //  FATAL_GC_ERROR();
33837                         bCurrentBrickInvalid = FALSE;
33838                     }
33839                     else if (!heap_segment_read_only_p (seg))
33840                     {
33841                         bCurrentBrickInvalid = TRUE;
33842                     }
33843                 }
33844             }
33845
33846             if (bCurrentBrickInvalid)
33847             {
33848                 if (curr_object == (brick_address(curr_brick) + brick_table[curr_brick] - 1))
33849                 {
33850                     bCurrentBrickInvalid = FALSE;
33851                     last_valid_brick = curr_brick;
33852                 }
33853             }
33854         }
33855
33856         if (*((uint8_t**)curr_object) != (uint8_t *) g_gc_pFreeObjectMethodTable)
33857         {
33858 #ifdef FEATURE_LOH_COMPACTION
33859             if ((curr_gen_num == (max_generation+1)) && (prev_object != 0))
33860             {
33861                 assert (method_table (prev_object) == g_gc_pFreeObjectMethodTable);
33862             }
33863 #endif //FEATURE_LOH_COMPACTION
33864
33865             total_objects_verified++;
33866
33867             BOOL can_verify_deep = TRUE;
33868 #ifdef BACKGROUND_GC
33869             can_verify_deep = fgc_should_consider_object (curr_object, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p);
33870 #endif //BACKGROUND_GC
33871
33872             BOOL deep_verify_obj = can_verify_deep;
33873             if ((heap_verify_level & GCConfig::HEAPVERIFY_DEEP_ON_COMPACT) && !settings.compaction)
33874                 deep_verify_obj = FALSE;
33875
33876             ((CObjectHeader*)curr_object)->ValidateHeap((Object*)curr_object, deep_verify_obj);
33877
33878             if (can_verify_deep)
33879             {
33880                 if (curr_gen_num > 0)
33881                 {
33882                     BOOL need_card_p = FALSE;
33883                     if (contain_pointers_or_collectible (curr_object))
33884                     {
33885                         dprintf (4, ("curr_object: %Ix", (size_t)curr_object));
33886                         size_t crd = card_of (curr_object);
33887                         BOOL found_card_p = card_set_p (crd);
33888
33889 #ifdef COLLECTIBLE_CLASS
33890                         if (is_collectible(curr_object))
33891                         {
33892                             uint8_t* class_obj = get_class_object (curr_object);
33893                             if ((class_obj < ephemeral_high) && (class_obj >= next_boundary))
33894                             {
33895                                 if (!found_card_p)
33896                                 {
33897                                     dprintf (3, ("Card not set, curr_object = [%Ix:%Ix pointing to class object %Ix",
33898                                                 card_of (curr_object), (size_t)curr_object, class_obj));
33899
33900                                     FATAL_GC_ERROR();
33901                                 }
33902                             }
33903                         }
33904 #endif //COLLECTIBLE_CLASS
33905
33906                         if (contain_pointers(curr_object))
33907                         {
33908                             go_through_object_nostart
33909                                 (method_table(curr_object), curr_object, s, oo,
33910                                 {
33911                                     if ((crd != card_of ((uint8_t*)oo)) && !found_card_p)
33912                                     {
33913                                         crd = card_of ((uint8_t*)oo);
33914                                         found_card_p = card_set_p (crd);
33915                                         need_card_p = FALSE;
33916                                     }
33917                                     if ((*oo < ephemeral_high) && (*oo >= next_boundary))
33918                                     {
33919                                         need_card_p = TRUE;
33920                                     }
33921
33922                                 if (need_card_p && !found_card_p)
33923                                 {
33924
33925                                         dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33926                                                     card_of (curr_object), (size_t)curr_object,
33927                                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33928                                         FATAL_GC_ERROR();
33929                                     }
33930                                 }
33931                                     );
33932                         }
33933                         if (need_card_p && !found_card_p)
33934                         {
33935                             dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33936                                     card_of (curr_object), (size_t)curr_object,
33937                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33938                             FATAL_GC_ERROR();
33939                         }
33940                     }
33941                 }
33942                 total_objects_verified_deep++;
33943             }
33944         }
33945
33946         prev_object = curr_object;
33947         prev_brick = curr_brick;
33948         curr_object = curr_object + Align(s, align_const);
33949         if (curr_object < prev_object)
33950         {
33951             dprintf (3, ("overflow because of a bad object size: %Ix size %Ix", prev_object, s));
33952             FATAL_GC_ERROR();
33953         }
33954     }
33955
33956 #ifdef BACKGROUND_GC
33957     dprintf (2, ("(%s)(%s)(%s) total_objects_verified is %Id, total_objects_verified_deep is %Id", 
33958                  (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p () ? "FGC" : "NGC")),
33959                  (begin_gc_p ? "BEG" : "END"),
33960                  ((current_c_gc_state == c_gc_state_planning) ? "in plan" : "not in plan"),
33961                  total_objects_verified, total_objects_verified_deep));
33962     if (current_c_gc_state != c_gc_state_planning)
33963     {
33964         assert (total_objects_verified == total_objects_verified_deep);
33965     }
33966 #endif //BACKGROUND_GC
33967     
33968     verify_free_lists();
33969
33970 #ifdef FEATURE_PREMORTEM_FINALIZATION
33971     finalize_queue->CheckFinalizerObjects();
33972 #endif // FEATURE_PREMORTEM_FINALIZATION
33973
33974     {
33975         // to be consistent with handle table APIs pass a ScanContext*
33976         // to provide the heap number.  the SC isn't complete though so
33977         // limit its scope to handle table verification.
33978         ScanContext sc;
33979         sc.thread_number = heap_number;
33980         GCScan::VerifyHandleTable(max_generation, max_generation, &sc);
33981     }
33982
33983 #ifdef MULTIPLE_HEAPS
33984     current_join->join(this, gc_join_verify_objects_done);
33985     if (current_join->joined())
33986 #endif //MULTIPLE_HEAPS
33987     {
33988         GCToEEInterface::VerifySyncTableEntry();
33989 #ifdef MULTIPLE_HEAPS
33990         current_join->restart();
33991 #endif //MULTIPLE_HEAPS
33992     }
33993
33994 #ifdef BACKGROUND_GC 
33995     if (!settings.concurrent)
33996     {
33997         if (current_c_gc_state == c_gc_state_planning)
33998         {
33999             // temporarily commenting this out 'cause an FGC
34000             // could be triggered before we sweep ephemeral.
34001             //verify_seg_end_mark_array_cleared();
34002         }
34003     }
34004
34005     if (settings.concurrent)
34006     {
34007         verify_mark_array_cleared();
34008     }
34009     dprintf (2,("GC%d(%s): Verifying heap - end", 
34010         VolatileLoad(&settings.gc_index), 
34011         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
34012 #else
34013     dprintf (2,("GC#d: Verifying heap - end", VolatileLoad(&settings.gc_index)));
34014 #endif //BACKGROUND_GC 
34015 }
34016
34017 #endif  //VERIFY_HEAP
34018
34019
34020 void GCHeap::ValidateObjectMember (Object* obj)
34021 {
34022 #ifdef VERIFY_HEAP
34023     size_t s = size (obj);
34024     uint8_t* o = (uint8_t*)obj;
34025
34026     go_through_object_cl (method_table (obj), o, s, oo,
34027                                 {
34028                                     uint8_t* child_o = *oo;
34029                                     if (child_o)
34030                                     {
34031                                         dprintf (3, ("VOM: m: %Ix obj %Ix", (size_t)child_o, o));
34032                                         MethodTable *pMT = method_table (child_o);
34033                                         assert(pMT);
34034                                         if (!pMT->SanityCheck()) {
34035                                             dprintf (3, ("Bad member of %Ix %Ix",
34036                                                         (size_t)oo, (size_t)child_o));
34037                                             FATAL_GC_ERROR();
34038                                         }
34039                                     }
34040                                 } );
34041 #endif // VERIFY_HEAP
34042 }
34043
34044 void DestructObject (CObjectHeader* hdr)
34045 {
34046     UNREFERENCED_PARAMETER(hdr); // compiler bug? -- this *is*, indeed, referenced
34047     hdr->~CObjectHeader();
34048 }
34049
34050 HRESULT GCHeap::Shutdown ()
34051 {
34052     deleteGCShadow();
34053
34054     GCScan::GcRuntimeStructuresValid (FALSE);
34055
34056     // Cannot assert this, since we use SuspendEE as the mechanism to quiesce all
34057     // threads except the one performing the shutdown.
34058     // ASSERT( !GcInProgress );
34059
34060     // Guard against any more GC occurring and against any threads blocking
34061     // for GC to complete when the GC heap is gone.  This fixes a race condition
34062     // where a thread in GC is destroyed as part of process destruction and
34063     // the remaining threads block for GC complete.
34064
34065     //GCTODO
34066     //EnterAllocLock();
34067     //Enter();
34068     //EnterFinalizeLock();
34069     //SetGCDone();
34070
34071     // during shutdown lot of threads are suspended
34072     // on this even, we don't want to wake them up just yet
34073     //CloseHandle (WaitForGCEvent);
34074
34075     //find out if the global card table hasn't been used yet
34076     uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
34077     if (card_table_refcount (ct) == 0)
34078     {
34079         destroy_card_table (ct);
34080         g_gc_card_table = nullptr;
34081
34082 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
34083         g_gc_card_bundle_table = nullptr;
34084 #endif
34085 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
34086         SoftwareWriteWatch::StaticClose();
34087 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
34088     }
34089
34090     //destroy all segments on the standby list
34091     while(gc_heap::segment_standby_list != 0)
34092     {
34093         heap_segment* next_seg = heap_segment_next (gc_heap::segment_standby_list);
34094 #ifdef MULTIPLE_HEAPS
34095         (gc_heap::g_heaps[0])->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
34096 #else //MULTIPLE_HEAPS
34097         pGenGCHeap->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
34098 #endif //MULTIPLE_HEAPS
34099         gc_heap::segment_standby_list = next_seg;
34100     }
34101
34102
34103 #ifdef MULTIPLE_HEAPS
34104
34105     for (int i = 0; i < gc_heap::n_heaps; i ++)
34106     {
34107         delete gc_heap::g_heaps[i]->vm_heap;
34108         //destroy pure GC stuff
34109         gc_heap::destroy_gc_heap (gc_heap::g_heaps[i]);
34110     }
34111 #else
34112     gc_heap::destroy_gc_heap (pGenGCHeap);
34113
34114 #endif //MULTIPLE_HEAPS
34115     gc_heap::shutdown_gc();
34116
34117     return S_OK;
34118 }
34119
34120 // Wait until a garbage collection is complete
34121 // returns NOERROR if wait was OK, other error code if failure.
34122 // WARNING: This will not undo the must complete state. If you are
34123 // in a must complete when you call this, you'd better know what you're
34124 // doing.
34125
34126 #ifdef FEATURE_PREMORTEM_FINALIZATION
34127 static
34128 HRESULT AllocateCFinalize(CFinalize **pCFinalize)
34129 {
34130     *pCFinalize = new (nothrow) CFinalize();
34131     if (*pCFinalize == NULL || !(*pCFinalize)->Initialize())
34132         return E_OUTOFMEMORY;
34133
34134     return S_OK;
34135 }
34136 #endif // FEATURE_PREMORTEM_FINALIZATION
34137
34138 // init the instance heap
34139 HRESULT GCHeap::Init(size_t hn)
34140 {
34141     HRESULT hres = S_OK;
34142
34143 #ifdef MULTIPLE_HEAPS
34144     if ((pGenGCHeap = gc_heap::make_gc_heap(this, (int)hn)) == 0)
34145         hres = E_OUTOFMEMORY;
34146 #else
34147     UNREFERENCED_PARAMETER(hn);
34148     if (!gc_heap::make_gc_heap())
34149         hres = E_OUTOFMEMORY;
34150 #endif //MULTIPLE_HEAPS
34151
34152     // Failed.
34153     return hres;
34154 }
34155
34156 //System wide initialization
34157 HRESULT GCHeap::Initialize()
34158 {
34159     HRESULT hr = S_OK;
34160
34161     g_gc_pFreeObjectMethodTable = GCToEEInterface::GetFreeObjectMethodTable();
34162     g_num_processors = GCToOSInterface::GetTotalProcessorCount();
34163     assert(g_num_processors != 0);
34164
34165 //Initialize the static members.
34166 #ifdef TRACE_GC
34167     GcDuration = 0;
34168     CreatedObjectCount = 0;
34169 #endif //TRACE_GC
34170
34171     bool is_restricted; 
34172     gc_heap::total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit (&is_restricted);
34173
34174 #ifdef BIT64
34175     gc_heap::heap_hard_limit = (size_t)GCConfig::GetGCHeapHardLimit();
34176
34177     if (!(gc_heap::heap_hard_limit))
34178     {
34179         uint32_t percent_of_mem = (uint32_t)GCConfig::GetGCHeapHardLimitPercent();
34180         if ((percent_of_mem > 0) && (percent_of_mem < 100))
34181         {
34182             gc_heap::heap_hard_limit = (size_t)(gc_heap::total_physical_mem * (uint64_t)percent_of_mem / (uint64_t)100);
34183         }
34184     }
34185
34186     // If the hard limit is specified, the user is saying even if the process is already
34187     // running in a container, use this limit for the GC heap.
34188     if (!(gc_heap::heap_hard_limit))
34189     {
34190         if (is_restricted)
34191         {
34192             uint64_t physical_mem_for_gc = gc_heap::total_physical_mem * (uint64_t)75 / (uint64_t)100;
34193             //printf ("returned physical mem %I64d, setting it to max (75%%: %I64d, 20mb)\n",
34194             //    gc_heap::total_physical_mem, physical_mem_for_gc);
34195             gc_heap::heap_hard_limit = (size_t)max ((20 * 1024 * 1024), physical_mem_for_gc);
34196         }
34197     }
34198
34199     //printf ("heap_hard_limit is %Id, total physical mem: %Id, %s restricted\n", 
34200     //    gc_heap::heap_hard_limit, gc_heap::total_physical_mem, (is_restricted ? "is" : "is not"));
34201 #endif //BIT64
34202
34203     uint32_t nhp = 1;
34204     uint32_t nhp_from_config = 0;
34205
34206 #ifdef MULTIPLE_HEAPS
34207     nhp_from_config = static_cast<uint32_t>(GCConfig::GetHeapCount());
34208     
34209     // GetCurrentProcessCpuCount only returns up to 64 procs.
34210     uint32_t nhp_from_process = GCToOSInterface::CanEnableGCCPUGroups() ?
34211                                 GCToOSInterface::GetTotalProcessorCount():
34212                                 GCToOSInterface::GetCurrentProcessCpuCount();
34213
34214     if (nhp_from_config)
34215     {
34216         // Even when the user specifies a heap count, it should not be more
34217         // than the number of procs this process can use.
34218         nhp_from_config = min (nhp_from_config, nhp_from_process);
34219     }
34220
34221     nhp = ((nhp_from_config == 0) ? nhp_from_process : nhp_from_config);
34222
34223     nhp = min (nhp, MAX_SUPPORTED_CPUS);
34224 #ifndef FEATURE_REDHAWK
34225     gc_heap::gc_thread_no_affinitize_p = (gc_heap::heap_hard_limit ? false : (GCConfig::GetNoAffinitize() != 0));
34226
34227     size_t gc_thread_affinity_mask = static_cast<size_t>(GCConfig::GetGCHeapAffinitizeMask());
34228
34229     if (gc_heap::heap_hard_limit)
34230     {
34231         gc_heap::gc_thread_no_affinitize_p = (gc_thread_affinity_mask == 0);
34232     }
34233
34234     if (!(gc_heap::gc_thread_no_affinitize_p))
34235     {
34236         if (!(GCToOSInterface::CanEnableGCCPUGroups()))
34237         {
34238             uintptr_t pmask, smask;
34239             if (GCToOSInterface::GetCurrentProcessAffinityMask(&pmask, &smask))
34240             {
34241                 pmask &= smask;
34242
34243 #ifdef FEATURE_PAL
34244                 // GetCurrentProcessAffinityMask can return pmask=0 and smask=0 on
34245                 // systems with more than 1 NUMA node. The pmask decides the
34246                 // number of GC heaps to be used and the processors they are
34247                 // affinitized with. So pmask is now set to reflect that 64
34248                 // processors are available to begin with. The actual processors in
34249                 // the system may be lower and are taken into account before
34250                 // finalizing the number of heaps.
34251                 if (!pmask)
34252                 {
34253                     pmask = SIZE_T_MAX;
34254                 }
34255 #endif // FEATURE_PAL
34256
34257                 if (gc_thread_affinity_mask)
34258                 {
34259                     pmask &= gc_thread_affinity_mask;
34260                 }
34261
34262                 process_mask = pmask;
34263
34264                 unsigned int set_bits_in_pmask = 0;
34265                 while (pmask)
34266                 {
34267                     if (pmask & 1)
34268                         set_bits_in_pmask++;
34269                     pmask >>= 1;
34270                 }
34271
34272                 nhp = min (nhp, set_bits_in_pmask);
34273
34274 #ifdef FEATURE_PAL
34275                 // Limit the GC heaps to the number of processors available in the system.
34276                 nhp = min (nhp, GCToOSInterface::GetTotalProcessorCount());
34277 #endif // FEATURE_PAL
34278             }
34279             else
34280             {
34281                 gc_heap::gc_thread_no_affinitize_p = true;
34282             }
34283         }
34284     }
34285 #endif //!FEATURE_REDHAWK
34286 #endif //MULTIPLE_HEAPS
34287
34288     size_t seg_size = 0;
34289     size_t large_seg_size = 0;
34290
34291     if (gc_heap::heap_hard_limit)
34292     {
34293         seg_size = gc_heap::get_segment_size_hard_limit (&nhp, (nhp_from_config == 0));
34294         gc_heap::soh_segment_size = seg_size;
34295         large_seg_size = seg_size * 2;
34296     }
34297     else
34298     {
34299         seg_size = get_valid_segment_size();
34300         gc_heap::soh_segment_size = seg_size;
34301         large_seg_size = get_valid_segment_size (TRUE);
34302     }
34303
34304     dprintf (1, ("%d heaps, soh seg size: %Id mb, loh: %Id mb\n", 
34305         nhp,
34306         (seg_size / (size_t)1024 / 1024), 
34307         (large_seg_size / 1024 / 1024)));
34308
34309     gc_heap::min_loh_segment_size = large_seg_size;
34310     gc_heap::min_segment_size = min (seg_size, large_seg_size);
34311 #ifdef SEG_MAPPING_TABLE
34312     gc_heap::min_segment_size_shr = index_of_highest_set_bit (gc_heap::min_segment_size);
34313 #endif //SEG_MAPPING_TABLE
34314
34315 #ifdef MULTIPLE_HEAPS
34316     gc_heap::n_heaps = nhp;
34317     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/, nhp);
34318 #else
34319     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/);
34320 #endif //MULTIPLE_HEAPS
34321
34322     if (hr != S_OK)
34323         return hr;
34324
34325     gc_heap::mem_one_percent = gc_heap::total_physical_mem / 100;
34326 #ifndef MULTIPLE_HEAPS
34327     gc_heap::mem_one_percent /= g_num_processors;
34328 #endif //!MULTIPLE_HEAPS
34329
34330     uint32_t highmem_th_from_config = (uint32_t)GCConfig::GetGCHighMemPercent();
34331     if (highmem_th_from_config)
34332     {
34333         gc_heap::high_memory_load_th = min (99, highmem_th_from_config);
34334         gc_heap::v_high_memory_load_th = min (99, (highmem_th_from_config + 7));
34335     }
34336     else
34337     {
34338         // We should only use this if we are in the "many process" mode which really is only applicable
34339         // to very powerful machines - before that's implemented, temporarily I am only enabling this for 80GB+ memory. 
34340         // For now I am using an estimate to calculate these numbers but this should really be obtained 
34341         // programmatically going forward.
34342         // I am assuming 47 processes using WKS GC and 3 using SVR GC.
34343         // I am assuming 3 in part due to the "very high memory load" is 97%.
34344         int available_mem_th = 10;
34345         if (gc_heap::total_physical_mem >= ((uint64_t)80 * 1024 * 1024 * 1024))
34346         {
34347             int adjusted_available_mem_th = 3 + (int)((float)47 / (float)(GCToOSInterface::GetTotalProcessorCount()));
34348             available_mem_th = min (available_mem_th, adjusted_available_mem_th);
34349         }
34350
34351         gc_heap::high_memory_load_th = 100 - available_mem_th;
34352         gc_heap::v_high_memory_load_th = 97;
34353     }
34354
34355     gc_heap::m_high_memory_load_th = min ((gc_heap::high_memory_load_th + 5), gc_heap::v_high_memory_load_th);
34356
34357     gc_heap::pm_stress_on = (GCConfig::GetGCProvModeStress() != 0);
34358
34359 #if defined(BIT64) 
34360     gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent;
34361 #endif // BIT64
34362
34363     WaitForGCEvent = new (nothrow) GCEvent;
34364
34365     if (!WaitForGCEvent)
34366     {
34367         return E_OUTOFMEMORY;
34368     }
34369
34370     if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE))
34371     {
34372         return E_FAIL;
34373     }
34374
34375 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
34376 #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS)
34377     if (GCStress<cfg_any>::IsEnabled())  {
34378         for(int i = 0; i < GCHeap::NUM_HEAP_STRESS_OBJS; i++)
34379         {
34380             m_StressObjs[i] = CreateGlobalHandle(0);
34381         }
34382         m_CurStressObj = 0;
34383     }
34384 #endif //STRESS_HEAP && !MULTIPLE_HEAPS
34385 #endif // FEATURE_REDHAWK
34386
34387     initGCShadow();         // If we are debugging write barriers, initialize heap shadow
34388
34389 #ifdef MULTIPLE_HEAPS
34390
34391     for (unsigned i = 0; i < nhp; i++)
34392     {
34393         GCHeap* Hp = new (nothrow) GCHeap();
34394         if (!Hp)
34395             return E_OUTOFMEMORY;
34396
34397         if ((hr = Hp->Init (i))!= S_OK)
34398         {
34399             return hr;
34400         }
34401     }
34402     // initialize numa node to heap map
34403     heap_select::init_numa_node_to_heap_map(nhp);
34404 #else
34405     hr = Init (0);
34406 #endif //MULTIPLE_HEAPS
34407
34408     if (hr == S_OK)
34409     {
34410         GCScan::GcRuntimeStructuresValid (TRUE);
34411
34412         GCToEEInterface::DiagUpdateGenerationBounds();
34413     }
34414
34415     return hr;
34416 };
34417
34418 ////
34419 // GC callback functions
34420 bool GCHeap::IsPromoted(Object* object)
34421 {
34422 #ifdef _DEBUG
34423     ((CObjectHeader*)object)->Validate();
34424 #endif //_DEBUG
34425
34426     uint8_t* o = (uint8_t*)object;
34427
34428     if (gc_heap::settings.condemned_generation == max_generation)
34429     {
34430 #ifdef MULTIPLE_HEAPS
34431         gc_heap* hp = gc_heap::g_heaps[0];
34432 #else
34433         gc_heap* hp = pGenGCHeap;
34434 #endif //MULTIPLE_HEAPS
34435
34436 #ifdef BACKGROUND_GC
34437         if (gc_heap::settings.concurrent)
34438         {
34439             bool is_marked = (!((o < hp->background_saved_highest_address) && (o >= hp->background_saved_lowest_address))||
34440                             hp->background_marked (o));
34441             return is_marked;
34442         }
34443         else
34444 #endif //BACKGROUND_GC
34445         {
34446             return (!((o < hp->highest_address) && (o >= hp->lowest_address))
34447                     || hp->is_mark_set (o));
34448         }
34449     }
34450     else
34451     {
34452         gc_heap* hp = gc_heap::heap_of (o);
34453         return (!((o < hp->gc_high) && (o >= hp->gc_low))
34454                 || hp->is_mark_set (o));
34455     }
34456 }
34457
34458 size_t GCHeap::GetPromotedBytes(int heap_index)
34459 {
34460 #ifdef BACKGROUND_GC
34461     if (gc_heap::settings.concurrent)
34462     {
34463         return gc_heap::bpromoted_bytes (heap_index);
34464     }
34465     else
34466 #endif //BACKGROUND_GC
34467     {
34468         return gc_heap::promoted_bytes (heap_index);
34469     }
34470 }
34471
34472 void GCHeap::SetYieldProcessorScalingFactor (float scalingFactor)
34473 {
34474     assert (yp_spin_count_unit != 0);
34475     int saved_yp_spin_count_unit = yp_spin_count_unit;
34476     yp_spin_count_unit = (int)((float)yp_spin_count_unit * scalingFactor / (float)9);
34477
34478     // It's very suspicious if it becomes 0
34479     if (yp_spin_count_unit == 0)
34480     {
34481         yp_spin_count_unit = saved_yp_spin_count_unit;
34482     }
34483 }
34484
34485 unsigned int GCHeap::WhichGeneration (Object* object)
34486 {
34487     gc_heap* hp = gc_heap::heap_of ((uint8_t*)object);
34488     unsigned int g = hp->object_gennum ((uint8_t*)object);
34489     dprintf (3, ("%Ix is in gen %d", (size_t)object, g));
34490     return g;
34491 }
34492
34493 bool GCHeap::IsEphemeral (Object* object)
34494 {
34495     uint8_t* o = (uint8_t*)object;
34496     gc_heap* hp = gc_heap::heap_of (o);
34497     return !!hp->ephemeral_pointer_p (o);
34498 }
34499
34500 // Return NULL if can't find next object. When EE is not suspended,
34501 // the result is not accurate: if the input arg is in gen0, the function could 
34502 // return zeroed out memory as next object
34503 Object * GCHeap::NextObj (Object * object)
34504 {
34505 #ifdef VERIFY_HEAP
34506     uint8_t* o = (uint8_t*)object;
34507
34508 #ifndef FEATURE_BASICFREEZE
34509     if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address)))
34510     {
34511         return NULL;
34512     }
34513 #endif //!FEATURE_BASICFREEZE
34514
34515     heap_segment * hs = gc_heap::find_segment (o, FALSE);
34516     if (!hs)
34517     {
34518         return NULL;
34519     }
34520
34521     BOOL large_object_p = heap_segment_loh_p (hs);
34522     if (large_object_p)
34523         return NULL; //could be racing with another core allocating. 
34524 #ifdef MULTIPLE_HEAPS
34525     gc_heap* hp = heap_segment_heap (hs);
34526 #else //MULTIPLE_HEAPS
34527     gc_heap* hp = 0;
34528 #endif //MULTIPLE_HEAPS
34529     unsigned int g = hp->object_gennum ((uint8_t*)object);
34530     if ((g == 0) && hp->settings.demotion)
34531         return NULL;//could be racing with another core allocating. 
34532     int align_const = get_alignment_constant (!large_object_p);
34533     uint8_t* nextobj = o + Align (size (o), align_const);
34534     if (nextobj <= o) // either overflow or 0 sized object.
34535     {
34536         return NULL;
34537     }
34538
34539     if ((nextobj < heap_segment_mem(hs)) || 
34540         (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) || 
34541         (nextobj >= hp->alloc_allocated))
34542     {
34543         return NULL;
34544     }
34545
34546     return (Object *)nextobj;
34547 #else
34548     return nullptr;
34549 #endif // VERIFY_HEAP
34550 }
34551
34552 // returns TRUE if the pointer is in one of the GC heaps.
34553 bool GCHeap::IsHeapPointer (void* vpObject, bool small_heap_only)
34554 {
34555     // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment 
34556     // no longer calls GCEvent::Wait which eventually takes a lock.
34557
34558     uint8_t* object = (uint8_t*) vpObject;
34559 #ifndef FEATURE_BASICFREEZE
34560     if (!((object < g_gc_highest_address) && (object >= g_gc_lowest_address)))
34561         return FALSE;
34562 #endif //!FEATURE_BASICFREEZE
34563
34564     heap_segment * hs = gc_heap::find_segment (object, small_heap_only);
34565     return !!hs;
34566 }
34567
34568 #ifdef STRESS_PINNING
34569 static n_promote = 0;
34570 #endif //STRESS_PINNING
34571 // promote an object
34572 void GCHeap::Promote(Object** ppObject, ScanContext* sc, uint32_t flags)
34573 {
34574     THREAD_NUMBER_FROM_CONTEXT;
34575 #ifndef MULTIPLE_HEAPS
34576     const int thread = 0;
34577 #endif //!MULTIPLE_HEAPS
34578
34579     uint8_t* o = (uint8_t*)*ppObject;
34580
34581     if (o == 0)
34582         return;
34583
34584 #ifdef DEBUG_DestroyedHandleValue
34585     // we can race with destroy handle during concurrent scan
34586     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
34587         return;
34588 #endif //DEBUG_DestroyedHandleValue
34589
34590     HEAP_FROM_THREAD;
34591
34592     gc_heap* hp = gc_heap::heap_of (o);
34593
34594     dprintf (3, ("Promote %Ix", (size_t)o));
34595
34596 #ifdef INTERIOR_POINTERS
34597     if (flags & GC_CALL_INTERIOR)
34598     {
34599         if ((o < hp->gc_low) || (o >= hp->gc_high))
34600         {
34601             return;
34602         }
34603         if ( (o = hp->find_object (o, hp->gc_low)) == 0)
34604         {
34605             return;
34606         }
34607
34608     }
34609 #endif //INTERIOR_POINTERS
34610
34611 #ifdef FEATURE_CONSERVATIVE_GC
34612     // For conservative GC, a value on stack may point to middle of a free object.
34613     // In this case, we don't need to promote the pointer.
34614     if (GCConfig::GetConservativeGC()
34615         && ((CObjectHeader*)o)->IsFree())
34616     {
34617         return;
34618     }
34619 #endif
34620
34621 #ifdef _DEBUG
34622     ((CObjectHeader*)o)->ValidatePromote(sc, flags);
34623 #else 
34624     UNREFERENCED_PARAMETER(sc);
34625 #endif //_DEBUG
34626
34627     if (flags & GC_CALL_PINNED)
34628         hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
34629
34630 #ifdef STRESS_PINNING
34631     if ((++n_promote % 20) == 1)
34632             hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
34633 #endif //STRESS_PINNING
34634
34635 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34636     size_t promoted_size_begin = hp->promoted_bytes (thread);
34637 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34638
34639     if ((o >= hp->gc_low) && (o < hp->gc_high))
34640     {
34641         hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
34642     }
34643
34644 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34645     size_t promoted_size_end = hp->promoted_bytes (thread);
34646     if (g_fEnableAppDomainMonitoring)
34647     {
34648         if (sc->pCurrentDomain)
34649         {
34650             GCToEEInterface::RecordSurvivedBytesForHeap((promoted_size_end - promoted_size_begin), thread, sc->pCurrentDomain);
34651         }
34652     }
34653 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34654
34655     STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL);
34656 }
34657
34658 void GCHeap::Relocate (Object** ppObject, ScanContext* sc,
34659                        uint32_t flags)
34660 {
34661     UNREFERENCED_PARAMETER(sc);
34662
34663     uint8_t* object = (uint8_t*)(Object*)(*ppObject);
34664     
34665     THREAD_NUMBER_FROM_CONTEXT;
34666
34667     //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject));
34668     dprintf (3, ("R: %Ix", (size_t)ppObject));
34669     
34670     if (object == 0)
34671         return;
34672
34673     gc_heap* hp = gc_heap::heap_of (object);
34674
34675 #ifdef _DEBUG
34676     if (!(flags & GC_CALL_INTERIOR))
34677     {
34678         // We cannot validate this object if it's in the condemned gen because it could 
34679         // be one of the objects that were overwritten by an artificial gap due to a pinned plug.
34680         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
34681         {
34682             ((CObjectHeader*)object)->Validate(FALSE);
34683         }
34684     }
34685 #endif //_DEBUG
34686
34687     dprintf (3, ("Relocate %Ix\n", (size_t)object));
34688
34689     uint8_t* pheader;
34690
34691     if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction)
34692     {
34693         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
34694         {
34695             return;
34696         }
34697
34698         if (gc_heap::loh_object_p (object))
34699         {
34700             pheader = hp->find_object (object, 0);
34701             if (pheader == 0)
34702             {
34703                 return;
34704             }
34705
34706             ptrdiff_t ref_offset = object - pheader;
34707             hp->relocate_address(&pheader THREAD_NUMBER_ARG);
34708             *ppObject = (Object*)(pheader + ref_offset);
34709             return;
34710         }
34711     }
34712
34713     {
34714         pheader = object;
34715         hp->relocate_address(&pheader THREAD_NUMBER_ARG);
34716         *ppObject = (Object*)pheader;
34717     }
34718
34719     STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0));
34720 }
34721
34722 /*static*/ bool GCHeap::IsObjectInFixedHeap(Object *pObj)
34723 {
34724     // For now we simply look at the size of the object to determine if it in the
34725     // fixed heap or not. If the bit indicating this gets set at some point
34726     // we should key off that instead.
34727     return size( pObj ) >= loh_size_threshold;
34728 }
34729
34730 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
34731 #ifdef STRESS_HEAP
34732
34733 void StressHeapDummy ();
34734
34735 static int32_t GCStressStartCount = -1;
34736 static int32_t GCStressCurCount = 0;
34737 static int32_t GCStressStartAtJit = -1;
34738
34739 // the maximum number of foreground GCs we'll induce during one BGC
34740 // (this number does not include "naturally" occuring GCs).
34741 static int32_t GCStressMaxFGCsPerBGC = -1;
34742
34743 // CLRRandom implementation can produce FPU exceptions if 
34744 // the test/application run by CLR is enabling any FPU exceptions. 
34745 // We want to avoid any unexpected exception coming from stress 
34746 // infrastructure, so CLRRandom is not an option.
34747 // The code below is a replicate of CRT rand() implementation.
34748 // Using CRT rand() is not an option because we will interfere with the user application
34749 // that may also use it. 
34750 int StressRNG(int iMaxValue)
34751 {
34752     static BOOL bisRandInit = FALSE;
34753     static int lHoldrand = 1L;
34754
34755     if (!bisRandInit)
34756     {
34757         lHoldrand = (int)time(NULL);
34758         bisRandInit = TRUE;
34759     }
34760     int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
34761     return randValue % iMaxValue;
34762 }
34763 #endif // STRESS_HEAP
34764 #endif // !FEATURE_REDHAWK
34765
34766 // free up object so that things will move and then do a GC
34767 //return TRUE if GC actually happens, otherwise FALSE
34768 bool GCHeap::StressHeap(gc_alloc_context * context)
34769 {
34770 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34771     alloc_context* acontext = static_cast<alloc_context*>(context);
34772     assert(context != nullptr);
34773
34774     // if GC stress was dynamically disabled during this run we return FALSE
34775     if (!GCStressPolicy::IsEnabled())
34776         return FALSE;
34777
34778 #ifdef _DEBUG
34779     if (g_pConfig->FastGCStressLevel() && !GCToEEInterface::GetThread()->StressHeapIsEnabled()) {
34780         return FALSE;
34781     }
34782
34783 #endif //_DEBUG
34784
34785     if ((g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_UNIQUE)
34786 #ifdef _DEBUG
34787         || g_pConfig->FastGCStressLevel() > 1
34788 #endif //_DEBUG
34789         ) {
34790         if (!Thread::UniqueStack(&acontext)) {
34791             return FALSE;
34792         }
34793     }
34794
34795 #ifdef BACKGROUND_GC
34796         // don't trigger a GC from the GC threads but still trigger GCs from user threads.
34797         if (GCToEEInterface::WasCurrentThreadCreatedByGC())
34798         {
34799             return FALSE;
34800         }
34801 #endif //BACKGROUND_GC
34802
34803         if (GCStressStartAtJit == -1 || GCStressStartCount == -1)
34804         {
34805             GCStressStartCount = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStressStart);
34806             GCStressStartAtJit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressStartAtJit);
34807         }
34808
34809         if (GCStressMaxFGCsPerBGC == -1)
34810         {
34811             GCStressMaxFGCsPerBGC = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressMaxFGCsPerBGC);
34812             if (g_pConfig->IsGCStressMix() && GCStressMaxFGCsPerBGC == -1)
34813                 GCStressMaxFGCsPerBGC = 6;
34814         }
34815
34816 #ifdef _DEBUG
34817         if (g_JitCount < GCStressStartAtJit)
34818             return FALSE;
34819 #endif //_DEBUG
34820
34821         // Allow programmer to skip the first N Stress GCs so that you can
34822         // get to the interesting ones faster.
34823         Interlocked::Increment(&GCStressCurCount);
34824         if (GCStressCurCount < GCStressStartCount)
34825             return FALSE;
34826
34827         // throttle the number of stress-induced GCs by a factor given by GCStressStep
34828         if ((GCStressCurCount % g_pConfig->GetGCStressStep()) != 0)
34829         {
34830             return FALSE;
34831         }
34832
34833 #ifdef BACKGROUND_GC
34834         if (IsConcurrentGCEnabled() && IsConcurrentGCInProgress())
34835         {
34836             // allow a maximum number of stress induced FGCs during one BGC
34837             if (gc_stress_fgcs_in_bgc >= GCStressMaxFGCsPerBGC)
34838                 return FALSE;
34839             ++gc_stress_fgcs_in_bgc;
34840         }
34841 #endif // BACKGROUND_GC
34842
34843     if (g_pStringClass == 0)
34844     {
34845         // If the String class has not been loaded, dont do any stressing. This should
34846         // be kept to a minimum to get as complete coverage as possible.
34847         _ASSERTE(g_fEEInit);
34848         return FALSE;
34849     }
34850
34851 #ifndef MULTIPLE_HEAPS
34852     static int32_t OneAtATime = -1;
34853
34854     // Only bother with this if the stress level is big enough and if nobody else is
34855     // doing it right now.  Note that some callers are inside the AllocLock and are
34856     // guaranteed synchronized.  But others are using AllocationContexts and have no
34857     // particular synchronization.
34858     //
34859     // For this latter case, we want a very high-speed way of limiting this to one
34860     // at a time.  A secondary advantage is that we release part of our StressObjs
34861     // buffer sparingly but just as effectively.
34862
34863     if (Interlocked::Increment(&OneAtATime) == 0 &&
34864         !TrackAllocations()) // Messing with object sizes can confuse the profiler (see ICorProfilerInfo::GetObjectSize)
34865     {
34866         StringObject* str;
34867
34868         // If the current string is used up
34869         if (HndFetchHandle(m_StressObjs[m_CurStressObj]) == 0)
34870         {
34871             // Populate handles with strings
34872             int i = m_CurStressObj;
34873             while(HndFetchHandle(m_StressObjs[i]) == 0)
34874             {
34875                 _ASSERTE(m_StressObjs[i] != 0);
34876                 unsigned strLen = ((unsigned)loh_size_threshold - 32) / sizeof(WCHAR);
34877                 unsigned strSize = PtrAlign(StringObject::GetSize(strLen));
34878                 
34879                 // update the cached type handle before allocating
34880                 SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass));
34881                 str = (StringObject*) pGenGCHeap->allocate (strSize, acontext);
34882                 if (str)
34883                 {
34884                     str->SetMethodTable (g_pStringClass);
34885                     str->SetStringLength (strLen);
34886                     HndAssignHandle(m_StressObjs[i], ObjectToOBJECTREF(str));
34887                 }
34888                 i = (i + 1) % NUM_HEAP_STRESS_OBJS;
34889                 if (i == m_CurStressObj) break;
34890             }
34891
34892             // advance the current handle to the next string
34893             m_CurStressObj = (m_CurStressObj + 1) % NUM_HEAP_STRESS_OBJS;
34894         }
34895
34896         // Get the current string
34897         str = (StringObject*) OBJECTREFToObject(HndFetchHandle(m_StressObjs[m_CurStressObj]));
34898         if (str)
34899         {
34900             // Chop off the end of the string and form a new object out of it.
34901             // This will 'free' an object at the begining of the heap, which will
34902             // force data movement.  Note that we can only do this so many times.
34903             // before we have to move on to the next string.
34904             unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31);
34905             if (str->GetStringLength() > sizeOfNewObj / sizeof(WCHAR))
34906             {
34907                 unsigned sizeToNextObj = (unsigned)Align(size(str));
34908                 uint8_t* freeObj = ((uint8_t*) str) + sizeToNextObj - sizeOfNewObj;
34909                 pGenGCHeap->make_unused_array (freeObj, sizeOfNewObj);                    
34910                 str->SetStringLength(str->GetStringLength() - (sizeOfNewObj / sizeof(WCHAR)));
34911             }
34912             else
34913             {
34914                 // Let the string itself become garbage.
34915                 // will be realloced next time around
34916                 HndAssignHandle(m_StressObjs[m_CurStressObj], 0);
34917             }
34918         }
34919     }
34920     Interlocked::Decrement(&OneAtATime);
34921 #endif // !MULTIPLE_HEAPS
34922     if (IsConcurrentGCEnabled())
34923     {
34924         int rgen = StressRNG(10);
34925
34926         // gen0:gen1:gen2 distribution: 40:40:20
34927         if (rgen >= 8)
34928             rgen = 2;
34929         else if (rgen >= 4)
34930             rgen = 1;
34931     else
34932             rgen = 0;
34933
34934         GarbageCollectTry (rgen, FALSE, collection_gcstress);
34935     }
34936     else
34937     {
34938         GarbageCollect(max_generation, FALSE, collection_gcstress);
34939     }
34940
34941     return TRUE;
34942 #else
34943     UNREFERENCED_PARAMETER(context);
34944     return FALSE;
34945 #endif // defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34946 }
34947
34948
34949 #ifdef FEATURE_PREMORTEM_FINALIZATION
34950 #define REGISTER_FOR_FINALIZATION(_object, _size) \
34951     hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))
34952 #else // FEATURE_PREMORTEM_FINALIZATION
34953 #define REGISTER_FOR_FINALIZATION(_object, _size) true
34954 #endif // FEATURE_PREMORTEM_FINALIZATION
34955
34956 #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do {  \
34957     if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size)))   \
34958     {                                                                                       \
34959         STRESS_LOG_OOM_STACK(_size);                                                        \
34960         return NULL;                                                                        \
34961     }                                                                                       \
34962 } while (false)
34963
34964 //
34965 // Small Object Allocator
34966 //
34967 //
34968 // Allocate small object with an alignment requirement of 8-bytes.
34969 Object*
34970 GCHeap::AllocAlign8(gc_alloc_context* ctx, size_t size, uint32_t flags )
34971 {
34972 #ifdef FEATURE_64BIT_ALIGNMENT
34973     CONTRACTL {
34974         NOTHROW;
34975         GC_TRIGGERS;
34976     } CONTRACTL_END;
34977
34978     alloc_context* acontext = static_cast<alloc_context*>(ctx);
34979
34980 #ifdef MULTIPLE_HEAPS
34981     if (acontext->get_alloc_heap() == 0)
34982     {
34983         AssignHeap (acontext);
34984         assert (acontext->get_alloc_heap());
34985     }
34986
34987     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34988 #else
34989     gc_heap* hp = pGenGCHeap;
34990 #endif //MULTIPLE_HEAPS
34991
34992     return AllocAlign8Common(hp, acontext, size, flags);
34993 #else
34994     UNREFERENCED_PARAMETER(ctx);
34995     UNREFERENCED_PARAMETER(size);
34996     UNREFERENCED_PARAMETER(flags);
34997     assert(!"should not call GCHeap::AllocAlign8 without FEATURE_64BIT_ALIGNMENT defined!");
34998     return nullptr;
34999 #endif  //FEATURE_64BIT_ALIGNMENT
35000 }
35001
35002 // Common code used by both variants of AllocAlign8 above.
35003 Object*
35004 GCHeap::AllocAlign8Common(void* _hp, alloc_context* acontext, size_t size, uint32_t flags)
35005 {
35006 #ifdef FEATURE_64BIT_ALIGNMENT
35007     CONTRACTL {
35008         NOTHROW;
35009         GC_TRIGGERS;
35010     } CONTRACTL_END;
35011
35012     gc_heap* hp = (gc_heap*)_hp;
35013
35014     TRIGGERSGC();
35015
35016     Object* newAlloc = NULL;
35017
35018 #ifdef TRACE_GC
35019 #ifdef COUNT_CYCLES
35020     AllocStart = GetCycleCount32();
35021     unsigned finish;
35022 #elif defined(ENABLE_INSTRUMENTATION)
35023     unsigned AllocStart = GetInstLogTime();
35024     unsigned finish;
35025 #endif //COUNT_CYCLES
35026 #endif //TRACE_GC
35027
35028     if (size < loh_size_threshold)
35029     {
35030 #ifdef TRACE_GC
35031         AllocSmallCount++;
35032 #endif //TRACE_GC
35033
35034         // Depending on where in the object the payload requiring 8-byte alignment resides we might have to
35035         // align the object header on an 8-byte boundary or midway between two such boundaries. The unaligned
35036         // case is indicated to the GC via the GC_ALLOC_ALIGN8_BIAS flag.
35037         size_t desiredAlignment = (flags & GC_ALLOC_ALIGN8_BIAS) ? 4 : 0;
35038
35039         // Retrieve the address of the next allocation from the context (note that we're inside the alloc
35040         // lock at this point).
35041         uint8_t*  result = acontext->alloc_ptr;
35042
35043         // Will an allocation at this point yield the correct alignment and fit into the remainder of the
35044         // context?
35045         if ((((size_t)result & 7) == desiredAlignment) && ((result + size) <= acontext->alloc_limit))
35046         {
35047             // Yes, we can just go ahead and make the allocation.
35048             newAlloc = (Object*) hp->allocate (size, acontext);
35049             ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
35050         }
35051         else
35052         {
35053             // No, either the next available address is not aligned in the way we require it or there's
35054             // not enough space to allocate an object of the required size. In both cases we allocate a
35055             // padding object (marked as a free object). This object's size is such that it will reverse
35056             // the alignment of the next header (asserted below).
35057             //
35058             // We allocate both together then decide based on the result whether we'll format the space as
35059             // free object + real object or real object + free object.
35060             ASSERT((Align(min_obj_size) & 7) == 4);
35061             CObjectHeader *freeobj = (CObjectHeader*) hp->allocate (Align(size) + Align(min_obj_size), acontext);
35062             if (freeobj)
35063             {
35064                 if (((size_t)freeobj & 7) == desiredAlignment)
35065                 {
35066                     // New allocation has desired alignment, return this one and place the free object at the
35067                     // end of the allocated space.
35068                     newAlloc = (Object*)freeobj;
35069                     freeobj = (CObjectHeader*)((uint8_t*)freeobj + Align(size));
35070                 }
35071                 else
35072                 {
35073                     // New allocation is still mis-aligned, format the initial space as a free object and the
35074                     // rest of the space should be correctly aligned for the real object.
35075                     newAlloc = (Object*)((uint8_t*)freeobj + Align(min_obj_size));
35076                     ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
35077                 }
35078                 freeobj->SetFree(min_obj_size);
35079             }
35080         }
35081     }
35082     else
35083     {
35084         // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't
35085         // support mis-aligned object headers so we can't support biased headers as above. Luckily for us
35086         // we've managed to arrange things so the only case where we see a bias is for boxed value types and
35087         // these can never get large enough to be allocated on the LOH.
35088         ASSERT(65536 < loh_size_threshold);
35089         ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0);
35090
35091         alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
35092
35093         newAlloc = (Object*) hp->allocate_large_object (size, acontext->alloc_bytes_loh);
35094         ASSERT(((size_t)newAlloc & 7) == 0);
35095     }
35096
35097     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
35098
35099 #ifdef TRACE_GC
35100 #ifdef COUNT_CYCLES
35101     finish = GetCycleCount32();
35102 #elif defined(ENABLE_INSTRUMENTATION)
35103     finish = GetInstLogTime();
35104 #endif //COUNT_CYCLES
35105     AllocDuration += finish - AllocStart;
35106     AllocCount++;
35107 #endif //TRACE_GC
35108     return newAlloc;
35109 #else
35110     UNREFERENCED_PARAMETER(_hp);
35111     UNREFERENCED_PARAMETER(acontext);
35112     UNREFERENCED_PARAMETER(size);
35113     UNREFERENCED_PARAMETER(flags);
35114     assert(!"Should not call GCHeap::AllocAlign8Common without FEATURE_64BIT_ALIGNMENT defined!");
35115     return nullptr;
35116 #endif // FEATURE_64BIT_ALIGNMENT
35117 }
35118
35119 Object *
35120 GCHeap::AllocLHeap( size_t size, uint32_t flags REQD_ALIGN_DCL)
35121 {
35122     CONTRACTL {
35123         NOTHROW;
35124         GC_TRIGGERS;
35125     } CONTRACTL_END;
35126
35127     TRIGGERSGC();
35128
35129     Object* newAlloc = NULL;
35130
35131 #ifdef TRACE_GC
35132 #ifdef COUNT_CYCLES
35133     AllocStart = GetCycleCount32();
35134     unsigned finish;
35135 #elif defined(ENABLE_INSTRUMENTATION)
35136     unsigned AllocStart = GetInstLogTime();
35137     unsigned finish;
35138 #endif //COUNT_CYCLES
35139 #endif //TRACE_GC
35140
35141 #ifdef MULTIPLE_HEAPS
35142     //take the first heap....
35143     gc_heap* hp = gc_heap::g_heaps[0];
35144 #else
35145     gc_heap* hp = pGenGCHeap;
35146 #ifdef _PREFAST_
35147     // prefix complains about us dereferencing hp in wks build even though we only access static members
35148     // this way. not sure how to shut it up except for this ugly workaround:
35149     PREFIX_ASSUME(hp != NULL);
35150 #endif //_PREFAST_
35151 #endif //MULTIPLE_HEAPS
35152
35153     alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
35154
35155     newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
35156 #ifdef FEATURE_STRUCTALIGN
35157     newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
35158 #endif // FEATURE_STRUCTALIGN
35159     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
35160
35161 #ifdef TRACE_GC
35162 #ifdef COUNT_CYCLES
35163     finish = GetCycleCount32();
35164 #elif defined(ENABLE_INSTRUMENTATION)
35165     finish = GetInstLogTime();
35166 #endif //COUNT_CYCLES
35167     AllocDuration += finish - AllocStart;
35168     AllocCount++;
35169 #endif //TRACE_GC
35170     return newAlloc;
35171 }
35172
35173 Object*
35174 GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_DCL)
35175 {
35176     CONTRACTL {
35177         NOTHROW;
35178         GC_TRIGGERS;
35179     } CONTRACTL_END;
35180
35181     TRIGGERSGC();
35182
35183     Object* newAlloc = NULL;
35184     alloc_context* acontext = static_cast<alloc_context*>(context);
35185
35186 #ifdef TRACE_GC
35187 #ifdef COUNT_CYCLES
35188     AllocStart = GetCycleCount32();
35189     unsigned finish;
35190 #elif defined(ENABLE_INSTRUMENTATION)
35191     unsigned AllocStart = GetInstLogTime();
35192     unsigned finish;
35193 #endif //COUNT_CYCLES
35194 #endif //TRACE_GC
35195
35196 #ifdef MULTIPLE_HEAPS
35197     if (acontext->get_alloc_heap() == 0)
35198     {
35199         AssignHeap (acontext);
35200         assert (acontext->get_alloc_heap());
35201     }
35202 #endif //MULTIPLE_HEAPS
35203
35204 #ifdef MULTIPLE_HEAPS
35205     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
35206 #else
35207     gc_heap* hp = pGenGCHeap;
35208 #ifdef _PREFAST_
35209     // prefix complains about us dereferencing hp in wks build even though we only access static members
35210     // this way. not sure how to shut it up except for this ugly workaround:
35211     PREFIX_ASSUME(hp != NULL);
35212 #endif //_PREFAST_
35213 #endif //MULTIPLE_HEAPS
35214
35215     if (size < loh_size_threshold)
35216     {
35217
35218 #ifdef TRACE_GC
35219         AllocSmallCount++;
35220 #endif //TRACE_GC
35221         newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
35222 #ifdef FEATURE_STRUCTALIGN
35223         newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext);
35224 #endif // FEATURE_STRUCTALIGN
35225 //        ASSERT (newAlloc);
35226     }
35227     else 
35228     {
35229         newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
35230 #ifdef FEATURE_STRUCTALIGN
35231         newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
35232 #endif // FEATURE_STRUCTALIGN
35233     }
35234
35235     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
35236
35237 #ifdef TRACE_GC
35238 #ifdef COUNT_CYCLES
35239     finish = GetCycleCount32();
35240 #elif defined(ENABLE_INSTRUMENTATION)
35241     finish = GetInstLogTime();
35242 #endif //COUNT_CYCLES
35243     AllocDuration += finish - AllocStart;
35244     AllocCount++;
35245 #endif //TRACE_GC
35246     return newAlloc;
35247 }
35248
35249 void
35250 GCHeap::FixAllocContext (gc_alloc_context* context, void* arg, void *heap)
35251 {
35252     alloc_context* acontext = static_cast<alloc_context*>(context);
35253 #ifdef MULTIPLE_HEAPS
35254
35255     if (arg != 0)
35256         acontext->alloc_count = 0;
35257
35258     uint8_t * alloc_ptr = acontext->alloc_ptr;
35259
35260     if (!alloc_ptr)
35261         return;
35262
35263     // The acontext->alloc_heap can be out of sync with the ptrs because
35264     // of heap re-assignment in allocate
35265     gc_heap* hp = gc_heap::heap_of (alloc_ptr);
35266 #else
35267     gc_heap* hp = pGenGCHeap;
35268 #endif //MULTIPLE_HEAPS
35269
35270     if (heap == NULL || heap == hp)
35271     {
35272         hp->fix_allocation_context (acontext, ((arg != 0)? TRUE : FALSE),
35273                                     get_alignment_constant(TRUE));
35274     }
35275 }
35276
35277 Object*
35278 GCHeap::GetContainingObject (void *pInteriorPtr, bool fCollectedGenOnly)
35279 {
35280     uint8_t *o = (uint8_t*)pInteriorPtr;
35281
35282     gc_heap* hp = gc_heap::heap_of (o);
35283
35284     uint8_t* lowest = (fCollectedGenOnly ? hp->gc_low : hp->lowest_address);
35285     uint8_t* highest = (fCollectedGenOnly ? hp->gc_high : hp->highest_address);
35286
35287     if (o >= lowest && o < highest)
35288     {
35289         o = hp->find_object (o, lowest);
35290     }
35291     else
35292     {
35293         o = NULL;
35294     }
35295     
35296     return (Object *)o;
35297 }
35298
35299 BOOL should_collect_optimized (dynamic_data* dd, BOOL low_memory_p)
35300 {
35301     if (dd_new_allocation (dd) < 0)
35302     {
35303         return TRUE;
35304     }
35305
35306     if (((float)(dd_new_allocation (dd)) / (float)dd_desired_allocation (dd)) < (low_memory_p ? 0.7 : 0.3))
35307     {
35308         return TRUE;
35309     }
35310
35311     return FALSE;
35312 }
35313
35314 //----------------------------------------------------------------------------
35315 // #GarbageCollector
35316 //
35317 //  API to ensure that a complete new garbage collection takes place
35318 //
35319 HRESULT
35320 GCHeap::GarbageCollect (int generation, bool low_memory_p, int mode)
35321 {
35322 #if defined(BIT64) 
35323     if (low_memory_p)
35324     {
35325         size_t total_allocated = 0;
35326         size_t total_desired = 0;
35327 #ifdef MULTIPLE_HEAPS
35328         int hn = 0;
35329         for (hn = 0; hn < gc_heap::n_heaps; hn++)
35330         {
35331             gc_heap* hp = gc_heap::g_heaps [hn];
35332             total_desired += dd_desired_allocation (hp->dynamic_data_of (0));
35333             total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))-
35334                 dd_new_allocation (hp->dynamic_data_of (0));
35335         }
35336 #else
35337         gc_heap* hp = pGenGCHeap;
35338         total_desired = dd_desired_allocation (hp->dynamic_data_of (0));
35339         total_allocated = dd_desired_allocation (hp->dynamic_data_of (0))-
35340             dd_new_allocation (hp->dynamic_data_of (0));
35341 #endif //MULTIPLE_HEAPS
35342
35343         if ((total_desired > gc_heap::mem_one_percent) && (total_allocated < gc_heap::mem_one_percent))
35344         {
35345             dprintf (2, ("Async low mem but we've only allocated %d (< 10%% of physical mem) out of %d, returning",
35346                          total_allocated, total_desired));
35347
35348             return S_OK;
35349         }
35350     }
35351 #endif // BIT64 
35352
35353 #ifdef MULTIPLE_HEAPS
35354     gc_heap* hpt = gc_heap::g_heaps[0];
35355 #else
35356     gc_heap* hpt = 0;
35357 #endif //MULTIPLE_HEAPS
35358
35359     generation = (generation < 0) ? max_generation : min (generation, max_generation);
35360     dynamic_data* dd = hpt->dynamic_data_of (generation);
35361
35362 #ifdef BACKGROUND_GC
35363     if (recursive_gc_sync::background_running_p())
35364     {
35365         if ((mode == collection_optimized) || (mode & collection_non_blocking))
35366         {
35367             return S_OK;
35368         }
35369         if (mode & collection_blocking)
35370         {
35371             pGenGCHeap->background_gc_wait();
35372             if (mode & collection_optimized)
35373             {
35374                 return S_OK;
35375             }
35376         }
35377     }
35378 #endif //BACKGROUND_GC
35379
35380     if (mode & collection_optimized)
35381     {
35382         if (pGenGCHeap->gc_started)
35383         {
35384             return S_OK;
35385         }
35386         else 
35387         {
35388             BOOL should_collect = FALSE;
35389             BOOL should_check_loh = (generation == max_generation);
35390 #ifdef MULTIPLE_HEAPS
35391             for (int i = 0; i < gc_heap::n_heaps; i++)
35392             {
35393                 dynamic_data* dd1 = gc_heap::g_heaps [i]->dynamic_data_of (generation);
35394                 dynamic_data* dd2 = (should_check_loh ? 
35395                                      (gc_heap::g_heaps [i]->dynamic_data_of (max_generation + 1)) :
35396                                      0);
35397
35398                 if (should_collect_optimized (dd1, low_memory_p))
35399                 {
35400                     should_collect = TRUE;
35401                     break;
35402                 }
35403                 if (dd2 && should_collect_optimized (dd2, low_memory_p))
35404                 {
35405                     should_collect = TRUE;
35406                     break;
35407                 }
35408             }
35409 #else
35410             should_collect = should_collect_optimized (dd, low_memory_p);
35411             if (!should_collect && should_check_loh)
35412             {
35413                 should_collect = 
35414                     should_collect_optimized (hpt->dynamic_data_of (max_generation + 1), low_memory_p);
35415             }
35416 #endif //MULTIPLE_HEAPS
35417             if (!should_collect)
35418             {
35419                 return S_OK;
35420             }
35421         }
35422     }
35423
35424     size_t CollectionCountAtEntry = dd_collection_count (dd);
35425     size_t BlockingCollectionCountAtEntry = gc_heap::full_gc_counts[gc_type_blocking];
35426     size_t CurrentCollectionCount = 0;
35427
35428 retry:
35429
35430     CurrentCollectionCount = GarbageCollectTry(generation, low_memory_p, mode);
35431     
35432     if ((mode & collection_blocking) && 
35433         (generation == max_generation) && 
35434         (gc_heap::full_gc_counts[gc_type_blocking] == BlockingCollectionCountAtEntry))
35435     {
35436 #ifdef BACKGROUND_GC
35437         if (recursive_gc_sync::background_running_p())
35438         {
35439             pGenGCHeap->background_gc_wait();
35440         }
35441 #endif //BACKGROUND_GC
35442
35443         goto retry;
35444     }
35445
35446     if (CollectionCountAtEntry == CurrentCollectionCount)
35447     {
35448         goto retry;
35449     }
35450
35451     return S_OK;
35452 }
35453
35454 size_t
35455 GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
35456 {
35457     int gen = (generation < 0) ? 
35458                max_generation : min (generation, max_generation);
35459
35460     gc_reason reason = reason_empty;
35461     
35462     if (low_memory_p) 
35463     {
35464         if (mode & collection_blocking)
35465         {
35466             reason = reason_lowmemory_blocking;
35467         }
35468         else
35469         {
35470             reason = reason_lowmemory;
35471         }
35472     }
35473     else
35474     {
35475         reason = reason_induced;
35476     }
35477
35478     if (reason == reason_induced)
35479     {
35480         if (mode & collection_compacting)
35481         {
35482             reason = reason_induced_compacting;
35483         }
35484         else if (mode & collection_non_blocking)
35485         {
35486             reason = reason_induced_noforce;
35487         }
35488 #ifdef STRESS_HEAP
35489         else if (mode & collection_gcstress)
35490         {
35491             reason = reason_gcstress;
35492         }
35493 #endif
35494     }
35495
35496     return GarbageCollectGeneration (gen, reason);
35497 }
35498
35499 void gc_heap::do_pre_gc()
35500 {
35501     STRESS_LOG_GC_STACK;
35502
35503 #ifdef STRESS_LOG
35504     STRESS_LOG_GC_START(VolatileLoad(&settings.gc_index),
35505                         (uint32_t)settings.condemned_generation,
35506                         (uint32_t)settings.reason);
35507 #endif // STRESS_LOG
35508
35509 #ifdef MULTIPLE_HEAPS
35510     gc_heap* hp = g_heaps[0];
35511 #else
35512     gc_heap* hp = 0;
35513 #endif //MULTIPLE_HEAPS
35514
35515 #ifdef BACKGROUND_GC
35516     settings.b_state = hp->current_bgc_state;
35517 #endif //BACKGROUND_GC
35518
35519 #ifdef TRACE_GC
35520     size_t total_allocated_since_last_gc = get_total_allocated_since_last_gc();
35521 #ifdef BACKGROUND_GC
35522     dprintf (1, ("*GC* %d(gen0:%d)(%d)(alloc: %Id)(%s)(%d)", 
35523         VolatileLoad(&settings.gc_index), 
35524         dd_collection_count (hp->dynamic_data_of (0)),
35525         settings.condemned_generation,
35526         total_allocated_since_last_gc,
35527         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")),
35528         settings.b_state));
35529 #else
35530     dprintf (1, ("*GC* %d(gen0:%d)(%d)(alloc: %Id)", 
35531         VolatileLoad(&settings.gc_index), 
35532         dd_collection_count(hp->dynamic_data_of(0)),
35533         settings.condemned_generation,
35534         total_allocated_since_last_gc));
35535 #endif //BACKGROUND_GC
35536
35537     if (heap_hard_limit)
35538     {
35539         size_t total_heap_committed = get_total_committed_size();
35540         size_t total_heap_committed_recorded = current_total_committed - current_total_committed_bookkeeping;
35541         dprintf (1, ("(%d)GC commit BEG #%Id: %Id (recorded: %Id)", 
35542             settings.condemned_generation,
35543             (size_t)settings.gc_index, total_heap_committed, total_heap_committed_recorded));
35544     }
35545 #endif //TRACE_GC
35546
35547     // TODO: this can happen...it's because of the way we are calling
35548     // do_pre_gc, will fix later.
35549     //if (last_gc_index > VolatileLoad(&settings.gc_index))
35550     //{
35551     //    FATAL_GC_ERROR();
35552     //}
35553
35554     last_gc_index = VolatileLoad(&settings.gc_index);
35555     GCHeap::UpdatePreGCCounters();
35556
35557     if (settings.concurrent)
35558     {
35559 #ifdef BACKGROUND_GC
35560         full_gc_counts[gc_type_background]++;
35561 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
35562         GCHeap::gc_stress_fgcs_in_bgc = 0;
35563 #endif // STRESS_HEAP && !FEATURE_REDHAWK
35564 #endif // BACKGROUND_GC
35565     }
35566     else
35567     {
35568         if (settings.condemned_generation == max_generation)
35569         {
35570             full_gc_counts[gc_type_blocking]++;
35571         }
35572         else
35573         {
35574 #ifdef BACKGROUND_GC
35575             if (settings.background_p)
35576             {
35577                 ephemeral_fgc_counts[settings.condemned_generation]++;
35578             }
35579 #endif //BACKGROUND_GC
35580         }
35581     }
35582
35583 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
35584     if (g_fEnableAppDomainMonitoring)
35585     {
35586         GCToEEInterface::ResetTotalSurvivedBytes();
35587     }
35588 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
35589 }
35590
35591 #ifdef GC_CONFIG_DRIVEN
35592 void gc_heap::record_interesting_info_per_heap()
35593 {
35594     // datapoints are always from the last blocking GC so don't record again
35595     // for BGCs.
35596     if (!(settings.concurrent))
35597     {
35598         for (int i = 0; i < max_idp_count; i++)
35599         {
35600             interesting_data_per_heap[i] += interesting_data_per_gc[i];
35601         }
35602     }
35603
35604     int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
35605     if (compact_reason >= 0)
35606         (compact_reasons_per_heap[compact_reason])++;
35607     int expand_mechanism = get_gc_data_per_heap()->get_mechanism (gc_heap_expand);
35608     if (expand_mechanism >= 0)
35609         (expand_mechanisms_per_heap[expand_mechanism])++;
35610
35611     for (int i = 0; i < max_gc_mechanism_bits_count; i++)
35612     {
35613         if (get_gc_data_per_heap()->is_mechanism_bit_set ((gc_mechanism_bit_per_heap)i))
35614             (interesting_mechanism_bits_per_heap[i])++;
35615     }
35616
35617     //         h#  | GC  | gen | C   | EX  | NF  | BF  | ML  | DM  || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP |
35618     cprintf (("%2d | %6d | %1d | %1s | %2s | %2s | %2s | %2s | %2s || %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id |",
35619             heap_number,
35620             (size_t)settings.gc_index,
35621             settings.condemned_generation,
35622             // TEMP - I am just doing this for wks GC 'cuase I wanna see the pattern of doing C/S GCs.
35623             (settings.compaction ? (((compact_reason >= 0) && gc_heap_compact_reason_mandatory_p[compact_reason]) ? "M" : "W") : ""), // compaction
35624             ((expand_mechanism >= 0)? "X" : ""), // EX
35625             ((expand_mechanism == expand_reuse_normal) ? "X" : ""), // NF
35626             ((expand_mechanism == expand_reuse_bestfit) ? "X" : ""), // BF
35627             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_mark_list_bit) ? "X" : ""), // ML
35628             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_demotion_bit) ? "X" : ""), // DM
35629             interesting_data_per_gc[idp_pre_short],
35630             interesting_data_per_gc[idp_post_short],
35631             interesting_data_per_gc[idp_merged_pin],
35632             interesting_data_per_gc[idp_converted_pin],
35633             interesting_data_per_gc[idp_pre_pin],
35634             interesting_data_per_gc[idp_post_pin],
35635             interesting_data_per_gc[idp_pre_and_post_pin],
35636             interesting_data_per_gc[idp_pre_short_padded],
35637             interesting_data_per_gc[idp_post_short_padded]));
35638 }
35639
35640 void gc_heap::record_global_mechanisms()
35641 {
35642     for (int i = 0; i < max_global_mechanisms_count; i++)
35643     {
35644         if (gc_data_global.get_mechanism_p ((gc_global_mechanism_p)i))
35645         {
35646             ::record_global_mechanism (i);
35647         }
35648     }
35649 }
35650
35651 BOOL gc_heap::should_do_sweeping_gc (BOOL compact_p)
35652 {
35653     if (!compact_ratio)
35654         return (!compact_p);
35655
35656     size_t compact_count = compact_or_sweep_gcs[0];
35657     size_t sweep_count = compact_or_sweep_gcs[1];
35658
35659     size_t total_count = compact_count + sweep_count;
35660     BOOL should_compact = compact_p;
35661     if (total_count > 3)
35662     {
35663         if (compact_p)
35664         {
35665             int temp_ratio = (int)((compact_count + 1) * 100 / (total_count + 1));
35666             if (temp_ratio > compact_ratio)
35667             {
35668                 // cprintf (("compact would be: %d, total_count: %d, ratio would be %d%% > target\n",
35669                 //     (compact_count + 1), (total_count + 1), temp_ratio));
35670                 should_compact = FALSE;
35671             }
35672         }
35673         else
35674         {
35675             int temp_ratio = (int)((sweep_count + 1) * 100 / (total_count + 1));
35676             if (temp_ratio > (100 - compact_ratio))
35677             {
35678                 // cprintf (("sweep would be: %d, total_count: %d, ratio would be %d%% > target\n",
35679                 //     (sweep_count + 1), (total_count + 1), temp_ratio));
35680                 should_compact = TRUE;
35681             }
35682         }
35683     }
35684
35685     return !should_compact;
35686 }
35687 #endif //GC_CONFIG_DRIVEN
35688
35689 bool gc_heap::is_pm_ratio_exceeded()
35690 {
35691     size_t maxgen_frag = 0;
35692     size_t maxgen_size = 0;
35693     size_t total_heap_size = get_total_heap_size();
35694
35695 #ifdef MULTIPLE_HEAPS
35696     for (int i = 0; i < gc_heap::n_heaps; i++)
35697     {
35698         gc_heap* hp = gc_heap::g_heaps[i];
35699 #else //MULTIPLE_HEAPS
35700     {
35701         gc_heap* hp = pGenGCHeap;
35702 #endif //MULTIPLE_HEAPS
35703
35704         maxgen_frag += dd_fragmentation (hp->dynamic_data_of (max_generation));
35705         maxgen_size += hp->generation_size (max_generation);
35706     }
35707
35708     double maxgen_ratio = (double)maxgen_size / (double)total_heap_size;
35709     double maxgen_frag_ratio = (double)maxgen_frag / (double)maxgen_size;
35710     dprintf (GTC_LOG, ("maxgen %Id(%d%% total heap), frag: %Id (%d%% maxgen)",
35711         maxgen_size, (int)(maxgen_ratio * 100.0), 
35712         maxgen_frag, (int)(maxgen_frag_ratio * 100.0)));
35713
35714     bool maxgen_highfrag_p = ((maxgen_ratio > 0.5) && (maxgen_frag_ratio > 0.1));
35715
35716     // We need to adjust elevation here because if there's enough fragmentation it's not
35717     // unproductive.
35718     if (maxgen_highfrag_p)
35719     {
35720         settings.should_lock_elevation = FALSE;
35721         dprintf (GTC_LOG, ("high frag gen2, turn off elevation"));
35722     }
35723
35724     return maxgen_highfrag_p;
35725 }
35726
35727 void gc_heap::do_post_gc()
35728 {
35729     if (!settings.concurrent)
35730     {
35731         initGCShadow();
35732     }
35733
35734 #ifdef TRACE_GC
35735 #ifdef COUNT_CYCLES
35736     AllocStart = GetCycleCount32();
35737 #else
35738     AllocStart = clock();
35739 #endif //COUNT_CYCLES
35740 #endif //TRACE_GC
35741
35742 #ifdef MULTIPLE_HEAPS
35743     gc_heap* hp = g_heaps[0];
35744 #else
35745     gc_heap* hp = 0;
35746 #endif //MULTIPLE_HEAPS
35747     
35748     GCToEEInterface::GcDone(settings.condemned_generation);
35749
35750     GCToEEInterface::DiagGCEnd(VolatileLoad(&settings.gc_index),
35751                          (uint32_t)settings.condemned_generation,
35752                          (uint32_t)settings.reason,
35753                          !!settings.concurrent);
35754
35755     //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)", 
35756     dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)", 
35757         VolatileLoad(&settings.gc_index), 
35758         dd_collection_count(hp->dynamic_data_of(0)),
35759         settings.condemned_generation,
35760         (settings.concurrent ? "BGC" : "GC")));
35761
35762     if (settings.exit_memory_load != 0)
35763         last_gc_memory_load = settings.exit_memory_load;
35764     else if (settings.entry_memory_load != 0)
35765         last_gc_memory_load = settings.entry_memory_load;
35766
35767     last_gc_heap_size = get_total_heap_size();
35768     last_gc_fragmentation = get_total_fragmentation();
35769
35770 #ifdef TRACE_GC
35771     if (heap_hard_limit)
35772     {
35773         size_t total_heap_committed = get_total_committed_size();
35774         size_t total_heap_committed_recorded = current_total_committed - current_total_committed_bookkeeping;
35775         dprintf (1, ("(%d)GC commit END #%Id: %Id (recorded: %Id), heap %Id, frag: %Id", 
35776             settings.condemned_generation,
35777             (size_t)settings.gc_index, total_heap_committed, total_heap_committed_recorded,
35778             last_gc_heap_size, last_gc_fragmentation));
35779     }
35780 #endif //TRACE_GC
35781
35782     // Note we only do this at the end of full blocking GCs because we do not want
35783     // to turn on this provisional mode during the middle of a BGC.
35784     if ((settings.condemned_generation == max_generation) && (!settings.concurrent))
35785     {
35786         if (pm_stress_on)
35787         {
35788             size_t full_compacting_gc_count = full_gc_counts[gc_type_compacting];
35789             if (provisional_mode_triggered)
35790             {
35791                 uint64_t r = gc_rand::get_rand(10);
35792                 if ((full_compacting_gc_count - provisional_triggered_gc_count) >= r)
35793                 {
35794                     provisional_mode_triggered = false;
35795                     provisional_off_gc_count = full_compacting_gc_count;
35796                     dprintf (GTC_LOG, ("%Id NGC2s when turned on, %Id NGCs since(%Id)",
35797                         provisional_triggered_gc_count, (full_compacting_gc_count - provisional_triggered_gc_count),
35798                         num_provisional_triggered));
35799                 }
35800             }
35801             else
35802             {
35803                 uint64_t r = gc_rand::get_rand(5);
35804                 if ((full_compacting_gc_count - provisional_off_gc_count) >= r)
35805                 {
35806                     provisional_mode_triggered = true;
35807                     provisional_triggered_gc_count = full_compacting_gc_count;
35808                     num_provisional_triggered++;
35809                     dprintf (GTC_LOG, ("%Id NGC2s when turned off, %Id NGCs since(%Id)",
35810                         provisional_off_gc_count, (full_compacting_gc_count - provisional_off_gc_count),
35811                         num_provisional_triggered));
35812                 }
35813             }
35814         }
35815         else
35816         {
35817             if (provisional_mode_triggered)
35818             {
35819                 if ((settings.entry_memory_load < high_memory_load_th) ||
35820                     !is_pm_ratio_exceeded())
35821                 {
35822                     dprintf (GTC_LOG, ("turning off PM"));
35823                     provisional_mode_triggered = false;
35824                 }
35825             }
35826             else if ((settings.entry_memory_load >= high_memory_load_th) && is_pm_ratio_exceeded())
35827             {
35828                 dprintf (GTC_LOG, ("highmem && highfrag - turning on PM"));
35829                 provisional_mode_triggered = true;
35830                 num_provisional_triggered++;
35831             }
35832         }
35833     }
35834
35835     GCHeap::UpdatePostGCCounters();
35836 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
35837     //if (g_fEnableARM)
35838     //{
35839     //    SystemDomain::GetADSurvivedBytes();
35840     //}
35841 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
35842
35843 #ifdef STRESS_LOG
35844     STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index),
35845                       (uint32_t)settings.condemned_generation,
35846                       (uint32_t)settings.reason);
35847 #endif // STRESS_LOG
35848
35849 #ifdef GC_CONFIG_DRIVEN
35850     if (!settings.concurrent)
35851     {
35852         if (settings.compaction)
35853             (compact_or_sweep_gcs[0])++;
35854         else
35855             (compact_or_sweep_gcs[1])++;
35856     }
35857
35858 #ifdef MULTIPLE_HEAPS
35859     for (int i = 0; i < n_heaps; i++)
35860         g_heaps[i]->record_interesting_info_per_heap();
35861 #else
35862     record_interesting_info_per_heap();
35863 #endif //MULTIPLE_HEAPS
35864     record_global_mechanisms();
35865 #endif //GC_CONFIG_DRIVEN
35866 }
35867
35868 unsigned GCHeap::GetGcCount()
35869 {
35870     return (unsigned int)VolatileLoad(&pGenGCHeap->settings.gc_index);
35871 }
35872
35873 size_t
35874 GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
35875 {
35876     dprintf (2, ("triggered a GC!"));
35877
35878 #ifdef MULTIPLE_HEAPS
35879     gc_heap* hpt = gc_heap::g_heaps[0];
35880 #else
35881     gc_heap* hpt = 0;
35882 #endif //MULTIPLE_HEAPS
35883     bool cooperative_mode = true;
35884     dynamic_data* dd = hpt->dynamic_data_of (gen);
35885     size_t localCount = dd_collection_count (dd);
35886
35887     enter_spin_lock (&gc_heap::gc_lock);
35888     dprintf (SPINLOCK_LOG, ("GC Egc"));
35889     ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock);
35890
35891     //don't trigger another GC if one was already in progress
35892     //while waiting for the lock
35893     {
35894         size_t col_count = dd_collection_count (dd);
35895
35896         if (localCount != col_count)
35897         {
35898 #ifdef SYNCHRONIZATION_STATS
35899             gc_lock_contended++;
35900 #endif //SYNCHRONIZATION_STATS
35901             dprintf (SPINLOCK_LOG, ("no need GC Lgc"));
35902             leave_spin_lock (&gc_heap::gc_lock);
35903
35904             // We don't need to release msl here 'cause this means a GC
35905             // has happened and would have release all msl's.
35906             return col_count;
35907          }
35908     }
35909
35910 #ifdef COUNT_CYCLES
35911     int gc_start = GetCycleCount32();
35912 #endif //COUNT_CYCLES
35913
35914 #ifdef TRACE_GC
35915 #ifdef COUNT_CYCLES
35916     AllocDuration += GetCycleCount32() - AllocStart;
35917 #else
35918     AllocDuration += clock() - AllocStart;
35919 #endif //COUNT_CYCLES
35920 #endif //TRACE_GC
35921
35922         gc_heap::g_low_memory_status = (reason == reason_lowmemory) || 
35923                                        (reason == reason_lowmemory_blocking) ||
35924                                        (gc_heap::latency_level == latency_level_memory_footprint);
35925
35926         gc_trigger_reason = reason;
35927
35928 #ifdef MULTIPLE_HEAPS
35929     for (int i = 0; i < gc_heap::n_heaps; i++)
35930     {
35931         gc_heap::g_heaps[i]->reset_gc_done();
35932     }
35933 #else
35934     gc_heap::reset_gc_done();
35935 #endif //MULTIPLE_HEAPS
35936
35937     gc_heap::gc_started = TRUE;
35938
35939     {
35940         init_sync_log_stats();
35941
35942 #ifndef MULTIPLE_HEAPS
35943         cooperative_mode = gc_heap::enable_preemptive ();
35944
35945         dprintf (2, ("Suspending EE"));
35946         BEGIN_TIMING(suspend_ee_during_log);
35947         GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
35948         END_TIMING(suspend_ee_during_log);
35949         gc_heap::proceed_with_gc_p = gc_heap::should_proceed_with_gc();
35950         gc_heap::disable_preemptive (cooperative_mode);
35951         if (gc_heap::proceed_with_gc_p)
35952             pGenGCHeap->settings.init_mechanisms();
35953         else
35954             gc_heap::update_collection_counts_for_no_gc();
35955
35956 #endif //!MULTIPLE_HEAPS
35957     }
35958
35959 // MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0));
35960
35961 #ifdef TRACE_GC
35962 #ifdef COUNT_CYCLES
35963     unsigned start;
35964     unsigned finish;
35965     start = GetCycleCount32();
35966 #else
35967     clock_t start;
35968     clock_t finish;
35969     start = clock();
35970 #endif //COUNT_CYCLES
35971     PromotedObjectCount = 0;
35972 #endif //TRACE_GC
35973
35974     unsigned int condemned_generation_number = gen;
35975
35976     // We want to get a stack from the user thread that triggered the GC
35977     // instead of on the GC thread which is the case for Server GC.
35978     // But we are doing it for Workstation GC as well to be uniform.
35979     FIRE_EVENT(GCTriggered, static_cast<uint32_t>(reason));
35980
35981 #ifdef MULTIPLE_HEAPS
35982     GcCondemnedGeneration = condemned_generation_number;
35983
35984     cooperative_mode = gc_heap::enable_preemptive ();
35985
35986     BEGIN_TIMING(gc_during_log);
35987     gc_heap::ee_suspend_event.Set();
35988     gc_heap::wait_for_gc_done();
35989     END_TIMING(gc_during_log);
35990
35991     gc_heap::disable_preemptive (cooperative_mode);
35992
35993     condemned_generation_number = GcCondemnedGeneration;
35994 #else
35995         if (gc_heap::proceed_with_gc_p)
35996         {
35997             BEGIN_TIMING(gc_during_log);
35998             pGenGCHeap->garbage_collect (condemned_generation_number);
35999             if (gc_heap::pm_trigger_full_gc)
36000             {
36001                 pGenGCHeap->garbage_collect_pm_full_gc();
36002             }
36003             END_TIMING(gc_during_log);
36004         }
36005 #endif //MULTIPLE_HEAPS
36006
36007 #ifdef TRACE_GC
36008 #ifdef COUNT_CYCLES
36009     finish = GetCycleCount32();
36010 #else
36011     finish = clock();
36012 #endif //COUNT_CYCLES
36013     GcDuration += finish - start;
36014     dprintf (3,
36015              ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d",
36016               VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number,
36017               finish - start, GcDuration,
36018               AllocCount ? (AllocDuration / AllocCount) : 0,
36019               AllocSmallCount, AllocBigCount));
36020     AllocCount = 0;
36021     AllocDuration = 0;
36022 #endif // TRACE_GC
36023
36024 #ifdef BACKGROUND_GC
36025     // We are deciding whether we should fire the alloc wait end event here
36026     // because in begin_foreground we could be calling end_foreground 
36027     // if we need to retry.
36028     if (gc_heap::alloc_wait_event_p)
36029     {
36030         hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc);
36031         gc_heap::alloc_wait_event_p = FALSE;
36032     }
36033 #endif //BACKGROUND_GC
36034
36035 #ifndef MULTIPLE_HEAPS
36036 #ifdef BACKGROUND_GC
36037     if (!gc_heap::dont_restart_ee_p)
36038     {
36039 #endif //BACKGROUND_GC
36040         BEGIN_TIMING(restart_ee_during_log);
36041         GCToEEInterface::RestartEE(TRUE);
36042         END_TIMING(restart_ee_during_log);
36043 #ifdef BACKGROUND_GC
36044     }
36045 #endif //BACKGROUND_GC
36046 #endif //!MULTIPLE_HEAPS
36047
36048 #ifdef COUNT_CYCLES
36049     printf ("GC: %d Time: %d\n", GcCondemnedGeneration,
36050             GetCycleCount32() - gc_start);
36051 #endif //COUNT_CYCLES
36052
36053 #ifndef MULTIPLE_HEAPS
36054     process_sync_log_stats();
36055     gc_heap::gc_started = FALSE;
36056     gc_heap::set_gc_done();
36057     dprintf (SPINLOCK_LOG, ("GC Lgc"));
36058     leave_spin_lock (&gc_heap::gc_lock);    
36059 #endif //!MULTIPLE_HEAPS
36060
36061 #ifdef FEATURE_PREMORTEM_FINALIZATION
36062     GCToEEInterface::EnableFinalization(!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers);
36063 #endif // FEATURE_PREMORTEM_FINALIZATION
36064
36065     return dd_collection_count (dd);
36066 }
36067
36068 size_t      GCHeap::GetTotalBytesInUse ()
36069 {
36070 #ifdef MULTIPLE_HEAPS
36071     //enumarate all the heaps and get their size.
36072     size_t tot_size = 0;
36073     for (int i = 0; i < gc_heap::n_heaps; i++)
36074     {
36075         GCHeap* Hp = gc_heap::g_heaps [i]->vm_heap;
36076         tot_size += Hp->ApproxTotalBytesInUse (FALSE);
36077     }
36078     return tot_size;
36079 #else
36080     return ApproxTotalBytesInUse ();
36081 #endif //MULTIPLE_HEAPS
36082 }
36083
36084 int GCHeap::CollectionCount (int generation, int get_bgc_fgc_count)
36085 {
36086     if (get_bgc_fgc_count != 0)
36087     {
36088 #ifdef BACKGROUND_GC
36089         if (generation == max_generation)
36090         {
36091             return (int)(gc_heap::full_gc_counts[gc_type_background]);
36092         }
36093         else
36094         {
36095             return (int)(gc_heap::ephemeral_fgc_counts[generation]);
36096         }
36097 #else
36098         return 0;
36099 #endif //BACKGROUND_GC
36100     }
36101
36102 #ifdef MULTIPLE_HEAPS
36103     gc_heap* hp = gc_heap::g_heaps [0];
36104 #else  //MULTIPLE_HEAPS
36105     gc_heap* hp = pGenGCHeap;
36106 #endif //MULTIPLE_HEAPS
36107     if (generation > max_generation)
36108         return 0;
36109     else
36110         return (int)dd_collection_count (hp->dynamic_data_of (generation));
36111 }
36112
36113 size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only)
36114 {
36115     size_t totsize = 0;
36116     //GCTODO
36117     //ASSERT(InMustComplete());
36118     enter_spin_lock (&pGenGCHeap->gc_lock);
36119
36120     heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
36121     // Get small block heap size info
36122     totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
36123     heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
36124     while (seg1 != eph_seg)
36125     {
36126         totsize += heap_segment_allocated (seg1) -
36127             heap_segment_mem (seg1);
36128         seg1 = heap_segment_next (seg1);
36129     }
36130
36131     //discount the fragmentation
36132     for (int i = 0; i <= max_generation; i++)
36133     {
36134         generation* gen = pGenGCHeap->generation_of (i);
36135         totsize -= (generation_free_list_space (gen) + generation_free_obj_space (gen));
36136     }
36137
36138     if (!small_heap_only)
36139     {
36140         heap_segment* seg2 = generation_start_segment (pGenGCHeap->generation_of (max_generation+1));
36141
36142         while (seg2 != 0)
36143         {
36144             totsize += heap_segment_allocated (seg2) -
36145                 heap_segment_mem (seg2);
36146             seg2 = heap_segment_next (seg2);
36147         }
36148
36149         //discount the fragmentation
36150         generation* loh_gen = pGenGCHeap->generation_of (max_generation+1);
36151         size_t frag = generation_free_list_space (loh_gen) + generation_free_obj_space (loh_gen);
36152         totsize -= frag;
36153     }
36154     leave_spin_lock (&pGenGCHeap->gc_lock);
36155     return totsize;
36156 }
36157
36158 #ifdef MULTIPLE_HEAPS
36159 void GCHeap::AssignHeap (alloc_context* acontext)
36160 {
36161     // Assign heap based on processor
36162     acontext->set_alloc_heap(GetHeap(heap_select::select_heap(acontext, 0)));
36163     acontext->set_home_heap(acontext->get_alloc_heap());
36164 }
36165 GCHeap* GCHeap::GetHeap (int n)
36166 {
36167     assert (n < gc_heap::n_heaps);
36168     return gc_heap::g_heaps [n]->vm_heap;
36169 }
36170 #endif //MULTIPLE_HEAPS
36171
36172 bool GCHeap::IsThreadUsingAllocationContextHeap(gc_alloc_context* context, int thread_number)
36173 {
36174     alloc_context* acontext = static_cast<alloc_context*>(context);
36175 #ifdef MULTIPLE_HEAPS
36176     return ((acontext->get_home_heap() == GetHeap(thread_number)) ||
36177             ((acontext->get_home_heap() == 0) && (thread_number == 0)));
36178 #else
36179     UNREFERENCED_PARAMETER(acontext);
36180     UNREFERENCED_PARAMETER(thread_number);
36181     return true;
36182 #endif //MULTIPLE_HEAPS
36183 }
36184
36185 // Returns the number of processors required to trigger the use of thread based allocation contexts
36186 int GCHeap::GetNumberOfHeaps ()
36187 {
36188 #ifdef MULTIPLE_HEAPS
36189     return gc_heap::n_heaps;
36190 #else
36191     return 1;
36192 #endif //MULTIPLE_HEAPS
36193 }
36194
36195 /*
36196   in this way we spend extra time cycling through all the heaps while create the handle
36197   it ought to be changed by keeping alloc_context.home_heap as number (equals heap_number)
36198 */
36199 int GCHeap::GetHomeHeapNumber ()
36200 {
36201 #ifdef MULTIPLE_HEAPS
36202     gc_alloc_context* ctx = GCToEEInterface::GetAllocContext();
36203     if (!ctx)
36204     {
36205         return 0;
36206     }
36207
36208     GCHeap *hp = static_cast<alloc_context*>(ctx)->get_home_heap();
36209     return (hp ? hp->pGenGCHeap->heap_number : 0);
36210 #else
36211     return 0;
36212 #endif //MULTIPLE_HEAPS
36213 }
36214
36215 unsigned int GCHeap::GetCondemnedGeneration()
36216
36217     return gc_heap::settings.condemned_generation;
36218 }
36219
36220 void GCHeap::GetMemoryInfo(uint32_t* highMemLoadThreshold, 
36221                            uint64_t* totalPhysicalMem, 
36222                            uint32_t* lastRecordedMemLoad,
36223                            size_t* lastRecordedHeapSize,
36224                            size_t* lastRecordedFragmentation)
36225 {
36226     *highMemLoadThreshold = gc_heap::high_memory_load_th;
36227     *totalPhysicalMem = gc_heap::total_physical_mem;
36228     *lastRecordedMemLoad = gc_heap::last_gc_memory_load;
36229     *lastRecordedHeapSize = gc_heap::last_gc_heap_size;
36230     *lastRecordedFragmentation = gc_heap::last_gc_fragmentation;
36231 }
36232
36233 int GCHeap::GetGcLatencyMode()
36234 {
36235     return (int)(pGenGCHeap->settings.pause_mode);
36236 }
36237
36238 int GCHeap::SetGcLatencyMode (int newLatencyMode)
36239 {
36240     if (gc_heap::settings.pause_mode == pause_no_gc)
36241         return (int)set_pause_mode_no_gc;
36242
36243     gc_pause_mode new_mode = (gc_pause_mode)newLatencyMode;
36244
36245     if (new_mode == pause_low_latency)
36246     {
36247 #ifndef MULTIPLE_HEAPS
36248         pGenGCHeap->settings.pause_mode = new_mode;
36249 #endif //!MULTIPLE_HEAPS
36250     }
36251     else if (new_mode == pause_sustained_low_latency)
36252     {
36253 #ifdef BACKGROUND_GC
36254         if (gc_heap::gc_can_use_concurrent)
36255         {
36256             pGenGCHeap->settings.pause_mode = new_mode;
36257         }
36258 #endif //BACKGROUND_GC
36259     }
36260     else
36261     {
36262         pGenGCHeap->settings.pause_mode = new_mode;
36263     }
36264
36265 #ifdef BACKGROUND_GC
36266     if (recursive_gc_sync::background_running_p())
36267     {
36268         // If we get here, it means we are doing an FGC. If the pause
36269         // mode was altered we will need to save it in the BGC settings.
36270         if (gc_heap::saved_bgc_settings.pause_mode != new_mode)
36271         {
36272             gc_heap::saved_bgc_settings.pause_mode = new_mode;
36273         }
36274     }
36275 #endif //BACKGROUND_GC
36276
36277     return (int)set_pause_mode_success;
36278 }
36279
36280 int GCHeap::GetLOHCompactionMode()
36281 {
36282     return pGenGCHeap->loh_compaction_mode;
36283 }
36284
36285 void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode)
36286 {
36287 #ifdef FEATURE_LOH_COMPACTION
36288     pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode;
36289 #endif //FEATURE_LOH_COMPACTION
36290 }
36291
36292 bool GCHeap::RegisterForFullGCNotification(uint32_t gen2Percentage,
36293                                            uint32_t lohPercentage)
36294 {
36295 #ifdef MULTIPLE_HEAPS
36296     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36297     {
36298         gc_heap* hp = gc_heap::g_heaps [hn];
36299         hp->fgn_last_alloc = dd_new_allocation (hp->dynamic_data_of (0));
36300     }
36301 #else //MULTIPLE_HEAPS
36302     pGenGCHeap->fgn_last_alloc = dd_new_allocation (pGenGCHeap->dynamic_data_of (0));
36303 #endif //MULTIPLE_HEAPS
36304
36305     pGenGCHeap->full_gc_approach_event.Reset();
36306     pGenGCHeap->full_gc_end_event.Reset();
36307     pGenGCHeap->full_gc_approach_event_set = false;
36308
36309     pGenGCHeap->fgn_maxgen_percent = gen2Percentage;
36310     pGenGCHeap->fgn_loh_percent = lohPercentage;
36311
36312     return TRUE;
36313 }
36314
36315 bool GCHeap::CancelFullGCNotification()
36316 {
36317     pGenGCHeap->fgn_maxgen_percent = 0;
36318     pGenGCHeap->fgn_loh_percent = 0;
36319
36320     pGenGCHeap->full_gc_approach_event.Set();
36321     pGenGCHeap->full_gc_end_event.Set();
36322     
36323     return TRUE;
36324 }
36325
36326 int GCHeap::WaitForFullGCApproach(int millisecondsTimeout)
36327 {
36328     dprintf (2, ("WFGA: Begin wait"));
36329     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_approach_event), millisecondsTimeout);
36330     dprintf (2, ("WFGA: End wait"));
36331     return result;
36332 }
36333
36334 int GCHeap::WaitForFullGCComplete(int millisecondsTimeout)
36335 {
36336     dprintf (2, ("WFGE: Begin wait"));
36337     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_end_event), millisecondsTimeout);
36338     dprintf (2, ("WFGE: End wait"));
36339     return result;
36340 }
36341
36342 int GCHeap::StartNoGCRegion(uint64_t totalSize, bool lohSizeKnown, uint64_t lohSize, bool disallowFullBlockingGC)
36343 {
36344     NoGCRegionLockHolder lh;
36345
36346     dprintf (1, ("begin no gc called"));
36347     start_no_gc_region_status status = gc_heap::prepare_for_no_gc_region (totalSize, lohSizeKnown, lohSize, disallowFullBlockingGC);
36348     if (status == start_no_gc_success)
36349     {
36350         GarbageCollect (max_generation);
36351         status = gc_heap::get_start_no_gc_region_status();
36352     }
36353
36354     if (status != start_no_gc_success)
36355         gc_heap::handle_failure_for_no_gc();
36356
36357     return (int)status;
36358 }
36359
36360 int GCHeap::EndNoGCRegion()
36361 {
36362     NoGCRegionLockHolder lh;
36363     return (int)gc_heap::end_no_gc_region();
36364 }
36365
36366 void GCHeap::PublishObject (uint8_t* Obj)
36367 {
36368 #ifdef BACKGROUND_GC
36369     gc_heap* hp = gc_heap::heap_of (Obj);
36370     hp->bgc_alloc_lock->loh_alloc_done (Obj);
36371     hp->bgc_untrack_loh_alloc();
36372 #endif //BACKGROUND_GC
36373 }
36374
36375 // The spec for this one isn't clear. This function
36376 // returns the size that can be allocated without
36377 // triggering a GC of any kind.
36378 size_t GCHeap::ApproxFreeBytes()
36379 {
36380     //GCTODO
36381     //ASSERT(InMustComplete());
36382     enter_spin_lock (&pGenGCHeap->gc_lock);
36383
36384     generation* gen = pGenGCHeap->generation_of (0);
36385     size_t res = generation_allocation_limit (gen) - generation_allocation_pointer (gen);
36386
36387     leave_spin_lock (&pGenGCHeap->gc_lock);
36388
36389     return res;
36390 }
36391
36392 HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters)
36393 {
36394     if ((gen < 0) || (gen > max_generation))
36395         return E_FAIL;
36396 #ifdef MULTIPLE_HEAPS
36397     counters->current_size = 0;
36398     counters->promoted_size = 0;
36399     counters->collection_count = 0;
36400
36401     //enumarate all the heaps and get their counters.
36402     for (int i = 0; i < gc_heap::n_heaps; i++)
36403     {
36404         dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen);
36405
36406         counters->current_size += dd_current_size (dd);
36407         counters->promoted_size += dd_promoted_size (dd);
36408         if (i == 0)
36409         counters->collection_count += dd_collection_count (dd);
36410     }
36411 #else
36412     dynamic_data* dd = pGenGCHeap->dynamic_data_of (gen);
36413     counters->current_size = dd_current_size (dd);
36414     counters->promoted_size = dd_promoted_size (dd);
36415     counters->collection_count = dd_collection_count (dd);
36416 #endif //MULTIPLE_HEAPS
36417     return S_OK;
36418 }
36419
36420 // Get the segment size to use, making sure it conforms.
36421 size_t GCHeap::GetValidSegmentSize(bool large_seg)
36422 {
36423     return (large_seg ? gc_heap::min_loh_segment_size : gc_heap::soh_segment_size);
36424 }
36425
36426 // Get the max gen0 heap size, making sure it conforms.
36427 size_t gc_heap::get_gen0_min_size()
36428 {
36429     size_t gen0size = static_cast<size_t>(GCConfig::GetGen0Size());
36430     bool is_config_invalid = ((gen0size == 0) || !g_theGCHeap->IsValidGen0MaxSize(gen0size));
36431     if (is_config_invalid)
36432     {
36433 #ifdef SERVER_GC
36434         // performance data seems to indicate halving the size results
36435         // in optimal perf.  Ask for adjusted gen0 size.
36436         gen0size = max(GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),(256*1024));
36437
36438         // if gen0 size is too large given the available memory, reduce it.
36439         // Get true cache size, as we don't want to reduce below this.
36440         size_t trueSize = max(GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE),(256*1024));
36441         dprintf (1, ("cache: %Id-%Id", 
36442             GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),
36443             GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)));
36444
36445         int n_heaps = gc_heap::n_heaps;
36446 #else //SERVER_GC
36447         size_t trueSize = GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE);
36448         gen0size = max((4*trueSize/5),(256*1024));
36449         trueSize = max(trueSize, (256*1024));
36450         int n_heaps = 1;
36451 #endif //SERVER_GC
36452
36453         dprintf (1, ("gen0size: %Id * %d = %Id, physical mem: %Id / 6 = %Id",
36454                 gen0size, n_heaps, (gen0size * n_heaps), 
36455                 gc_heap::total_physical_mem,
36456                 gc_heap::total_physical_mem / 6));
36457
36458         // if the total min GC across heaps will exceed 1/6th of available memory,
36459         // then reduce the min GC size until it either fits or has been reduced to cache size.
36460         while ((gen0size * n_heaps) > (gc_heap::total_physical_mem / 6))
36461         {
36462             gen0size = gen0size / 2;
36463             if (gen0size <= trueSize)
36464             {
36465                 gen0size = trueSize;
36466                 break;
36467             }
36468         }
36469     }
36470
36471     size_t seg_size = gc_heap::soh_segment_size;
36472     assert (seg_size);
36473
36474     // Generation 0 must never be more than 1/2 the segment size.
36475     if (gen0size >= (seg_size / 2))
36476         gen0size = seg_size / 2;
36477
36478     // If the value from config is valid we use it as is without this adjustment.
36479     if (is_config_invalid)
36480     {
36481         if (heap_hard_limit)
36482         {
36483             size_t gen0size_seg = seg_size / 8;
36484             if (gen0size >= gen0size_seg)
36485             {
36486                 dprintf (1, ("gen0 limited by seg size %Id->%Id", gen0size, gen0size_seg));
36487                 gen0size = gen0size_seg;
36488             }
36489         }
36490
36491         gen0size = gen0size / 8 * 5;
36492     }
36493
36494     gen0size = Align (gen0size);
36495
36496     return gen0size;
36497 }
36498
36499 void GCHeap::SetReservedVMLimit (size_t vmlimit)
36500 {
36501     gc_heap::reserved_memory_limit = vmlimit;
36502 }
36503
36504 //versions of same method on each heap
36505
36506 #ifdef FEATURE_PREMORTEM_FINALIZATION
36507
36508 Object* GCHeap::GetNextFinalizableObject()
36509 {
36510
36511 #ifdef MULTIPLE_HEAPS
36512
36513     //return the first non critical one in the first queue.
36514     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36515     {
36516         gc_heap* hp = gc_heap::g_heaps [hn];
36517         Object* O = hp->finalize_queue->GetNextFinalizableObject(TRUE);
36518         if (O)
36519             return O;
36520     }
36521     //return the first non crtitical/critical one in the first queue.
36522     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36523     {
36524         gc_heap* hp = gc_heap::g_heaps [hn];
36525         Object* O = hp->finalize_queue->GetNextFinalizableObject(FALSE);
36526         if (O)
36527             return O;
36528     }
36529     return 0;
36530
36531
36532 #else //MULTIPLE_HEAPS
36533     return pGenGCHeap->finalize_queue->GetNextFinalizableObject();
36534 #endif //MULTIPLE_HEAPS
36535
36536 }
36537
36538 size_t GCHeap::GetNumberFinalizableObjects()
36539 {
36540 #ifdef MULTIPLE_HEAPS
36541     size_t cnt = 0;
36542     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36543     {
36544         gc_heap* hp = gc_heap::g_heaps [hn];
36545         cnt += hp->finalize_queue->GetNumberFinalizableObjects();
36546     }
36547     return cnt;
36548
36549
36550 #else //MULTIPLE_HEAPS
36551     return pGenGCHeap->finalize_queue->GetNumberFinalizableObjects();
36552 #endif //MULTIPLE_HEAPS
36553 }
36554
36555 size_t GCHeap::GetFinalizablePromotedCount()
36556 {
36557 #ifdef MULTIPLE_HEAPS
36558     size_t cnt = 0;
36559
36560     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36561     {
36562         gc_heap* hp = gc_heap::g_heaps [hn];
36563         cnt += hp->finalize_queue->GetPromotedCount();
36564     }
36565     return cnt;
36566
36567 #else //MULTIPLE_HEAPS
36568     return pGenGCHeap->finalize_queue->GetPromotedCount();
36569 #endif //MULTIPLE_HEAPS
36570 }
36571
36572 bool GCHeap::FinalizeAppDomain(void *pDomain, bool fRunFinalizers)
36573 {
36574 #ifdef MULTIPLE_HEAPS
36575     bool foundp = false;
36576     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36577     {
36578         gc_heap* hp = gc_heap::g_heaps [hn];
36579         if (hp->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers))
36580             foundp = true;
36581     }
36582     return foundp;
36583
36584 #else //MULTIPLE_HEAPS
36585     return pGenGCHeap->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers);
36586 #endif //MULTIPLE_HEAPS
36587 }
36588
36589 bool GCHeap::ShouldRestartFinalizerWatchDog()
36590 {
36591     // This condition was historically used as part of the condition to detect finalizer thread timeouts
36592     return gc_heap::gc_lock.lock != -1;
36593 }
36594
36595 void GCHeap::SetFinalizeQueueForShutdown(bool fHasLock)
36596 {
36597 #ifdef MULTIPLE_HEAPS
36598     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36599     {
36600         gc_heap* hp = gc_heap::g_heaps [hn];
36601         hp->finalize_queue->SetSegForShutDown(fHasLock);
36602     }
36603
36604 #else //MULTIPLE_HEAPS
36605     pGenGCHeap->finalize_queue->SetSegForShutDown(fHasLock);
36606 #endif //MULTIPLE_HEAPS
36607 }
36608
36609 //---------------------------------------------------------------------------
36610 // Finalized class tracking
36611 //---------------------------------------------------------------------------
36612
36613 bool GCHeap::RegisterForFinalization (int gen, Object* obj)
36614 {
36615     if (gen == -1)
36616         gen = 0;
36617     if (((((CObjectHeader*)obj)->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN))
36618     {
36619         //just reset the bit
36620         ((CObjectHeader*)obj)->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN);
36621         return true;
36622     }
36623     else
36624     {
36625         gc_heap* hp = gc_heap::heap_of ((uint8_t*)obj);
36626         return hp->finalize_queue->RegisterForFinalization (gen, obj);
36627     }
36628 }
36629
36630 void GCHeap::SetFinalizationRun (Object* obj)
36631 {
36632     ((CObjectHeader*)obj)->GetHeader()->SetBit(BIT_SBLK_FINALIZER_RUN);
36633 }
36634
36635
36636 //--------------------------------------------------------------------
36637 //
36638 //          Support for finalization
36639 //
36640 //--------------------------------------------------------------------
36641
36642 inline
36643 unsigned int gen_segment (int gen)
36644 {
36645     assert (((signed)NUMBERGENERATIONS - gen - 1)>=0);
36646     return (NUMBERGENERATIONS - gen - 1);
36647 }
36648
36649 bool CFinalize::Initialize()
36650 {
36651     CONTRACTL {
36652         NOTHROW;
36653         GC_NOTRIGGER;
36654     } CONTRACTL_END;
36655
36656     m_Array = new (nothrow)(Object*[100]);
36657
36658     if (!m_Array)
36659     {
36660         ASSERT (m_Array);
36661         STRESS_LOG_OOM_STACK(sizeof(Object*[100]));
36662         if (GCConfig::GetBreakOnOOM())
36663         {
36664             GCToOSInterface::DebugBreak();
36665         }
36666         return false;
36667     }
36668     m_EndArray = &m_Array[100];
36669
36670     for (int i =0; i < FreeList; i++)
36671     {
36672         SegQueueLimit (i) = m_Array;
36673     }
36674     m_PromotedCount = 0;
36675     lock = -1;
36676 #ifdef _DEBUG
36677     lockowner_threadid.Clear();
36678 #endif // _DEBUG
36679
36680     return true;
36681 }
36682
36683 CFinalize::~CFinalize()
36684 {
36685     delete m_Array;
36686 }
36687
36688 size_t CFinalize::GetPromotedCount ()
36689 {
36690     return m_PromotedCount;
36691 }
36692
36693 inline
36694 void CFinalize::EnterFinalizeLock()
36695 {
36696     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
36697              GCToEEInterface::GetThread() == 0 ||
36698              GCToEEInterface::IsPreemptiveGCDisabled());
36699
36700 retry:
36701     if (Interlocked::CompareExchange(&lock, 0, -1) >= 0)
36702     {
36703         unsigned int i = 0;
36704         while (lock >= 0)
36705         {
36706             YieldProcessor();           // indicate to the processor that we are spinning
36707             if (++i & 7)
36708                 GCToOSInterface::YieldThread (0);
36709             else
36710                 GCToOSInterface::Sleep (5);
36711         }
36712         goto retry;
36713     }
36714
36715 #ifdef _DEBUG
36716     lockowner_threadid.SetToCurrentThread();
36717 #endif // _DEBUG
36718 }
36719
36720 inline
36721 void CFinalize::LeaveFinalizeLock()
36722 {
36723     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
36724              GCToEEInterface::GetThread() == 0 ||
36725              GCToEEInterface::IsPreemptiveGCDisabled());
36726
36727 #ifdef _DEBUG
36728     lockowner_threadid.Clear();
36729 #endif // _DEBUG
36730     lock = -1;
36731 }
36732
36733 bool
36734 CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
36735 {
36736     CONTRACTL {
36737         NOTHROW;
36738         GC_NOTRIGGER;
36739     } CONTRACTL_END;
36740
36741     EnterFinalizeLock();
36742     // Adjust gen
36743     unsigned int dest = 0;
36744
36745     if (g_fFinalizerRunOnShutDown)
36746     {
36747         //no method table available yet,
36748         //put it in the finalizer queue and sort out when
36749         //dequeueing
36750         dest = FinalizerListSeg;
36751     }
36752
36753     else
36754         dest = gen_segment (gen);
36755
36756     // Adjust boundary for segments so that GC will keep objects alive.
36757     Object*** s_i = &SegQueue (FreeList);
36758     if ((*s_i) == m_EndArray)
36759     {
36760         if (!GrowArray())
36761         {
36762             LeaveFinalizeLock();
36763             if (method_table(obj) == NULL)
36764             {
36765                 // If the object is uninitialized, a valid size should have been passed.
36766                 assert (size >= Align (min_obj_size));
36767                 dprintf (3, ("Making unused array [%Ix, %Ix[", (size_t)obj, (size_t)(obj+size)));
36768                 ((CObjectHeader*)obj)->SetFree(size);
36769             }
36770             STRESS_LOG_OOM_STACK(0);
36771             if (GCConfig::GetBreakOnOOM())
36772             {
36773                 GCToOSInterface::DebugBreak();
36774             }
36775             return false;
36776         }
36777     }
36778     Object*** end_si = &SegQueueLimit (dest);
36779     do
36780     {
36781         //is the segment empty?
36782         if (!(*s_i == *(s_i-1)))
36783         {
36784             //no, swap the end elements.
36785             *(*s_i) = *(*(s_i-1));
36786         }
36787         //increment the fill pointer
36788         (*s_i)++;
36789         //go to the next segment.
36790         s_i--;
36791     } while (s_i > end_si);
36792
36793     // We have reached the destination segment
36794     // store the object
36795     **s_i = obj;
36796     // increment the fill pointer
36797     (*s_i)++;
36798
36799     LeaveFinalizeLock();
36800
36801     return true;
36802 }
36803
36804 Object*
36805 CFinalize::GetNextFinalizableObject (BOOL only_non_critical)
36806 {
36807     Object* obj = 0;
36808     //serialize
36809     EnterFinalizeLock();
36810
36811 retry:
36812     if (!IsSegEmpty(FinalizerListSeg))
36813     {
36814         if (g_fFinalizerRunOnShutDown)
36815         {
36816             obj = *(SegQueueLimit (FinalizerListSeg)-1);
36817             if (method_table(obj)->HasCriticalFinalizer())
36818             {
36819                 MoveItem ((SegQueueLimit (FinalizerListSeg)-1),
36820                           FinalizerListSeg, CriticalFinalizerListSeg);
36821                 goto retry;
36822             }
36823             else
36824                 --SegQueueLimit (FinalizerListSeg);
36825         }
36826         else
36827             obj =  *(--SegQueueLimit (FinalizerListSeg));
36828
36829     }
36830     else if (!only_non_critical && !IsSegEmpty(CriticalFinalizerListSeg))
36831     {
36832         //the FinalizerList is empty, we can adjust both
36833         // limit instead of moving the object to the free list
36834         obj =  *(--SegQueueLimit (CriticalFinalizerListSeg));
36835         --SegQueueLimit (FinalizerListSeg);
36836     }
36837     if (obj)
36838     {
36839         dprintf (3, ("running finalizer for %Ix (mt: %Ix)", obj, method_table (obj)));
36840     }
36841     LeaveFinalizeLock();
36842     return obj;
36843 }
36844
36845 void
36846 CFinalize::SetSegForShutDown(BOOL fHasLock)
36847 {
36848     int i;
36849
36850     if (!fHasLock)
36851         EnterFinalizeLock();
36852     for (i = 0; i <= max_generation; i++)
36853     {
36854         unsigned int seg = gen_segment (i);
36855         Object** startIndex = SegQueueLimit (seg)-1;
36856         Object** stopIndex  = SegQueue (seg);
36857         for (Object** po = startIndex; po >= stopIndex; po--)
36858         {
36859             Object* obj = *po;
36860             if (method_table(obj)->HasCriticalFinalizer())
36861             {
36862                 MoveItem (po, seg, CriticalFinalizerListSeg);
36863             }
36864             else
36865             {
36866                 MoveItem (po, seg, FinalizerListSeg);
36867             }
36868         }
36869     }
36870     if (!fHasLock)
36871         LeaveFinalizeLock();
36872 }
36873
36874 void
36875 CFinalize::DiscardNonCriticalObjects()
36876 {
36877     //empty the finalization queue
36878     Object** startIndex = SegQueueLimit (FinalizerListSeg)-1;
36879     Object** stopIndex  = SegQueue (FinalizerListSeg);
36880     for (Object** po = startIndex; po >= stopIndex; po--)
36881     {
36882         MoveItem (po, FinalizerListSeg, FreeList);
36883     }
36884 }
36885
36886 size_t
36887 CFinalize::GetNumberFinalizableObjects()
36888 {
36889     return SegQueueLimit (FinalizerListSeg) -
36890         (g_fFinalizerRunOnShutDown ? m_Array : SegQueue(FinalizerListSeg));
36891 }
36892
36893 BOOL
36894 CFinalize::FinalizeSegForAppDomain (void *pDomain, 
36895                                     BOOL fRunFinalizers, 
36896                                     unsigned int Seg)
36897 {
36898     BOOL finalizedFound = FALSE;
36899     Object** endIndex = SegQueue (Seg);
36900     for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
36901     {
36902         CObjectHeader* obj = (CObjectHeader*)*i;
36903
36904         // Objects are put into the finalization queue before they are complete (ie their methodtable
36905         // may be null) so we must check that the object we found has a method table before checking
36906         // if it has the index we are looking for. If the methodtable is null, it can't be from the
36907         // unloading domain, so skip it.
36908         if (method_table(obj) == NULL)
36909         {
36910             continue;
36911         }
36912
36913         // does the EE actually want us to finalize this object?
36914         if (!GCToEEInterface::ShouldFinalizeObjectForUnload(pDomain, obj))
36915         {
36916             continue;
36917         }
36918
36919         if (!fRunFinalizers || (obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
36920         {
36921             //remove the object because we don't want to
36922             //run the finalizer
36923             MoveItem (i, Seg, FreeList);
36924             //Reset the bit so it will be put back on the queue
36925             //if resurrected and re-registered.
36926             obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
36927         }
36928         else
36929         {
36930             if (method_table(obj)->HasCriticalFinalizer())
36931             {
36932                 finalizedFound = TRUE;
36933                 MoveItem (i, Seg, CriticalFinalizerListSeg);
36934             }
36935             else
36936             {
36937                 if (GCToEEInterface::AppDomainIsRudeUnload(pDomain))
36938                 {
36939                     MoveItem (i, Seg, FreeList);
36940                 }
36941                 else
36942                 {
36943                     finalizedFound = TRUE;
36944                     MoveItem (i, Seg, FinalizerListSeg);
36945                 }
36946             }
36947         }
36948     }
36949
36950     return finalizedFound;
36951 }
36952
36953 bool
36954 CFinalize::FinalizeAppDomain (void *pDomain, bool fRunFinalizers)
36955 {
36956     bool finalizedFound = false;
36957
36958     unsigned int startSeg = gen_segment (max_generation);
36959
36960     EnterFinalizeLock();
36961
36962     for (unsigned int Seg = startSeg; Seg <= gen_segment (0); Seg++)
36963     {
36964         if (FinalizeSegForAppDomain (pDomain, fRunFinalizers, Seg))
36965         {
36966             finalizedFound = true;
36967         }
36968     }
36969
36970     LeaveFinalizeLock();
36971
36972     return finalizedFound;
36973 }
36974
36975 void
36976 CFinalize::MoveItem (Object** fromIndex,
36977                      unsigned int fromSeg,
36978                      unsigned int toSeg)
36979 {
36980
36981     int step;
36982     ASSERT (fromSeg != toSeg);
36983     if (fromSeg > toSeg)
36984         step = -1;
36985     else
36986         step = +1;
36987     // Place the element at the boundary closest to dest
36988     Object** srcIndex = fromIndex;
36989     for (unsigned int i = fromSeg; i != toSeg; i+= step)
36990     {
36991         Object**& destFill = m_FillPointers[i+(step - 1 )/2];
36992         Object** destIndex = destFill - (step + 1)/2;
36993         if (srcIndex != destIndex)
36994         {
36995             Object* tmp = *srcIndex;
36996             *srcIndex = *destIndex;
36997             *destIndex = tmp;
36998         }
36999         destFill -= step;
37000         srcIndex = destIndex;
37001     }
37002 }
37003
37004 void
37005 CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC)
37006 {
37007     ScanContext sc;
37008     if (pSC == 0)
37009         pSC = &sc;
37010
37011     pSC->thread_number = hn;
37012
37013     //scan the finalization queue
37014     Object** startIndex  = SegQueue (CriticalFinalizerListSeg);
37015     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
37016
37017     for (Object** po = startIndex; po < stopIndex; po++)
37018     {
37019         Object* o = *po;
37020         //dprintf (3, ("scan freacheable %Ix", (size_t)o));
37021         dprintf (3, ("scan f %Ix", (size_t)o));
37022 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
37023         if (g_fEnableAppDomainMonitoring)
37024         {
37025             pSC->pCurrentDomain = GCToEEInterface::GetAppDomainForObject(o);
37026         }
37027 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
37028
37029         (*fn)(po, pSC, 0);
37030     }
37031 }
37032
37033 void CFinalize::WalkFReachableObjects (fq_walk_fn fn)
37034 {
37035     Object** startIndex = SegQueue (CriticalFinalizerListSeg);
37036     Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg);
37037     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
37038     for (Object** po = startIndex; po < stopIndex; po++)
37039     {
37040         //report *po
37041         fn(po < stopCriticalIndex, *po);
37042     }
37043 }
37044
37045 BOOL
37046 CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,
37047                                 gc_heap* hp)
37048 {
37049     ScanContext sc;
37050     sc.promotion = TRUE;
37051 #ifdef MULTIPLE_HEAPS
37052     sc.thread_number = hp->heap_number;
37053 #else
37054     UNREFERENCED_PARAMETER(hp);
37055 #endif //MULTIPLE_HEAPS
37056
37057     BOOL finalizedFound = FALSE;
37058
37059     //start with gen and explore all the younger generations.
37060     unsigned int startSeg = gen_segment (gen);
37061     {
37062         m_PromotedCount = 0;
37063         for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++)
37064         {
37065             Object** endIndex = SegQueue (Seg);
37066             for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
37067             {
37068                 CObjectHeader* obj = (CObjectHeader*)*i;
37069                 dprintf (3, ("scanning: %Ix", (size_t)obj));
37070                 if (!g_theGCHeap->IsPromoted (obj))
37071                 {
37072                     dprintf (3, ("freacheable: %Ix", (size_t)obj));
37073
37074                     assert (method_table(obj)->HasFinalizer());
37075
37076                     if (GCToEEInterface::EagerFinalized(obj))
37077                     {
37078                         MoveItem (i, Seg, FreeList);
37079                     }
37080                     else if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
37081                     {
37082                         //remove the object because we don't want to
37083                         //run the finalizer
37084
37085                         MoveItem (i, Seg, FreeList);
37086
37087                         //Reset the bit so it will be put back on the queue
37088                         //if resurrected and re-registered.
37089                         obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
37090
37091                     }
37092                     else
37093                     {
37094                         m_PromotedCount++;
37095
37096                         if (method_table(obj)->HasCriticalFinalizer())
37097                         {
37098                             MoveItem (i, Seg, CriticalFinalizerListSeg);
37099                         }
37100                         else
37101                         {
37102                             MoveItem (i, Seg, FinalizerListSeg);
37103                         }
37104                     }
37105                 }
37106 #ifdef BACKGROUND_GC
37107                 else
37108                 {
37109                     if ((gen == max_generation) && (recursive_gc_sync::background_running_p()))
37110                     {
37111                         // TODO - fix the following line.
37112                         //assert (gc_heap::background_object_marked ((uint8_t*)obj, FALSE));
37113                         dprintf (3, ("%Ix is marked", (size_t)obj));
37114                     }
37115                 }
37116 #endif //BACKGROUND_GC
37117             }
37118         }
37119     }
37120     finalizedFound = !IsSegEmpty(FinalizerListSeg) ||
37121                      !IsSegEmpty(CriticalFinalizerListSeg);
37122                     
37123     if (finalizedFound)
37124     {
37125         //Promote the f-reachable objects
37126         GcScanRoots (pfn,
37127 #ifdef MULTIPLE_HEAPS
37128                      hp->heap_number
37129 #else
37130                      0
37131 #endif //MULTIPLE_HEAPS
37132                      , 0);
37133
37134         hp->settings.found_finalizers = TRUE;
37135
37136 #ifdef BACKGROUND_GC
37137         if (hp->settings.concurrent)
37138         {
37139             hp->settings.found_finalizers = !(IsSegEmpty(FinalizerListSeg) && IsSegEmpty(CriticalFinalizerListSeg));
37140         }
37141 #endif //BACKGROUND_GC
37142         if (hp->settings.concurrent && hp->settings.found_finalizers)
37143         {
37144             if (!mark_only_p)
37145                 GCToEEInterface::EnableFinalization(true);
37146         }
37147     }
37148
37149     return finalizedFound;
37150 }
37151
37152 //Relocates all of the objects in the finalization array
37153 void
37154 CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
37155 {
37156     ScanContext sc;
37157     sc.promotion = FALSE;
37158 #ifdef MULTIPLE_HEAPS
37159     sc.thread_number = hp->heap_number;
37160 #else
37161     UNREFERENCED_PARAMETER(hp);
37162 #endif //MULTIPLE_HEAPS
37163
37164     unsigned int Seg = gen_segment (gen);
37165
37166     Object** startIndex = SegQueue (Seg);
37167     for (Object** po = startIndex; po < SegQueue (FreeList);po++)
37168     {
37169         GCHeap::Relocate (po, &sc);
37170     }
37171 }
37172
37173 void
37174 CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
37175 {
37176     // update the generation fill pointers.
37177     // if gen_0_empty is FALSE, test each object to find out if
37178     // it was promoted or not
37179     if (gen_0_empty_p)
37180     {
37181         for (int i = min (gen+1, max_generation); i > 0; i--)
37182         {
37183             m_FillPointers [gen_segment(i)] = m_FillPointers [gen_segment(i-1)];
37184         }
37185     }
37186     else
37187     {
37188         //Look for demoted or promoted plugs
37189
37190         for (int i = gen; i >= 0; i--)
37191         {
37192             unsigned int Seg = gen_segment (i);
37193             Object** startIndex = SegQueue (Seg);
37194
37195             for (Object** po = startIndex;
37196                  po < SegQueueLimit (gen_segment(i)); po++)
37197             {
37198                 int new_gen = g_theGCHeap->WhichGeneration (*po);
37199                 if (new_gen != i)
37200                 {
37201                     if (new_gen > i)
37202                     {
37203                         //promotion
37204                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
37205                     }
37206                     else
37207                     {
37208                         //demotion
37209                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
37210                         //back down in order to see all objects.
37211                         po--;
37212                     }
37213                 }
37214
37215             }
37216         }
37217     }
37218 }
37219
37220 BOOL
37221 CFinalize::GrowArray()
37222 {
37223     size_t oldArraySize = (m_EndArray - m_Array);
37224     size_t newArraySize =  (size_t)(((float)oldArraySize / 10) * 12);
37225
37226     Object** newArray = new (nothrow) Object*[newArraySize];
37227     if (!newArray)
37228     {
37229         // It's not safe to throw here, because of the FinalizeLock.  Tell our caller
37230         // to throw for us.
37231 //        ASSERT (newArray);
37232         return FALSE;
37233     }
37234     memcpy (newArray, m_Array, oldArraySize*sizeof(Object*));
37235
37236     //adjust the fill pointers
37237     for (int i = 0; i < FreeList; i++)
37238     {
37239         m_FillPointers [i] += (newArray - m_Array);
37240     }
37241     delete m_Array;
37242     m_Array = newArray;
37243     m_EndArray = &m_Array [newArraySize];
37244
37245     return TRUE;
37246 }
37247
37248 #ifdef VERIFY_HEAP
37249 void CFinalize::CheckFinalizerObjects()
37250 {
37251     for (int i = 0; i <= max_generation; i++)
37252     {
37253         Object **startIndex = SegQueue (gen_segment (i));
37254         Object **stopIndex  = SegQueueLimit (gen_segment (i));
37255
37256         for (Object **po = startIndex; po < stopIndex; po++)
37257         {
37258             if ((int)g_theGCHeap->WhichGeneration (*po) < i)
37259                 FATAL_GC_ERROR ();
37260             ((CObjectHeader*)*po)->Validate();
37261         }
37262     }
37263 }
37264 #endif //VERIFY_HEAP
37265
37266 #endif // FEATURE_PREMORTEM_FINALIZATION
37267
37268
37269 //------------------------------------------------------------------------------
37270 //
37271 //                      End of VM specific support
37272 //
37273 //------------------------------------------------------------------------------
37274 void gc_heap::walk_heap_per_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
37275 {
37276     generation* gen = gc_heap::generation_of (gen_number);
37277     heap_segment*    seg = generation_start_segment (gen);
37278     uint8_t*       x = ((gen_number == max_generation) ? heap_segment_mem (seg) :
37279                      generation_allocation_start (gen));
37280
37281     uint8_t*       end = heap_segment_allocated (seg);
37282     BOOL small_object_segments = TRUE;
37283     int align_const = get_alignment_constant (small_object_segments);
37284
37285     while (1)
37286
37287     {
37288         if (x >= end)
37289         {
37290             if ((seg = heap_segment_next (seg)) != 0)
37291             {
37292                 x = heap_segment_mem (seg);
37293                 end = heap_segment_allocated (seg);
37294                 continue;
37295             }
37296             else
37297             {
37298                 if (small_object_segments && walk_large_object_heap_p)
37299
37300                 {
37301                     small_object_segments = FALSE;
37302                     align_const = get_alignment_constant (small_object_segments);
37303                     seg = generation_start_segment (large_object_generation);
37304                     x = heap_segment_mem (seg);
37305                     end = heap_segment_allocated (seg);
37306                     continue;
37307                 }
37308                 else
37309                 {
37310                     break;
37311                 }
37312             }
37313         }
37314
37315         size_t s = size (x);
37316         CObjectHeader* o = (CObjectHeader*)x;
37317
37318         if (!o->IsFree())
37319
37320         {
37321             _ASSERTE(((size_t)o & 0x3) == 0); // Last two bits should never be set at this point
37322
37323             if (!fn (o->GetObjectBase(), context))
37324                 return;
37325         }
37326         x = x + Align (s, align_const);
37327     }
37328 }
37329
37330 void gc_heap::walk_finalize_queue (fq_walk_fn fn)
37331 {
37332 #ifdef FEATURE_PREMORTEM_FINALIZATION
37333     finalize_queue->WalkFReachableObjects (fn);
37334 #endif //FEATURE_PREMORTEM_FINALIZATION
37335 }
37336
37337 void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
37338 {
37339 #ifdef MULTIPLE_HEAPS
37340     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
37341     {
37342         gc_heap* hp = gc_heap::g_heaps [hn];
37343
37344         hp->walk_heap_per_heap (fn, context, gen_number, walk_large_object_heap_p);
37345     }
37346 #else
37347     walk_heap_per_heap(fn, context, gen_number, walk_large_object_heap_p);
37348 #endif //MULTIPLE_HEAPS
37349 }
37350
37351 void GCHeap::DiagWalkObject (Object* obj, walk_fn fn, void* context)
37352 {
37353     uint8_t* o = (uint8_t*)obj;
37354     if (o)
37355     {
37356         go_through_object_cl (method_table (o), o, size(o), oo,
37357                                     {
37358                                         if (*oo)
37359                                         {
37360                                             Object *oh = (Object*)*oo;
37361                                             if (!fn (oh, context))
37362                                                 return;
37363                                         }
37364                                     }
37365             );
37366     }
37367 }
37368
37369 void GCHeap::DiagWalkSurvivorsWithType (void* gc_context, record_surv_fn fn, void* diag_context, walk_surv_type type)
37370 {
37371     gc_heap* hp = (gc_heap*)gc_context;
37372     hp->walk_survivors (fn, diag_context, type);
37373 }
37374
37375 void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p)
37376 {
37377     gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p);
37378 }
37379
37380 void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn)
37381 {
37382     gc_heap* hp = (gc_heap*)gc_context;
37383     hp->walk_finalize_queue (fn);
37384 }
37385
37386 void GCHeap::DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* sc)
37387 {
37388 #ifdef MULTIPLE_HEAPS
37389     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
37390     {
37391         gc_heap* hp = gc_heap::g_heaps [hn];
37392         hp->finalize_queue->GcScanRoots(fn, hn, sc);
37393     }
37394 #else
37395         pGenGCHeap->finalize_queue->GcScanRoots(fn, 0, sc);
37396 #endif //MULTIPLE_HEAPS
37397 }
37398
37399 void GCHeap::DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
37400 {
37401     UNREFERENCED_PARAMETER(gen_number);
37402     GCScan::GcScanHandlesForProfilerAndETW (max_generation, context, fn);
37403 }
37404
37405 void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
37406 {
37407     UNREFERENCED_PARAMETER(gen_number);
37408     GCScan::GcScanDependentHandlesForProfilerAndETW (max_generation, context, fn);
37409 }
37410
37411 // Go through and touch (read) each page straddled by a memory block.
37412 void TouchPages(void * pStart, size_t cb)
37413 {
37414     const uint32_t pagesize = OS_PAGE_SIZE;
37415     _ASSERTE(0 == (pagesize & (pagesize-1))); // Must be a power of 2.
37416     if (cb)
37417     {
37418         VOLATILE(char)* pEnd = (VOLATILE(char)*)(cb + (char*)pStart);
37419         VOLATILE(char)* p = (VOLATILE(char)*)(((char*)pStart) -  (((size_t)pStart) & (pagesize-1)));
37420         while (p < pEnd)
37421         {
37422             char a;
37423             a = VolatileLoad(p);
37424             //printf("Touching page %lxh\n", (uint32_t)p);
37425             p += pagesize;
37426         }
37427     }
37428 }
37429
37430 #if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
37431     // This code is designed to catch the failure to update the write barrier
37432     // The way it works is to copy the whole heap right after every GC.  The write
37433     // barrier code has been modified so that it updates the shadow as well as the
37434     // real GC heap.  Before doing the next GC, we walk the heap, looking for pointers
37435     // that were updated in the real heap, but not the shadow.  A mismatch indicates
37436     // an error.  The offending code can be found by breaking after the correct GC,
37437     // and then placing a data breakpoint on the Heap location that was updated without
37438     // going through the write barrier.
37439
37440     // Called at process shutdown
37441 void deleteGCShadow()
37442 {
37443     if (g_GCShadow != 0)
37444         GCToOSInterface::VirtualRelease (g_GCShadow, g_GCShadowEnd - g_GCShadow);
37445     g_GCShadow = 0;
37446     g_GCShadowEnd = 0;
37447 }
37448
37449     // Called at startup and right after a GC, get a snapshot of the GC Heap
37450 void initGCShadow()
37451 {
37452     if (!(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK))
37453         return;
37454
37455     size_t len = g_gc_highest_address - g_gc_lowest_address;
37456     if (len > (size_t)(g_GCShadowEnd - g_GCShadow)) 
37457     {
37458         deleteGCShadow();
37459         g_GCShadowEnd = g_GCShadow = (uint8_t *)GCToOSInterface::VirtualReserve(len, 0, VirtualReserveFlags::None);
37460         if (g_GCShadow == NULL || !GCToOSInterface::VirtualCommit(g_GCShadow, len))
37461         {
37462             _ASSERTE(!"Not enough memory to run HeapVerify level 2");
37463             // If after the assert we decide to allow the program to continue 
37464             // running we need to be in a state that will not trigger any 
37465             // additional AVs while we fail to allocate a shadow segment, i.e. 
37466             // ensure calls to updateGCShadow() checkGCWriteBarrier() don't AV
37467             deleteGCShadow();
37468             return;
37469         }
37470
37471         g_GCShadowEnd += len;
37472     }
37473
37474     // save the value of g_gc_lowest_address at this time.  If this value changes before
37475     // the next call to checkGCWriteBarrier() it means we extended the heap (with a
37476     // large object segment most probably), and the whole shadow segment is inconsistent.
37477     g_shadow_lowest_address = g_gc_lowest_address;
37478
37479         //****** Copy the whole GC heap ******
37480     //
37481     // NOTE: This is the one situation where the combination of heap_segment_rw(gen_start_segment())
37482     // can produce a NULL result.  This is because the initialization has not completed.
37483     //
37484     generation* gen = gc_heap::generation_of (max_generation);
37485     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
37486
37487     ptrdiff_t delta = g_GCShadow - g_gc_lowest_address;
37488     BOOL small_object_segments = TRUE;
37489     while(1)
37490     {
37491         if (!seg)
37492         {
37493             if (small_object_segments)
37494             {
37495                 small_object_segments = FALSE;
37496                 seg = heap_segment_rw (generation_start_segment (gc_heap::generation_of (max_generation+1)));
37497                 continue;
37498             }
37499             else
37500                 break;
37501         }
37502             // Copy the segment
37503         uint8_t* start = heap_segment_mem(seg);
37504         uint8_t* end = heap_segment_allocated (seg);
37505         memcpy(start + delta, start, end - start);
37506         seg = heap_segment_next_rw (seg);
37507     }
37508 }
37509
37510 #define INVALIDGCVALUE (void*)((size_t)0xcccccccd)
37511
37512     // test to see if 'ptr' was only updated via the write barrier.
37513 inline void testGCShadow(Object** ptr)
37514 {
37515     Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_gc_lowest_address)];
37516     if (*ptr != 0 && (uint8_t*) shadow < g_GCShadowEnd && *ptr != *shadow)
37517     {
37518
37519         // If you get this assertion, someone updated a GC pointer in the heap without
37520         // using the write barrier.  To find out who, check the value of 
37521         // dd_collection_count (dynamic_data_of (0)). Also
37522         // note the value of 'ptr'.  Rerun the App that the previous GC just occurred.
37523         // Then put a data breakpoint for the value of 'ptr'  Then check every write
37524         // to pointer between the two GCs.  The last one is not using the write barrier.
37525
37526         // If the memory of interest does not exist at system startup,
37527         // you need to set the data breakpoint right after the memory gets committed
37528         // Set a breakpoint at the end of grow_heap_segment, and put the value of 'ptr'
37529         // in the memory window.  run until the memory gets mapped. Then you can set
37530         // your breakpoint
37531
37532         // Note a recent change, we've identified race conditions when updating the gc shadow.
37533         // Throughout the runtime, code will update an address in the gc heap, then erect the
37534         // write barrier, which calls updateGCShadow. With an app that pounds one heap location
37535         // from multiple threads, you can hit this assert even though all involved are using the
37536         // write barrier properly. Thusly, we detect the race and set this location to INVALIDGCVALUE.
37537         // TODO: the code in jithelp.asm doesn't call updateGCShadow, and hasn't been
37538         // TODO: fixed to detect the race. We've only seen this race from VolatileWritePtr,
37539         // TODO: so elect not to fix jithelp.asm at this time. It should be done if we start hitting
37540         // TODO: erroneous asserts in here.
37541
37542         if(*shadow!=INVALIDGCVALUE)
37543         {
37544 #ifdef FEATURE_BASICFREEZE
37545             // Write barriers for stores of references to frozen objects may be optimized away.
37546             if (!gc_heap::frozen_object_p(*ptr))
37547 #endif // FEATURE_BASICFREEZE
37548             {
37549                 _ASSERTE(!"Pointer updated without using write barrier");
37550             }
37551         }
37552         /*
37553         else
37554         {
37555              printf("saw a INVALIDGCVALUE. (just to let you know)\n");
37556         }
37557         */
37558     }
37559 }
37560
37561 void testGCShadowHelper (uint8_t* x)
37562 {
37563     size_t s = size (x);
37564     if (contain_pointers (x))
37565     {
37566         go_through_object_nostart (method_table(x), x, s, oo,
37567                            { testGCShadow((Object**) oo); });
37568     }
37569 }
37570
37571     // Walk the whole heap, looking for pointers that were not updated with the write barrier.
37572 void checkGCWriteBarrier()
37573 {
37574     // g_shadow_lowest_address != g_gc_lowest_address means the GC heap was extended by a segment
37575     // and the GC shadow segment did not track that change!
37576     if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_gc_lowest_address)
37577     {
37578         // No shadow stack, nothing to check.
37579         return;
37580     }
37581
37582     {
37583         generation* gen = gc_heap::generation_of (max_generation);
37584         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
37585
37586         PREFIX_ASSUME(seg != NULL);
37587
37588         while(seg)
37589         {
37590             uint8_t* x = heap_segment_mem(seg);
37591             while (x < heap_segment_allocated (seg))
37592             {
37593                 size_t s = size (x);
37594                 testGCShadowHelper (x);
37595                 x = x + Align (s);
37596             }
37597             seg = heap_segment_next_rw (seg);
37598         }
37599     }
37600
37601     {
37602         // go through large object heap
37603         int alignment = get_alignment_constant(FALSE);
37604         generation* gen = gc_heap::generation_of (max_generation+1);
37605         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
37606
37607         PREFIX_ASSUME(seg != NULL);
37608
37609         while(seg)
37610         {
37611             uint8_t* x = heap_segment_mem(seg);
37612             while (x < heap_segment_allocated (seg))
37613             {
37614                 size_t s = size (x);
37615                 testGCShadowHelper (x);
37616                 x = x + Align (s, alignment);
37617             }
37618             seg = heap_segment_next_rw (seg);
37619         }
37620     }
37621 }
37622 #endif //WRITE_BARRIER_CHECK && !SERVER_GC
37623
37624 #endif // !DACCESS_COMPILE
37625
37626 #ifdef FEATURE_BASICFREEZE
37627 void gc_heap::walk_read_only_segment(heap_segment *seg, void *pvContext, object_callback_func pfnMethodTable, object_callback_func pfnObjRef)
37628 {
37629 #ifdef DACCESS_COMPILE
37630     UNREFERENCED_PARAMETER(seg);
37631     UNREFERENCED_PARAMETER(pvContext);
37632     UNREFERENCED_PARAMETER(pfnMethodTable);
37633     UNREFERENCED_PARAMETER(pfnObjRef);
37634 #else
37635     uint8_t *o = heap_segment_mem(seg);
37636
37637     // small heap alignment constant
37638     int alignment = get_alignment_constant(TRUE);
37639
37640     while (o < heap_segment_allocated(seg))
37641     {
37642         pfnMethodTable(pvContext, o);
37643
37644         if (contain_pointers (o))
37645         {
37646             go_through_object_nostart (method_table (o), o, size(o), oo,
37647                    {
37648                        if (*oo)
37649                            pfnObjRef(pvContext, oo);
37650                    }
37651             );
37652         }
37653
37654         o += Align(size(o), alignment);
37655     }
37656 #endif //!DACCESS_COMPILE
37657 }
37658 #endif // FEATURE_BASICFREEZE
37659
37660 #ifndef DACCESS_COMPILE
37661 HRESULT GCHeap::WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout)
37662 {
37663 #ifdef BACKGROUND_GC
37664     if (recursive_gc_sync::background_running_p())
37665     {
37666         uint32_t dwRet = pGenGCHeap->background_gc_wait(awr_ignored, millisecondsTimeout);
37667         if (dwRet == WAIT_OBJECT_0)
37668             return S_OK;
37669         else if (dwRet == WAIT_TIMEOUT)
37670             return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
37671         else
37672             return E_FAIL;      // It is not clear if what the last error would be if the wait failed,
37673                                 // as there are too many layers in between. The best we can do is to return E_FAIL;
37674     }
37675 #endif
37676
37677     return S_OK;
37678 }
37679 #endif // !DACCESS_COMPILE
37680
37681 void GCHeap::TemporaryEnableConcurrentGC()
37682 {
37683 #ifdef BACKGROUND_GC
37684     gc_heap::temp_disable_concurrent_p = false;
37685 #endif //BACKGROUND_GC
37686 }
37687
37688 void GCHeap::TemporaryDisableConcurrentGC()
37689 {
37690 #ifdef BACKGROUND_GC
37691     gc_heap::temp_disable_concurrent_p = true;
37692 #endif //BACKGROUND_GC
37693 }
37694
37695 bool GCHeap::IsConcurrentGCEnabled()
37696 {
37697 #ifdef BACKGROUND_GC
37698     return (gc_heap::gc_can_use_concurrent && !(gc_heap::temp_disable_concurrent_p));
37699 #else
37700     return FALSE;
37701 #endif //BACKGROUND_GC
37702 }
37703
37704 void GCHeap::SetFinalizeRunOnShutdown(bool value)
37705 {
37706     g_fFinalizerRunOnShutDown = value;
37707 }
37708
37709 void PopulateDacVars(GcDacVars *gcDacVars)
37710 {
37711 #ifndef DACCESS_COMPILE
37712     assert(gcDacVars != nullptr);
37713     *gcDacVars = {};
37714     gcDacVars->major_version_number = 1;
37715     gcDacVars->minor_version_number = 0;
37716     gcDacVars->built_with_svr = &g_built_with_svr_gc;
37717     gcDacVars->build_variant = &g_build_variant;
37718     gcDacVars->gc_structures_invalid_cnt = const_cast<int32_t*>(&GCScan::m_GcStructuresInvalidCnt);
37719     gcDacVars->generation_size = sizeof(generation);
37720     gcDacVars->max_gen = &g_max_generation;
37721 #ifndef MULTIPLE_HEAPS
37722     gcDacVars->mark_array = &gc_heap::mark_array;
37723     gcDacVars->ephemeral_heap_segment = reinterpret_cast<dac_heap_segment**>(&gc_heap::ephemeral_heap_segment);
37724     gcDacVars->current_c_gc_state = const_cast<c_gc_state*>(&gc_heap::current_c_gc_state);
37725     gcDacVars->saved_sweep_ephemeral_seg = reinterpret_cast<dac_heap_segment**>(&gc_heap::saved_sweep_ephemeral_seg);
37726     gcDacVars->saved_sweep_ephemeral_start = &gc_heap::saved_sweep_ephemeral_start;
37727     gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address;
37728     gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address;
37729     gcDacVars->alloc_allocated = &gc_heap::alloc_allocated;
37730     gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj;
37731     gcDacVars->oom_info = &gc_heap::oom_info;
37732     gcDacVars->finalize_queue = reinterpret_cast<dac_finalize_queue**>(&gc_heap::finalize_queue);
37733     gcDacVars->generation_table = reinterpret_cast<dac_generation**>(&gc_heap::generation_table);
37734 #ifdef GC_CONFIG_DRIVEN
37735     gcDacVars->gc_global_mechanisms = reinterpret_cast<size_t**>(&gc_global_mechanisms);
37736     gcDacVars->interesting_data_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_data_per_heap);
37737     gcDacVars->compact_reasons_per_heap = reinterpret_cast<size_t**>(&gc_heap::compact_reasons_per_heap);
37738     gcDacVars->expand_mechanisms_per_heap = reinterpret_cast<size_t**>(&gc_heap::expand_mechanisms_per_heap);
37739     gcDacVars->interesting_mechanism_bits_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_mechanism_bits_per_heap);
37740 #endif // GC_CONFIG_DRIVEN
37741 #ifdef HEAP_ANALYZE
37742     gcDacVars->internal_root_array = &gc_heap::internal_root_array;
37743     gcDacVars->internal_root_array_index = &gc_heap::internal_root_array_index;
37744     gcDacVars->heap_analyze_success = &gc_heap::heap_analyze_success;
37745 #endif // HEAP_ANALYZE
37746 #else
37747     gcDacVars->n_heaps = &gc_heap::n_heaps;
37748     gcDacVars->g_heaps = reinterpret_cast<dac_gc_heap***>(&gc_heap::g_heaps);
37749 #endif // MULTIPLE_HEAPS
37750 #endif // DACCESS_COMPILE
37751 }