Merge pull request #23187 from BruceForstall/AlwaysArchiveCoreFxTestResultsXml
[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 defined(__linux__)
10196     GCToEEInterface::UpdateGCEventStatus(static_cast<int>(GCEventStatus::GetEnabledLevel(GCEventProvider_Default)),
10197                                          static_cast<int>(GCEventStatus::GetEnabledKeywords(GCEventProvider_Default)),
10198                                          static_cast<int>(GCEventStatus::GetEnabledLevel(GCEventProvider_Private)),
10199                                          static_cast<int>(GCEventStatus::GetEnabledKeywords(GCEventProvider_Private)));
10200 #endif // __linux__
10201
10202     if (!init_semi_shared())
10203     {
10204         hres = E_FAIL;
10205     }
10206
10207     return hres;
10208 }
10209
10210 //Initializes PER_HEAP_ISOLATED data members.
10211 int
10212 gc_heap::init_semi_shared()
10213 {
10214     int ret = 0;
10215
10216     // This is used for heap expansion - it's to fix exactly the start for gen 0
10217     // through (max_generation-1). When we expand the heap we allocate all these
10218     // gen starts at the beginning of the new ephemeral seg. 
10219     eph_gen_starts_size = (Align (min_obj_size)) * max_generation;
10220
10221 #ifdef MARK_LIST
10222 #ifdef MULTIPLE_HEAPS
10223     mark_list_size = min (150*1024, max (8192, soh_segment_size/(2*10*32)));
10224     g_mark_list = make_mark_list (mark_list_size*n_heaps);
10225
10226     min_balance_threshold = alloc_quantum_balance_units * CLR_SIZE * 2;
10227 #ifdef PARALLEL_MARK_LIST_SORT
10228     g_mark_list_copy = make_mark_list (mark_list_size*n_heaps);
10229     if (!g_mark_list_copy)
10230     {
10231         goto cleanup;
10232     }
10233 #endif //PARALLEL_MARK_LIST_SORT
10234
10235 #else //MULTIPLE_HEAPS
10236
10237     mark_list_size = max (8192, soh_segment_size/(64*32));
10238     g_mark_list = make_mark_list (mark_list_size);
10239
10240 #endif //MULTIPLE_HEAPS
10241
10242     dprintf (3, ("mark_list_size: %d", mark_list_size));
10243
10244     if (!g_mark_list)
10245     {
10246         goto cleanup;
10247     }
10248 #endif //MARK_LIST
10249
10250 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10251     if (!seg_mapping_table_init())
10252         goto cleanup;
10253 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10254
10255 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10256     seg_table = sorted_table::make_sorted_table();
10257
10258     if (!seg_table)
10259         goto cleanup;
10260 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10261
10262     segment_standby_list = 0;
10263
10264     if (!full_gc_approach_event.CreateManualEventNoThrow(FALSE))
10265     {
10266         goto cleanup;
10267     }
10268     if (!full_gc_end_event.CreateManualEventNoThrow(FALSE))
10269     {
10270         goto cleanup;
10271     }
10272
10273     fgn_maxgen_percent = 0;
10274     fgn_loh_percent = 0;
10275     full_gc_approach_event_set = false;
10276
10277     memset (full_gc_counts, 0, sizeof (full_gc_counts));
10278
10279     last_gc_index = 0;
10280     should_expand_in_full_gc = FALSE;
10281
10282 #ifdef FEATURE_LOH_COMPACTION
10283     loh_compaction_always_p = GCConfig::GetLOHCompactionMode() != 0;
10284     loh_compaction_mode = loh_compaction_default;
10285 #endif //FEATURE_LOH_COMPACTION
10286
10287     loh_size_threshold = (size_t)GCConfig::GetLOHThreshold();
10288     assert (loh_size_threshold >= LARGE_OBJECT_SIZE);
10289
10290 #ifdef BACKGROUND_GC
10291     memset (ephemeral_fgc_counts, 0, sizeof (ephemeral_fgc_counts));
10292     bgc_alloc_spin_count = static_cast<uint32_t>(GCConfig::GetBGCSpinCount());
10293     bgc_alloc_spin = static_cast<uint32_t>(GCConfig::GetBGCSpin());
10294
10295     {   
10296         int number_bgc_threads = 1;
10297 #ifdef MULTIPLE_HEAPS
10298         number_bgc_threads = n_heaps;
10299 #endif //MULTIPLE_HEAPS
10300         if (!create_bgc_threads_support (number_bgc_threads))
10301         {
10302             goto cleanup;
10303         }
10304     }
10305 #endif //BACKGROUND_GC
10306
10307     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
10308
10309 #ifdef GC_CONFIG_DRIVEN
10310     compact_or_sweep_gcs[0] = 0;
10311     compact_or_sweep_gcs[1] = 0;
10312 #endif //GC_CONFIG_DRIVEN
10313
10314 #ifdef SHORT_PLUGS
10315     short_plugs_pad_ratio = (double)DESIRED_PLUG_LENGTH / (double)(DESIRED_PLUG_LENGTH - Align (min_obj_size));
10316 #endif //SHORT_PLUGS
10317
10318     ret = 1;
10319
10320 cleanup:
10321
10322     if (!ret)
10323     {
10324         if (full_gc_approach_event.IsValid())
10325         {
10326             full_gc_approach_event.CloseEvent();
10327         }
10328         if (full_gc_end_event.IsValid())
10329         {
10330             full_gc_end_event.CloseEvent();
10331         }
10332     }
10333
10334     return ret;
10335 }
10336
10337 gc_heap* gc_heap::make_gc_heap (
10338 #ifdef MULTIPLE_HEAPS
10339                                 GCHeap* vm_hp,
10340                                 int heap_number
10341 #endif //MULTIPLE_HEAPS
10342                                 )
10343 {
10344     gc_heap* res = 0;
10345
10346 #ifdef MULTIPLE_HEAPS
10347     res = new (nothrow) gc_heap;
10348     if (!res)
10349         return 0;
10350
10351     res->vm_heap = vm_hp;
10352     res->alloc_context_count = 0;
10353
10354 #ifdef MARK_LIST
10355 #ifdef PARALLEL_MARK_LIST_SORT
10356     res->mark_list_piece_start = new (nothrow) uint8_t**[n_heaps];
10357     if (!res->mark_list_piece_start)
10358         return 0;
10359
10360 #ifdef _PREFAST_ 
10361 #pragma warning(push)
10362 #pragma warning(disable:22011) // Suppress PREFast warning about integer underflow/overflow
10363 #endif // _PREFAST_
10364     res->mark_list_piece_end = new (nothrow) uint8_t**[n_heaps + 32]; // +32 is padding to reduce false sharing
10365 #ifdef _PREFAST_ 
10366 #pragma warning(pop)
10367 #endif // _PREFAST_
10368
10369     if (!res->mark_list_piece_end)
10370         return 0;
10371 #endif //PARALLEL_MARK_LIST_SORT
10372 #endif //MARK_LIST
10373
10374
10375 #endif //MULTIPLE_HEAPS
10376
10377     if (res->init_gc_heap (
10378 #ifdef MULTIPLE_HEAPS
10379         heap_number
10380 #else  //MULTIPLE_HEAPS
10381         0
10382 #endif //MULTIPLE_HEAPS
10383         )==0)
10384     {
10385         return 0;
10386     }
10387
10388 #ifdef MULTIPLE_HEAPS
10389     return res;
10390 #else
10391     return (gc_heap*)1;
10392 #endif //MULTIPLE_HEAPS
10393 }
10394
10395 uint32_t
10396 gc_heap::wait_for_gc_done(int32_t timeOut)
10397 {
10398     bool cooperative_mode = enable_preemptive ();
10399
10400     uint32_t dwWaitResult = NOERROR;
10401
10402     gc_heap* wait_heap = NULL;
10403     while (gc_heap::gc_started)
10404     {       
10405 #ifdef MULTIPLE_HEAPS
10406         wait_heap = GCHeap::GetHeap(heap_select::select_heap(NULL, 0))->pGenGCHeap;
10407         dprintf(2, ("waiting for the gc_done_event on heap %d", wait_heap->heap_number));
10408 #endif // MULTIPLE_HEAPS
10409
10410 #ifdef _PREFAST_
10411         PREFIX_ASSUME(wait_heap != NULL);
10412 #endif // _PREFAST_
10413
10414         dwWaitResult = wait_heap->gc_done_event.Wait(timeOut, FALSE); 
10415     }
10416     disable_preemptive (cooperative_mode);
10417
10418     return dwWaitResult;
10419 }
10420
10421 void 
10422 gc_heap::set_gc_done()
10423 {
10424     enter_gc_done_event_lock();
10425     if (!gc_done_event_set)
10426     {
10427         gc_done_event_set = true;
10428         dprintf (2, ("heap %d: setting gc_done_event", heap_number));
10429         gc_done_event.Set();
10430     }
10431     exit_gc_done_event_lock();
10432 }
10433
10434 void 
10435 gc_heap::reset_gc_done()
10436 {
10437     enter_gc_done_event_lock();
10438     if (gc_done_event_set)
10439     {
10440         gc_done_event_set = false;
10441         dprintf (2, ("heap %d: resetting gc_done_event", heap_number));
10442         gc_done_event.Reset();
10443     }
10444     exit_gc_done_event_lock();
10445 }
10446
10447 void 
10448 gc_heap::enter_gc_done_event_lock()
10449 {
10450     uint32_t dwSwitchCount = 0;
10451 retry:
10452
10453     if (Interlocked::CompareExchange(&gc_done_event_lock, 0, -1) >= 0)
10454     {
10455         while (gc_done_event_lock >= 0)
10456         {
10457             if  (g_num_processors > 1)
10458             {
10459                 int spin_count = yp_spin_count_unit;
10460                 for (int j = 0; j < spin_count; j++)
10461                 {
10462                     if  (gc_done_event_lock < 0)
10463                         break;
10464                     YieldProcessor();           // indicate to the processor that we are spinning
10465                 }
10466                 if  (gc_done_event_lock >= 0)
10467                     GCToOSInterface::YieldThread(++dwSwitchCount);
10468             }
10469             else
10470                 GCToOSInterface::YieldThread(++dwSwitchCount);
10471         }
10472         goto retry;
10473     }
10474 }
10475
10476 void 
10477 gc_heap::exit_gc_done_event_lock()
10478 {
10479     gc_done_event_lock = -1;
10480 }
10481
10482 #ifndef MULTIPLE_HEAPS
10483
10484 #ifdef RECORD_LOH_STATE
10485 int gc_heap::loh_state_index = 0;
10486 gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states];
10487 #endif //RECORD_LOH_STATE
10488
10489 VOLATILE(int32_t) gc_heap::gc_done_event_lock;
10490 VOLATILE(bool) gc_heap::gc_done_event_set;
10491 GCEvent gc_heap::gc_done_event;
10492 #endif //!MULTIPLE_HEAPS
10493 VOLATILE(bool) gc_heap::internal_gc_done;
10494
10495 void gc_heap::add_saved_spinlock_info (
10496             bool loh_p, 
10497             msl_enter_state enter_state, 
10498             msl_take_state take_state)
10499
10500 {
10501 #ifdef SPINLOCK_HISTORY
10502     spinlock_info* current = &last_spinlock_info[spinlock_info_index];
10503
10504     current->enter_state = enter_state;
10505     current->take_state = take_state;
10506     current->thread_id.SetToCurrentThread();
10507     current->loh_p = loh_p;
10508     dprintf (SPINLOCK_LOG, ("[%d]%s %s %s", 
10509         heap_number, 
10510         (loh_p ? "loh" : "soh"),
10511         ((enter_state == me_acquire) ? "E" : "L"),
10512         msl_take_state_str[take_state]));
10513
10514     spinlock_info_index++;
10515
10516     assert (spinlock_info_index <= max_saved_spinlock_info);
10517
10518     if (spinlock_info_index >= max_saved_spinlock_info)
10519     {
10520         spinlock_info_index = 0;
10521     }
10522 #else
10523     MAYBE_UNUSED_VAR(enter_state);
10524     MAYBE_UNUSED_VAR(take_state);
10525 #endif //SPINLOCK_HISTORY
10526 }
10527
10528 int
10529 gc_heap::init_gc_heap (int  h_number)
10530 {
10531 #ifdef MULTIPLE_HEAPS
10532
10533     time_bgc_last = 0;
10534
10535     allocated_since_last_gc = 0;
10536
10537 #ifdef SPINLOCK_HISTORY
10538     spinlock_info_index = 0;
10539     memset (last_spinlock_info, 0, sizeof(last_spinlock_info));
10540 #endif //SPINLOCK_HISTORY
10541
10542     // initialize per heap members.
10543     ephemeral_low = (uint8_t*)1;
10544
10545     ephemeral_high = MAX_PTR;
10546
10547     ephemeral_heap_segment = 0;
10548
10549     freeable_large_heap_segment = 0;
10550
10551     condemned_generation_num = 0;
10552
10553     blocking_collection = FALSE;
10554
10555     generation_skip_ratio = 100;
10556
10557     mark_stack_tos = 0;
10558
10559     mark_stack_bos = 0;
10560
10561     mark_stack_array_length = 0;
10562
10563     mark_stack_array = 0;
10564
10565 #if defined (_DEBUG) && defined (VERIFY_HEAP)
10566     verify_pinned_queue_p = FALSE;
10567 #endif // _DEBUG && VERIFY_HEAP
10568
10569     loh_pinned_queue_tos = 0;
10570
10571     loh_pinned_queue_bos = 0;
10572
10573     loh_pinned_queue_length = 0;
10574
10575     loh_pinned_queue_decay = LOH_PIN_DECAY;
10576
10577     loh_pinned_queue = 0;
10578
10579     min_overflow_address = MAX_PTR;
10580
10581     max_overflow_address = 0;
10582
10583     gen0_bricks_cleared = FALSE;
10584
10585     gen0_must_clear_bricks = 0;
10586
10587     allocation_quantum = CLR_SIZE;
10588
10589     more_space_lock_soh = gc_lock;
10590
10591     more_space_lock_loh = gc_lock;
10592
10593     ro_segments_in_range = FALSE;
10594
10595     loh_alloc_since_cg = 0;
10596
10597     new_heap_segment = NULL;
10598
10599     gen0_allocated_after_gc_p = false;
10600
10601 #ifdef RECORD_LOH_STATE
10602     loh_state_index = 0;
10603 #endif //RECORD_LOH_STATE
10604 #endif //MULTIPLE_HEAPS
10605
10606 #ifdef MULTIPLE_HEAPS
10607     if (h_number > n_heaps)
10608     {
10609         assert (!"Number of heaps exceeded");
10610         return 0;
10611     }
10612
10613     heap_number = h_number;
10614 #endif //MULTIPLE_HEAPS
10615
10616     memset (&oom_info, 0, sizeof (oom_info));
10617     memset (&fgm_result, 0, sizeof (fgm_result));
10618     if (!gc_done_event.CreateManualEventNoThrow(FALSE))
10619     {
10620         return 0;
10621     }
10622     gc_done_event_lock = -1;
10623     gc_done_event_set = false;
10624
10625 #ifndef SEG_MAPPING_TABLE
10626     if (!gc_heap::seg_table->ensure_space_for_insert ())
10627     {
10628         return 0;
10629     }
10630 #endif //!SEG_MAPPING_TABLE
10631
10632     heap_segment* seg = get_initial_segment (soh_segment_size, h_number);
10633     if (!seg)
10634         return 0;
10635
10636     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(seg),
10637                               (size_t)(heap_segment_reserved (seg) - heap_segment_mem(seg)),
10638                               gc_etw_segment_small_object_heap);
10639     
10640 #ifdef SEG_MAPPING_TABLE
10641     seg_mapping_table_add_segment (seg, __this);
10642 #else //SEG_MAPPING_TABLE
10643     seg_table->insert ((uint8_t*)seg, sdelta);
10644 #endif //SEG_MAPPING_TABLE
10645
10646 #ifdef MULTIPLE_HEAPS
10647     heap_segment_heap (seg) = this;
10648 #endif //MULTIPLE_HEAPS
10649
10650     /* todo: Need a global lock for this */
10651     uint32_t* ct = &g_gc_card_table [card_word (card_of (g_gc_lowest_address))];
10652     own_card_table (ct);
10653     card_table = translate_card_table (ct);
10654     /* End of global lock */
10655
10656     brick_table = card_table_brick_table (ct);
10657     highest_address = card_table_highest_address (ct);
10658     lowest_address = card_table_lowest_address (ct);
10659
10660 #ifdef CARD_BUNDLE
10661     card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct), g_gc_lowest_address);
10662     assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] ==
10663             card_table_card_bundle_table (ct));
10664 #endif //CARD_BUNDLE
10665
10666 #ifdef MARK_ARRAY
10667     if (gc_can_use_concurrent)
10668         mark_array = translate_mark_array (card_table_mark_array (&g_gc_card_table[card_word (card_of (g_gc_lowest_address))]));
10669     else
10670         mark_array = NULL;
10671 #endif //MARK_ARRAY
10672
10673     uint8_t*  start = heap_segment_mem (seg);
10674
10675     for (int i = 0; i < 1 + max_generation; i++)
10676     {
10677         make_generation (generation_table [ (max_generation - i) ],
10678                          seg, start, 0);
10679         generation_table [(max_generation - i)].gen_num = max_generation - i;
10680         start += Align (min_obj_size);
10681     }
10682
10683     heap_segment_allocated (seg) = start;
10684     alloc_allocated = start;
10685     heap_segment_used (seg) = start - plug_skew;
10686
10687     ephemeral_heap_segment = seg;
10688
10689 #ifndef SEG_MAPPING_TABLE
10690     if (!gc_heap::seg_table->ensure_space_for_insert ())
10691     {
10692         return 0;
10693     }
10694 #endif //!SEG_MAPPING_TABLE
10695     //Create the large segment generation
10696     heap_segment* lseg = get_initial_segment(min_loh_segment_size, h_number);
10697     if (!lseg)
10698         return 0;
10699     lseg->flags |= heap_segment_flags_loh;
10700
10701     FIRE_EVENT(GCCreateSegment_V1, heap_segment_mem(lseg),
10702                               (size_t)(heap_segment_reserved (lseg) - heap_segment_mem(lseg)),
10703                               gc_etw_segment_large_object_heap);
10704
10705 #ifdef SEG_MAPPING_TABLE
10706     seg_mapping_table_add_segment (lseg, __this);
10707 #else //SEG_MAPPING_TABLE
10708     seg_table->insert ((uint8_t*)lseg, sdelta);
10709 #endif //SEG_MAPPING_TABLE
10710
10711     generation_table [max_generation].free_list_allocator = allocator(NUM_GEN2_ALIST, BASE_GEN2_ALIST, gen2_alloc_list);
10712     //assign the alloc_list for the large generation 
10713     generation_table [max_generation+1].free_list_allocator = allocator(NUM_LOH_ALIST, BASE_LOH_ALIST, loh_alloc_list);
10714     generation_table [max_generation+1].gen_num = max_generation+1;
10715     make_generation (generation_table [max_generation+1],lseg, heap_segment_mem (lseg), 0);
10716     heap_segment_allocated (lseg) = heap_segment_mem (lseg) + Align (min_obj_size, get_alignment_constant (FALSE));
10717     heap_segment_used (lseg) = heap_segment_allocated (lseg) - plug_skew;
10718
10719     for (int gen_num = 0; gen_num <= 1 + max_generation; gen_num++)
10720     {
10721         generation*  gen = generation_of (gen_num);
10722         make_unused_array (generation_allocation_start (gen), Align (min_obj_size));
10723     }
10724
10725 #ifdef MULTIPLE_HEAPS
10726     heap_segment_heap (lseg) = this;
10727
10728     //initialize the alloc context heap
10729     generation_alloc_context (generation_of (0))->set_alloc_heap(vm_heap);
10730
10731     //initialize the alloc context heap
10732     generation_alloc_context (generation_of (max_generation+1))->set_alloc_heap(vm_heap);
10733
10734 #endif //MULTIPLE_HEAPS
10735
10736     //Do this only once
10737 #ifdef MULTIPLE_HEAPS
10738     if (h_number == 0)
10739 #endif //MULTIPLE_HEAPS
10740     {
10741 #ifndef INTERIOR_POINTERS
10742         //set the brick_table for large objects
10743         //but default value is clearded
10744         //clear_brick_table ((uint8_t*)heap_segment_mem (lseg),
10745         //                   (uint8_t*)heap_segment_reserved (lseg));
10746
10747 #else //INTERIOR_POINTERS
10748
10749         //Because of the interior pointer business, we have to clear
10750         //the whole brick table
10751         //but the default value is cleared
10752         // clear_brick_table (lowest_address, highest_address);
10753 #endif //INTERIOR_POINTERS
10754     }
10755
10756     if (!init_dynamic_data())
10757     {
10758         return 0;
10759     }
10760
10761     etw_allocation_running_amount[0] = 0;
10762     etw_allocation_running_amount[1] = 0;
10763
10764     //needs to be done after the dynamic data has been initialized
10765 #ifndef MULTIPLE_HEAPS
10766     allocation_running_amount = dd_min_size (dynamic_data_of (0));
10767 #endif //!MULTIPLE_HEAPS
10768
10769     fgn_last_alloc = dd_min_size (dynamic_data_of (0));
10770
10771     mark* arr = new (nothrow) (mark [MARK_STACK_INITIAL_LENGTH]);
10772     if (!arr)
10773         return 0;
10774
10775     make_mark_stack(arr);
10776
10777 #ifdef BACKGROUND_GC
10778     freeable_small_heap_segment = 0;
10779     gchist_index_per_heap = 0;
10780     uint8_t** b_arr = new (nothrow) (uint8_t* [MARK_STACK_INITIAL_LENGTH]);
10781     if (!b_arr)
10782         return 0;
10783
10784     make_background_mark_stack (b_arr);
10785 #endif //BACKGROUND_GC
10786
10787     ephemeral_low = generation_allocation_start(generation_of(max_generation - 1));
10788     ephemeral_high = heap_segment_reserved(ephemeral_heap_segment);
10789     if (heap_number == 0)
10790     {
10791         stomp_write_barrier_initialize(
10792 #ifdef MULTIPLE_HEAPS
10793             reinterpret_cast<uint8_t*>(1), reinterpret_cast<uint8_t*>(~0)
10794 #else
10795             ephemeral_low, ephemeral_high
10796 #endif //!MULTIPLE_HEAPS
10797         );
10798     }
10799
10800 #ifdef MARK_ARRAY
10801     // why would we clear the mark array for this page? it should be cleared..
10802     // clear the first committed page
10803     //if(gc_can_use_concurrent)
10804     //{
10805     //    clear_mark_array (align_lower_page (heap_segment_mem (seg)), heap_segment_committed (seg));
10806     //}
10807 #endif //MARK_ARRAY
10808
10809 #ifdef MULTIPLE_HEAPS
10810     //register the heap in the heaps array
10811
10812     if (!create_gc_thread ())
10813         return 0;
10814
10815     g_heaps [heap_number] = this;
10816
10817 #endif //MULTIPLE_HEAPS
10818
10819 #ifdef FEATURE_PREMORTEM_FINALIZATION
10820     HRESULT hr = AllocateCFinalize(&finalize_queue);
10821     if (FAILED(hr))
10822         return 0;
10823 #endif // FEATURE_PREMORTEM_FINALIZATION
10824
10825     max_free_space_items = MAX_NUM_FREE_SPACES;
10826
10827     bestfit_seg = new (nothrow) seg_free_spaces (heap_number);
10828
10829     if (!bestfit_seg)
10830     {
10831         return 0;
10832     }
10833
10834     if (!bestfit_seg->alloc())
10835     {
10836         return 0;
10837     }
10838
10839     last_gc_before_oom = FALSE;
10840
10841     sufficient_gen0_space_p = FALSE;
10842
10843 #ifdef MULTIPLE_HEAPS
10844
10845 #ifdef HEAP_ANALYZE
10846
10847     heap_analyze_success = TRUE;
10848
10849     internal_root_array  = 0;
10850
10851     internal_root_array_index = 0;
10852
10853     internal_root_array_length = initial_internal_roots;
10854
10855     current_obj          = 0;
10856
10857     current_obj_size     = 0;
10858
10859 #endif //HEAP_ANALYZE
10860
10861 #endif // MULTIPLE_HEAPS
10862
10863 #ifdef BACKGROUND_GC
10864     bgc_thread_id.Clear();
10865
10866     if (!create_bgc_thread_support())
10867     {
10868         return 0;
10869     }
10870
10871     bgc_alloc_lock = new (nothrow) exclusive_sync;
10872     if (!bgc_alloc_lock)
10873     {
10874         return 0;
10875     }
10876
10877     bgc_alloc_lock->init();
10878
10879     if (h_number == 0)
10880     {
10881         if (!recursive_gc_sync::init())
10882             return 0;
10883     }
10884
10885     bgc_thread_running = 0;
10886     bgc_thread = 0;
10887     bgc_threads_timeout_cs.Initialize();
10888     expanded_in_fgc = 0;
10889     current_bgc_state = bgc_not_in_process;
10890     background_soh_alloc_count = 0;
10891     background_loh_alloc_count = 0;
10892     bgc_overflow_count = 0;
10893     end_loh_size = dd_min_size (dynamic_data_of (max_generation + 1));
10894 #endif //BACKGROUND_GC
10895
10896 #ifdef GC_CONFIG_DRIVEN
10897     memset (interesting_data_per_heap, 0, sizeof (interesting_data_per_heap));
10898     memset(compact_reasons_per_heap, 0, sizeof (compact_reasons_per_heap));
10899     memset(expand_mechanisms_per_heap, 0, sizeof (expand_mechanisms_per_heap));
10900     memset(interesting_mechanism_bits_per_heap, 0, sizeof (interesting_mechanism_bits_per_heap));
10901 #endif //GC_CONFIG_DRIVEN
10902
10903     return 1;
10904 }
10905
10906 void
10907 gc_heap::destroy_semi_shared()
10908 {
10909 //TODO: will need to move this to per heap
10910 //#ifdef BACKGROUND_GC
10911 //    if (c_mark_list)
10912 //        delete c_mark_list;
10913 //#endif //BACKGROUND_GC
10914
10915 #ifdef MARK_LIST
10916     if (g_mark_list)
10917         delete g_mark_list;
10918 #endif //MARK_LIST
10919
10920 #if defined(SEG_MAPPING_TABLE) && !defined(GROWABLE_SEG_MAPPING_TABLE)
10921     if (seg_mapping_table)
10922         delete seg_mapping_table;
10923 #endif //SEG_MAPPING_TABLE && !GROWABLE_SEG_MAPPING_TABLE
10924
10925 #if !defined(SEG_MAPPING_TABLE) || defined(FEATURE_BASICFREEZE)
10926     //destroy the segment map
10927     seg_table->delete_sorted_table();
10928 #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE
10929 }
10930
10931 void
10932 gc_heap::self_destroy()
10933 {
10934 #ifdef BACKGROUND_GC
10935     kill_gc_thread();
10936 #endif //BACKGROUND_GC
10937
10938     if (gc_done_event.IsValid())
10939     {
10940         gc_done_event.CloseEvent();
10941     }
10942
10943     // destroy every segment.
10944     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
10945
10946     PREFIX_ASSUME(seg != NULL);
10947
10948     heap_segment* next_seg;
10949     while (seg)
10950     {
10951         next_seg = heap_segment_next_rw (seg);
10952         delete_heap_segment (seg);
10953         seg = next_seg;
10954     }
10955
10956     seg = heap_segment_rw (generation_start_segment (generation_of (max_generation+1)));
10957
10958     PREFIX_ASSUME(seg != NULL);
10959
10960     while (seg)
10961     {
10962         next_seg = heap_segment_next_rw (seg);
10963         delete_heap_segment (seg);
10964         seg = next_seg;
10965     }
10966
10967     // get rid of the card table
10968     release_card_table (card_table);
10969
10970     // destroy the mark stack
10971     delete mark_stack_array;
10972
10973 #ifdef FEATURE_PREMORTEM_FINALIZATION
10974     if (finalize_queue)
10975         delete finalize_queue;
10976 #endif // FEATURE_PREMORTEM_FINALIZATION
10977 }
10978
10979 void
10980 gc_heap::destroy_gc_heap(gc_heap* heap)
10981 {
10982     heap->self_destroy();
10983     delete heap;
10984 }
10985
10986 // Destroys resources owned by gc. It is assumed that a last GC has been performed and that
10987 // the finalizer queue has been drained.
10988 void gc_heap::shutdown_gc()
10989 {
10990     destroy_semi_shared();
10991
10992 #ifdef MULTIPLE_HEAPS
10993     //delete the heaps array
10994     delete g_heaps;
10995     destroy_thread_support();
10996     n_heaps = 0;
10997 #endif //MULTIPLE_HEAPS
10998     //destroy seg_manager
10999
11000     destroy_initial_memory();
11001
11002     GCToOSInterface::Shutdown();
11003 }
11004
11005 inline
11006 BOOL gc_heap::size_fit_p (size_t size REQD_ALIGN_AND_OFFSET_DCL, uint8_t* alloc_pointer, uint8_t* alloc_limit,
11007                           uint8_t* old_loc, int use_padding)
11008 {
11009     BOOL already_padded = FALSE;
11010 #ifdef SHORT_PLUGS
11011     if ((old_loc != 0) && (use_padding & USE_PADDING_FRONT))
11012     {
11013         alloc_pointer = alloc_pointer + Align (min_obj_size);
11014         already_padded = TRUE;
11015     }
11016 #endif //SHORT_PLUGS
11017
11018     if (!((old_loc == 0) || same_large_alignment_p (old_loc, alloc_pointer)))
11019         size = size + switch_alignment_size (already_padded);
11020
11021 #ifdef FEATURE_STRUCTALIGN
11022     alloc_pointer = StructAlign(alloc_pointer, requiredAlignment, alignmentOffset);
11023 #endif // FEATURE_STRUCTALIGN
11024
11025     // in allocate_in_condemned_generation we can have this when we
11026     // set the alloc_limit to plan_allocated which could be less than 
11027     // alloc_ptr
11028     if (alloc_limit < alloc_pointer)
11029     {
11030         return FALSE;
11031     }
11032
11033     if (old_loc != 0)
11034     {
11035         return (((size_t)(alloc_limit - alloc_pointer) >= (size + ((use_padding & USE_PADDING_TAIL)? Align(min_obj_size) : 0))) 
11036 #ifdef SHORT_PLUGS
11037                 ||((!(use_padding & USE_PADDING_FRONT)) && ((alloc_pointer + size) == alloc_limit))
11038 #else //SHORT_PLUGS
11039                 ||((alloc_pointer + size) == alloc_limit)
11040 #endif //SHORT_PLUGS
11041             );
11042     }
11043     else
11044     {
11045         assert (size == Align (min_obj_size));
11046         return ((size_t)(alloc_limit - alloc_pointer) >= size);
11047     }
11048 }
11049
11050 inline
11051 BOOL gc_heap::a_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit,
11052                             int align_const)
11053 {
11054     // We could have run into cases where this is true when alloc_allocated is the 
11055     // the same as the seg committed.
11056     if (alloc_limit < alloc_pointer)
11057     {
11058         return FALSE;
11059     }
11060
11061     return ((size_t)(alloc_limit - alloc_pointer) >= (size + Align(min_obj_size, align_const)));
11062 }
11063
11064 // Grow by committing more pages
11065 BOOL gc_heap::grow_heap_segment (heap_segment* seg, uint8_t* high_address, bool* hard_limit_exceeded_p)
11066 {
11067     assert (high_address <= heap_segment_reserved (seg));
11068
11069     if (hard_limit_exceeded_p)
11070         *hard_limit_exceeded_p = false;
11071
11072     //return 0 if we are at the end of the segment.
11073     if (align_on_page (high_address) > heap_segment_reserved (seg))
11074         return FALSE;
11075
11076     if (high_address <= heap_segment_committed (seg))
11077         return TRUE;
11078
11079     size_t c_size = align_on_page ((size_t)(high_address - heap_segment_committed (seg)));
11080     c_size = max (c_size, commit_min_th);
11081     c_size = min (c_size, (size_t)(heap_segment_reserved (seg) - heap_segment_committed (seg)));
11082
11083     if (c_size == 0)
11084         return FALSE;
11085
11086     STRESS_LOG2(LF_GC, LL_INFO10000,
11087                 "Growing heap_segment: %Ix high address: %Ix\n",
11088                 (size_t)seg, (size_t)high_address);
11089
11090     bool ret = virtual_commit (heap_segment_committed (seg), c_size, heap_number, hard_limit_exceeded_p);
11091     if (ret)
11092     {
11093 #ifdef MARK_ARRAY
11094 #ifndef BACKGROUND_GC
11095         clear_mark_array (heap_segment_committed (seg),
11096                         heap_segment_committed (seg)+c_size, TRUE);
11097 #endif //BACKGROUND_GC
11098 #endif //MARK_ARRAY
11099         heap_segment_committed (seg) += c_size;
11100
11101         STRESS_LOG1(LF_GC, LL_INFO10000, "New commit: %Ix",
11102                     (size_t)heap_segment_committed (seg));
11103
11104         assert (heap_segment_committed (seg) <= heap_segment_reserved (seg));
11105         assert (high_address <= heap_segment_committed (seg));
11106     }
11107
11108     return !!ret;
11109 }
11110
11111 inline
11112 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)
11113 {
11114 #ifdef SHORT_PLUGS
11115     if ((old_loc != 0) && pad_front_p)
11116     {
11117         allocated = allocated + Align (min_obj_size);
11118     }
11119 #endif //SHORT_PLUGS
11120
11121     if (!((old_loc == 0) || same_large_alignment_p (old_loc, allocated)))
11122         size = size + switch_alignment_size (FALSE);
11123 #ifdef FEATURE_STRUCTALIGN
11124     size_t pad = ComputeStructAlignPad(allocated, requiredAlignment, alignmentOffset);
11125     return grow_heap_segment (seg, allocated + pad + size);
11126 #else // FEATURE_STRUCTALIGN
11127     return grow_heap_segment (seg, allocated + size);
11128 #endif // FEATURE_STRUCTALIGN
11129 }
11130
11131 //used only in older generation allocation (i.e during gc).
11132 void gc_heap::adjust_limit (uint8_t* start, size_t limit_size, generation* gen,
11133                             int gennum)
11134 {
11135     UNREFERENCED_PARAMETER(gennum);
11136     dprintf (3, ("gc Expanding segment allocation"));
11137     heap_segment* seg = generation_allocation_segment (gen);
11138     if ((generation_allocation_limit (gen) != start) || (start != heap_segment_plan_allocated (seg)))
11139     {
11140         if (generation_allocation_limit (gen) == heap_segment_plan_allocated (seg))
11141         {
11142             assert (generation_allocation_pointer (gen) >= heap_segment_mem (seg));
11143             assert (generation_allocation_pointer (gen) <= heap_segment_committed (seg));
11144             heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
11145         }
11146         else
11147         {
11148             uint8_t*  hole = generation_allocation_pointer (gen);
11149             size_t  size = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
11150
11151             if (size != 0)
11152             {
11153                 dprintf (3, ("filling up hole: %Ix, size %Ix", hole, size));
11154                 size_t allocated_size = generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen);
11155                 if (size >= Align (min_free_list))
11156                 {
11157                     if (allocated_size < min_free_list)
11158                     {
11159                         if (size >= (Align (min_free_list) + Align (min_obj_size)))
11160                         {
11161                             //split hole into min obj + threadable free item
11162                             make_unused_array (hole, min_obj_size);
11163                             generation_free_obj_space (gen) += Align (min_obj_size);
11164                             make_unused_array (hole + Align (min_obj_size), size - Align (min_obj_size));
11165                             generation_free_list_space (gen) += size - Align (min_obj_size);
11166                             generation_allocator(gen)->thread_item_front (hole + Align (min_obj_size), 
11167                                                                           size - Align (min_obj_size));
11168                             add_gen_free (gen->gen_num, (size - Align (min_obj_size)));
11169                         }
11170                         else
11171                         {
11172                             dprintf (3, ("allocated size too small, can't put back rest on free list %Ix", allocated_size));
11173                             make_unused_array (hole, size);
11174                             generation_free_obj_space (gen) += size;
11175                         }
11176                     }
11177                     else 
11178                     {
11179                         dprintf (3, ("threading hole in front of free list"));
11180                         make_unused_array (hole, size);
11181                         generation_free_list_space (gen) += size;
11182                         generation_allocator(gen)->thread_item_front (hole, size);
11183                         add_gen_free (gen->gen_num, size);
11184                     }
11185                 }
11186                 else
11187                 {
11188                     make_unused_array (hole, size);
11189                     generation_free_obj_space (gen) += size;
11190                 }
11191             }
11192         }
11193         generation_allocation_pointer (gen) = start;
11194         generation_allocation_context_start_region (gen) = start;
11195     }
11196     generation_allocation_limit (gen) = (start + limit_size);
11197 }
11198
11199 void verify_mem_cleared (uint8_t* start, size_t size)
11200 {
11201     if (!Aligned (size))
11202     {
11203         FATAL_GC_ERROR();
11204     }
11205
11206     PTR_PTR curr_ptr = (PTR_PTR) start;
11207     for (size_t i = 0; i < size / sizeof(PTR_PTR); i++)
11208     {
11209         if (*(curr_ptr++) != 0)
11210         {
11211             FATAL_GC_ERROR();
11212         }
11213     }
11214 }
11215
11216 #if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
11217 void gc_heap::set_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11218 {
11219     size_t start_mark_bit = mark_bit_of (start);
11220     size_t end_mark_bit = mark_bit_of (end);
11221     unsigned int startbit = mark_bit_bit (start_mark_bit);
11222     unsigned int endbit = mark_bit_bit (end_mark_bit);
11223     size_t startwrd = mark_bit_word (start_mark_bit);
11224     size_t endwrd = mark_bit_word (end_mark_bit);
11225
11226     dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
11227         (size_t)start, (size_t)start_mark_bit, 
11228         (size_t)end, (size_t)end_mark_bit));
11229
11230     unsigned int firstwrd = ~(lowbits (~0, startbit));
11231     unsigned int lastwrd = ~(highbits (~0, endbit));
11232
11233     if (startwrd == endwrd)
11234     {
11235         unsigned int wrd = firstwrd & lastwrd;
11236         mark_array[startwrd] |= wrd;
11237         return;
11238     }
11239
11240     // set the first mark word.
11241     if (startbit)
11242     {
11243         mark_array[startwrd] |= firstwrd;
11244         startwrd++;
11245     }
11246
11247     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11248     {
11249         mark_array[wrdtmp] = ~(unsigned int)0;
11250     }
11251
11252     // set the last mark word.
11253     if (endbit)
11254     {
11255         mark_array[endwrd] |= lastwrd;
11256     }
11257 }
11258
11259 // makes sure that the mark array bits between start and end are 0.
11260 void gc_heap::check_batch_mark_array_bits (uint8_t* start, uint8_t* end)
11261 {
11262     size_t start_mark_bit = mark_bit_of (start);
11263     size_t end_mark_bit = mark_bit_of (end);
11264     unsigned int startbit = mark_bit_bit (start_mark_bit);
11265     unsigned int endbit = mark_bit_bit (end_mark_bit);
11266     size_t startwrd = mark_bit_word (start_mark_bit);
11267     size_t endwrd = mark_bit_word (end_mark_bit);
11268
11269     //dprintf (3, ("Setting all mark array bits between [%Ix:%Ix-[%Ix:%Ix", 
11270     //    (size_t)start, (size_t)start_mark_bit, 
11271     //    (size_t)end, (size_t)end_mark_bit));
11272
11273     unsigned int firstwrd = ~(lowbits (~0, startbit));
11274     unsigned int lastwrd = ~(highbits (~0, endbit));
11275
11276     if (startwrd == endwrd)
11277     {
11278         unsigned int wrd = firstwrd & lastwrd;
11279         if (mark_array[startwrd] & wrd)
11280         {
11281             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11282                             wrd, startwrd, 
11283                             mark_array [startwrd], mark_word_address (startwrd)));
11284             FATAL_GC_ERROR();
11285         }
11286         return;
11287     }
11288
11289     // set the first mark word.
11290     if (startbit)
11291     {
11292         if (mark_array[startwrd] & firstwrd)
11293         {
11294             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11295                             firstwrd, startwrd, 
11296                             mark_array [startwrd], mark_word_address (startwrd)));
11297             FATAL_GC_ERROR();
11298         }
11299
11300         startwrd++;
11301     }
11302
11303     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
11304     {
11305         if (mark_array[wrdtmp])
11306         {
11307             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11308                             wrdtmp, 
11309                             mark_array [wrdtmp], mark_word_address (wrdtmp)));
11310             FATAL_GC_ERROR();
11311         }
11312     }
11313
11314     // set the last mark word.
11315     if (endbit)
11316     {
11317         if (mark_array[endwrd] & lastwrd)
11318         {
11319             dprintf  (3, ("The %Ix portion of mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
11320                             lastwrd, lastwrd, 
11321                             mark_array [lastwrd], mark_word_address (lastwrd)));
11322             FATAL_GC_ERROR();
11323         }
11324     }
11325 }
11326 #endif //VERIFY_HEAP && BACKGROUND_GC
11327
11328 allocator::allocator (unsigned int num_b, size_t fbs, alloc_list* b)
11329 {
11330     assert (num_b < MAX_BUCKET_COUNT);
11331     num_buckets = num_b;
11332     frst_bucket_size = fbs;
11333     buckets = b;
11334 }
11335
11336 alloc_list& allocator::alloc_list_of (unsigned int bn)
11337 {
11338     assert (bn < num_buckets);
11339     if (bn == 0)
11340         return first_bucket;
11341     else
11342         return buckets [bn-1];
11343 }
11344
11345 size_t& allocator::alloc_list_damage_count_of (unsigned int bn)
11346 {
11347     assert (bn < num_buckets);
11348     if (bn == 0)
11349         return first_bucket.alloc_list_damage_count();
11350     else
11351         return buckets [bn-1].alloc_list_damage_count();
11352 }
11353
11354 void allocator::unlink_item (unsigned int bn, uint8_t* item, uint8_t* prev_item, BOOL use_undo_p)
11355 {
11356     //unlink the free_item
11357     alloc_list* al = &alloc_list_of (bn);
11358     if (prev_item)
11359     {
11360         if (use_undo_p && (free_list_undo (prev_item) == UNDO_EMPTY))
11361         {
11362             assert (item == free_list_slot (prev_item));
11363             free_list_undo (prev_item) = item;
11364             alloc_list_damage_count_of (bn)++;
11365         }
11366         free_list_slot (prev_item) = free_list_slot(item);
11367     }
11368     else
11369     {
11370         al->alloc_list_head() = (uint8_t*)free_list_slot(item);
11371     }
11372     if (al->alloc_list_tail() == item)
11373     {
11374         al->alloc_list_tail() = prev_item;
11375     }
11376 }
11377
11378 void allocator::clear()
11379 {
11380     for (unsigned int i = 0; i < num_buckets; i++)
11381     {
11382         alloc_list_head_of (i) = 0;
11383         alloc_list_tail_of (i) = 0;
11384     }
11385 }
11386
11387 //always thread to the end.
11388 void allocator::thread_free_item (uint8_t* item, uint8_t*& head, uint8_t*& tail)
11389 {
11390     free_list_slot (item) = 0;
11391     free_list_undo (item) = UNDO_EMPTY;
11392     assert (item != head);
11393
11394     if (head == 0)
11395     {
11396        head = item;
11397     }
11398     //TODO: This shouldn't happen anymore - verify that's the case.
11399     //the following is necessary because the last free element
11400     //may have been truncated, and tail isn't updated.
11401     else if (free_list_slot (head) == 0)
11402     {
11403         free_list_slot (head) = item;
11404     }
11405     else
11406     {
11407         assert (item != tail);
11408         assert (free_list_slot(tail) == 0);
11409         free_list_slot (tail) = item;
11410     }
11411     tail = item;
11412 }
11413
11414 void allocator::thread_item (uint8_t* item, size_t size)
11415 {
11416     size_t sz = frst_bucket_size;
11417     unsigned int a_l_number = 0; 
11418
11419     for (; a_l_number < (num_buckets-1); a_l_number++)
11420     {
11421         if (size < sz)
11422         {
11423             break;
11424         }
11425         sz = sz * 2;
11426     }
11427     alloc_list* al = &alloc_list_of (a_l_number);
11428     thread_free_item (item, 
11429                       al->alloc_list_head(),
11430                       al->alloc_list_tail());
11431 }
11432
11433 void allocator::thread_item_front (uint8_t* item, size_t size)
11434 {
11435     //find right free list
11436     size_t sz = frst_bucket_size;
11437     unsigned int a_l_number = 0; 
11438     for (; a_l_number < (num_buckets-1); a_l_number++)
11439     {
11440         if (size < sz)
11441         {
11442             break;
11443         }
11444         sz = sz * 2;
11445     }
11446     alloc_list* al = &alloc_list_of (a_l_number);
11447     free_list_slot (item) = al->alloc_list_head();
11448     free_list_undo (item) = UNDO_EMPTY;
11449
11450     if (al->alloc_list_tail() == 0)
11451     {
11452         al->alloc_list_tail() = al->alloc_list_head();
11453     }
11454     al->alloc_list_head() = item;
11455     if (al->alloc_list_tail() == 0)
11456     {
11457         al->alloc_list_tail() = item;
11458     }
11459 }
11460
11461 void allocator::copy_to_alloc_list (alloc_list* toalist)
11462 {
11463     for (unsigned int i = 0; i < num_buckets; i++)
11464     {
11465         toalist [i] = alloc_list_of (i);
11466 #ifdef FL_VERIFICATION
11467         uint8_t* free_item = alloc_list_head_of (i);
11468         size_t count = 0;
11469         while (free_item)
11470         {
11471             count++;
11472             free_item = free_list_slot (free_item);
11473         }
11474
11475         toalist[i].item_count = count;
11476 #endif //FL_VERIFICATION
11477     }
11478 }
11479
11480 void allocator::copy_from_alloc_list (alloc_list* fromalist)
11481 {
11482     BOOL repair_list = !discard_if_no_fit_p ();
11483     for (unsigned int i = 0; i < num_buckets; i++)
11484     {
11485         size_t count = alloc_list_damage_count_of (i);
11486         alloc_list_of (i) = fromalist [i];
11487         assert (alloc_list_damage_count_of (i) == 0);
11488
11489         if (repair_list)
11490         {
11491             //repair the the list
11492             //new items may have been added during the plan phase 
11493             //items may have been unlinked. 
11494             uint8_t* free_item = alloc_list_head_of (i);
11495             while (free_item && count)
11496             {
11497                 assert (((CObjectHeader*)free_item)->IsFree());
11498                 if ((free_list_undo (free_item) != UNDO_EMPTY))
11499                 {
11500                     count--;
11501                     free_list_slot (free_item) = free_list_undo (free_item);
11502                     free_list_undo (free_item) = UNDO_EMPTY;
11503                 }
11504
11505                 free_item = free_list_slot (free_item);
11506             }
11507
11508 #ifdef FL_VERIFICATION
11509             free_item = alloc_list_head_of (i);
11510             size_t item_count = 0;
11511             while (free_item)
11512             {
11513                 item_count++;
11514                 free_item = free_list_slot (free_item);
11515             }
11516
11517             assert (item_count == alloc_list_of (i).item_count);
11518 #endif //FL_VERIFICATION
11519         }
11520 #ifdef DEBUG
11521         uint8_t* tail_item = alloc_list_tail_of (i);
11522         assert ((tail_item == 0) || (free_list_slot (tail_item) == 0));
11523 #endif
11524     }
11525 }
11526
11527 void allocator::commit_alloc_list_changes()
11528 {
11529     BOOL repair_list = !discard_if_no_fit_p ();
11530     if (repair_list)
11531     {
11532         for (unsigned int i = 0; i < num_buckets; i++)
11533         {
11534             //remove the undo info from list. 
11535             uint8_t* free_item = alloc_list_head_of (i);
11536             size_t count = alloc_list_damage_count_of (i);
11537             while (free_item && count)
11538             {
11539                 assert (((CObjectHeader*)free_item)->IsFree());
11540
11541                 if (free_list_undo (free_item) != UNDO_EMPTY)
11542                 {
11543                     free_list_undo (free_item) = UNDO_EMPTY;
11544                     count--;
11545                 }
11546
11547                 free_item = free_list_slot (free_item);
11548             }
11549
11550             alloc_list_damage_count_of (i) = 0; 
11551         }
11552     }
11553 }
11554
11555 void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size,
11556                                 alloc_context* acontext, heap_segment* seg,
11557                                 int align_const, int gen_number)
11558 {
11559     bool loh_p = (gen_number > 0);
11560     GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
11561
11562     size_t aligned_min_obj_size = Align(min_obj_size, align_const);
11563
11564     if (seg)
11565     {
11566         assert (heap_segment_used (seg) <= heap_segment_committed (seg));
11567     }
11568
11569 #ifdef MULTIPLE_HEAPS
11570     if (gen_number == 0)
11571     {
11572         if (!gen0_allocated_after_gc_p)
11573         {
11574             gen0_allocated_after_gc_p = true;
11575         }
11576     }
11577 #endif //MULTIPLE_HEAPS
11578
11579     dprintf (3, ("Expanding segment allocation [%Ix, %Ix[", (size_t)start,
11580                (size_t)start + limit_size - aligned_min_obj_size));
11581
11582     if ((acontext->alloc_limit != start) &&
11583         (acontext->alloc_limit + aligned_min_obj_size)!= start)
11584     {
11585         uint8_t*  hole = acontext->alloc_ptr;
11586         if (hole != 0)
11587         {
11588             size_t  size = (acontext->alloc_limit - acontext->alloc_ptr);
11589             dprintf (3, ("filling up hole [%Ix, %Ix[", (size_t)hole, (size_t)hole + size + Align (min_obj_size, align_const)));
11590             // when we are finishing an allocation from a free list
11591             // we know that the free area was Align(min_obj_size) larger
11592             acontext->alloc_bytes -= size;
11593             size_t free_obj_size = size + aligned_min_obj_size;
11594             make_unused_array (hole, free_obj_size);
11595             generation_free_obj_space (generation_of (gen_number)) += free_obj_size;
11596         }
11597         acontext->alloc_ptr = start;
11598     }
11599     else
11600     {
11601         if (gen_number == 0)
11602         {
11603             size_t pad_size = Align (min_obj_size, align_const);
11604             make_unused_array (acontext->alloc_ptr, pad_size);
11605             dprintf (3, ("contigous ac: making min obj gap %Ix->%Ix(%Id)", 
11606                 acontext->alloc_ptr, (acontext->alloc_ptr + pad_size), pad_size));
11607             acontext->alloc_ptr += pad_size;
11608         }
11609     }
11610     acontext->alloc_limit = (start + limit_size - aligned_min_obj_size);
11611     acontext->alloc_bytes += limit_size - ((gen_number < max_generation + 1) ? aligned_min_obj_size : 0);
11612
11613 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
11614     if (g_fEnableAppDomainMonitoring)
11615     {
11616         GCToEEInterface::RecordAllocatedBytesForHeap(limit_size, heap_number);
11617     }
11618 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
11619
11620     uint8_t* saved_used = 0;
11621
11622     if (seg)
11623     {
11624         saved_used = heap_segment_used (seg);
11625     }
11626
11627     if (seg == ephemeral_heap_segment)
11628     {
11629         //Sometimes the allocated size is advanced without clearing the
11630         //memory. Let's catch up here
11631         if (heap_segment_used (seg) < (alloc_allocated - plug_skew))
11632         {
11633 #ifdef MARK_ARRAY
11634 #ifndef BACKGROUND_GC
11635             clear_mark_array (heap_segment_used (seg) + plug_skew, alloc_allocated);
11636 #endif //BACKGROUND_GC
11637 #endif //MARK_ARRAY
11638             heap_segment_used (seg) = alloc_allocated - plug_skew;
11639         }
11640     }
11641 #ifdef BACKGROUND_GC
11642     else if (seg)
11643     {
11644         uint8_t* old_allocated = heap_segment_allocated (seg) - plug_skew - limit_size;
11645 #ifdef FEATURE_LOH_COMPACTION
11646         old_allocated -= Align (loh_padding_obj_size, align_const);
11647 #endif //FEATURE_LOH_COMPACTION
11648
11649         assert (heap_segment_used (seg) >= old_allocated);
11650     }
11651 #endif //BACKGROUND_GC
11652     if ((seg == 0) ||
11653         (start - plug_skew + limit_size) <= heap_segment_used (seg))
11654     {
11655         add_saved_spinlock_info (loh_p, me_release, mt_clr_mem);
11656         leave_spin_lock (msl);
11657         dprintf (3, ("clearing memory at %Ix for %d bytes", (start - plug_skew), limit_size));
11658         memclr (start - plug_skew, limit_size);
11659     }
11660     else
11661     {
11662         uint8_t* used = heap_segment_used (seg);
11663         heap_segment_used (seg) = start + limit_size - plug_skew;
11664
11665         add_saved_spinlock_info (loh_p, me_release, mt_clr_mem);
11666         leave_spin_lock (msl);
11667
11668         if ((start - plug_skew) < used)
11669         {
11670             if (used != saved_used)
11671             {
11672                 FATAL_GC_ERROR ();
11673             }
11674
11675             dprintf (2, ("clearing memory before used at %Ix for %Id bytes", 
11676                 (start - plug_skew), (plug_skew + used - start)));
11677             memclr (start - plug_skew, used - (start - plug_skew));
11678         }
11679     }
11680
11681     //this portion can be done after we release the lock
11682     if (seg == ephemeral_heap_segment)
11683     {
11684 #ifdef FFIND_OBJECT
11685         if (gen0_must_clear_bricks > 0)
11686         {
11687             //set the brick table to speed up find_object
11688             size_t b = brick_of (acontext->alloc_ptr);
11689             set_brick (b, acontext->alloc_ptr - brick_address (b));
11690             b++;
11691             dprintf (3, ("Allocation Clearing bricks [%Ix, %Ix[",
11692                          b, brick_of (align_on_brick (start + limit_size))));
11693             volatile short* x = &brick_table [b];
11694             short* end_x = &brick_table [brick_of (align_on_brick (start + limit_size))];
11695
11696             for (;x < end_x;x++)
11697                 *x = -1;
11698         }
11699         else
11700 #endif //FFIND_OBJECT
11701         {
11702             gen0_bricks_cleared = FALSE;
11703         }
11704     }
11705
11706     // verifying the memory is completely cleared.
11707     //verify_mem_cleared (start - plug_skew, limit_size);
11708 }
11709
11710 size_t gc_heap::new_allocation_limit (size_t size, size_t physical_limit, int gen_number)
11711 {
11712     dynamic_data* dd = dynamic_data_of (gen_number);
11713     ptrdiff_t new_alloc = dd_new_allocation (dd);
11714     assert (new_alloc == (ptrdiff_t)Align (new_alloc,
11715                                            get_alignment_constant (!(gen_number == (max_generation+1)))));
11716
11717     ptrdiff_t logical_limit = max (new_alloc, (ptrdiff_t)size);
11718     size_t limit = min (logical_limit, (ptrdiff_t)physical_limit);
11719     assert (limit == Align (limit, get_alignment_constant (!(gen_number == (max_generation+1)))));
11720     dd_new_allocation (dd) = (new_alloc - limit);
11721     return limit;
11722 }
11723
11724 size_t gc_heap::limit_from_size (size_t size, size_t physical_limit, int gen_number,
11725                                  int align_const)
11726 {
11727     size_t padded_size = size + Align (min_obj_size, align_const);
11728     // for LOH this is not true...we could select a physical_limit that's exactly the same
11729     // as size.
11730     assert ((gen_number != 0) || (physical_limit >= padded_size));
11731     size_t min_size_to_allocate = ((gen_number == 0) ? allocation_quantum : 0);
11732
11733     // For SOH if the size asked for is very small, we want to allocate more than 
11734     // just what's asked for if possible.
11735     size_t desired_size_to_allocate  = max (padded_size, min_size_to_allocate);
11736     size_t new_physical_limit = min (physical_limit, desired_size_to_allocate);
11737
11738     size_t new_limit = new_allocation_limit (padded_size,
11739                                              new_physical_limit,
11740                                              gen_number);
11741     assert (new_limit >= (size + Align (min_obj_size, align_const)));
11742     dprintf (100, ("requested to allocate %Id bytes, actual size is %Id", size, new_limit));
11743     return new_limit;
11744 }
11745
11746 void gc_heap::handle_oom (int heap_num, oom_reason reason, size_t alloc_size, 
11747                           uint8_t* allocated, uint8_t* reserved)
11748 {
11749     UNREFERENCED_PARAMETER(heap_num);
11750
11751     if (reason == oom_budget)
11752     {
11753         alloc_size = dd_min_size (dynamic_data_of (0)) / 2;
11754     }
11755
11756     if ((reason == oom_budget) && ((!fgm_result.loh_p) && (fgm_result.fgm != fgm_no_failure)))
11757     {
11758         // This means during the last GC we needed to reserve and/or commit more memory
11759         // but we couldn't. We proceeded with the GC and ended up not having enough
11760         // memory at the end. This is a legitimate OOM situtation. Otherwise we 
11761         // probably made a mistake and didn't expand the heap when we should have.
11762         reason = oom_low_mem;
11763     }
11764
11765     oom_info.reason = reason;
11766     oom_info.allocated = allocated;
11767     oom_info.reserved = reserved;
11768     oom_info.alloc_size = alloc_size;
11769     oom_info.gc_index = settings.gc_index;
11770     oom_info.fgm = fgm_result.fgm;
11771     oom_info.size = fgm_result.size;
11772     oom_info.available_pagefile_mb = fgm_result.available_pagefile_mb;
11773     oom_info.loh_p = fgm_result.loh_p;
11774
11775     fgm_result.fgm = fgm_no_failure;
11776
11777     // Break early - before the more_space_lock is release so no other threads
11778     // could have allocated on the same heap when OOM happened.
11779     if (GCConfig::GetBreakOnOOM())
11780     {
11781         GCToOSInterface::DebugBreak();
11782     }
11783 }
11784
11785 #ifdef BACKGROUND_GC
11786 BOOL gc_heap::background_allowed_p()
11787 {
11788     return ( gc_can_use_concurrent && ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)) );
11789 }
11790 #endif //BACKGROUND_GC
11791
11792 void gc_heap::check_for_full_gc (int gen_num, size_t size)
11793 {
11794     BOOL should_notify = FALSE;
11795     // if we detect full gc because of the allocation budget specified this is TRUE;
11796     // it's FALSE if it's due to other factors.
11797     BOOL alloc_factor = TRUE; 
11798     int i = 0;
11799     int n = 0;
11800     int n_initial = gen_num;
11801     BOOL local_blocking_collection = FALSE;
11802     BOOL local_elevation_requested = FALSE;
11803     int new_alloc_remain_percent = 0;
11804
11805     if (full_gc_approach_event_set)
11806     {
11807         return;
11808     }
11809     
11810     if (gen_num != (max_generation + 1))
11811     {
11812         gen_num = max_generation;
11813     }
11814
11815     dynamic_data* dd_full = dynamic_data_of (gen_num);
11816     ptrdiff_t new_alloc_remain = 0;
11817     uint32_t pct = ((gen_num == (max_generation + 1)) ? fgn_loh_percent : fgn_maxgen_percent);
11818
11819     for (int gen_index = 0; gen_index <= (max_generation + 1); gen_index++)
11820     {
11821         dprintf (2, ("FGN: h#%d: gen%d: %Id(%Id)", 
11822                      heap_number, gen_index,
11823                      dd_new_allocation (dynamic_data_of (gen_index)),
11824                      dd_desired_allocation (dynamic_data_of (gen_index))));
11825     }
11826
11827     // For small object allocations we only check every fgn_check_quantum bytes.
11828     if (n_initial == 0)
11829     {
11830         dprintf (2, ("FGN: gen0 last recorded alloc: %Id", fgn_last_alloc));
11831         dynamic_data* dd_0 = dynamic_data_of (n_initial);
11832         if (((fgn_last_alloc - dd_new_allocation (dd_0)) < fgn_check_quantum) &&
11833             (dd_new_allocation (dd_0) >= 0))
11834         {
11835             return;
11836         }
11837         else
11838         {
11839             fgn_last_alloc = dd_new_allocation (dd_0);
11840             dprintf (2, ("FGN: gen0 last recorded alloc is now: %Id", fgn_last_alloc));
11841         }
11842
11843         // We don't consider the size that came from soh 'cause it doesn't contribute to the
11844         // gen2 budget.
11845         size = 0;
11846     }
11847
11848     for (i = n+1; i <= max_generation; i++)
11849     {
11850         if (get_new_allocation (i) <= 0)
11851         {
11852             n = min (i, max_generation);
11853         }
11854         else
11855             break;
11856     }
11857
11858     dprintf (2, ("FGN: h#%d: gen%d budget exceeded", heap_number, n));
11859     if (gen_num == max_generation)
11860     {
11861         // If it's small object heap we should first see if we will even be looking at gen2 budget
11862         // in the next GC or not. If not we should go directly to checking other factors.
11863         if (n < (max_generation - 1))
11864         {
11865             goto check_other_factors;
11866         }
11867     }
11868
11869     new_alloc_remain = dd_new_allocation (dd_full) - size;
11870
11871     new_alloc_remain_percent = (int)(((float)(new_alloc_remain) / (float)dd_desired_allocation (dd_full)) * 100);
11872
11873     dprintf (2, ("FGN: alloc threshold for gen%d is %d%%, current threshold is %d%%", 
11874                  gen_num, pct, new_alloc_remain_percent));
11875
11876     if (new_alloc_remain_percent <= (int)pct)
11877     {
11878 #ifdef BACKGROUND_GC
11879         // If background GC is enabled, we still want to check whether this will
11880         // be a blocking GC or not because we only want to notify when it's a 
11881         // blocking full GC.
11882         if (background_allowed_p())
11883         {
11884             goto check_other_factors;
11885         }
11886 #endif //BACKGROUND_GC
11887
11888         should_notify = TRUE;
11889         goto done;
11890     }
11891
11892 check_other_factors:
11893
11894     dprintf (2, ("FGC: checking other factors"));
11895     n = generation_to_condemn (n, 
11896                                &local_blocking_collection, 
11897                                &local_elevation_requested, 
11898                                TRUE);
11899
11900     if (local_elevation_requested && (n == max_generation))
11901     {
11902         if (settings.should_lock_elevation)
11903         {
11904             int local_elevation_locked_count = settings.elevation_locked_count + 1;
11905             if (local_elevation_locked_count != 6)
11906             {
11907                 dprintf (2, ("FGN: lock count is %d - Condemning max_generation-1", 
11908                     local_elevation_locked_count));
11909                 n = max_generation - 1;
11910             }
11911         }
11912     }
11913
11914     dprintf (2, ("FGN: we estimate gen%d will be collected", n));
11915
11916 #ifdef BACKGROUND_GC
11917     // When background GC is enabled it decreases the accuracy of our predictability -
11918     // by the time the GC happens, we may not be under BGC anymore. If we try to 
11919     // predict often enough it should be ok.
11920     if ((n == max_generation) &&
11921         (recursive_gc_sync::background_running_p()))
11922     {
11923         n = max_generation - 1;
11924         dprintf (2, ("FGN: bgc - 1 instead of 2"));
11925     }
11926
11927     if ((n == max_generation) && !local_blocking_collection)
11928     {
11929         if (!background_allowed_p())
11930         {
11931             local_blocking_collection = TRUE;
11932         }
11933     }
11934 #endif //BACKGROUND_GC
11935
11936     dprintf (2, ("FGN: we estimate gen%d will be collected: %s", 
11937                        n, 
11938                        (local_blocking_collection ? "blocking" : "background")));
11939
11940     if ((n == max_generation) && local_blocking_collection)
11941     {
11942         alloc_factor = FALSE;
11943         should_notify = TRUE;
11944         goto done;
11945     }
11946
11947 done:
11948
11949     if (should_notify)
11950     {
11951         dprintf (2, ("FGN: gen%d detecting full GC approaching(%s) (GC#%d) (%Id%% left in gen%d)", 
11952                      n_initial,
11953                      (alloc_factor ? "alloc" : "other"),
11954                      dd_collection_count (dynamic_data_of (0)),
11955                      new_alloc_remain_percent, 
11956                      gen_num));
11957
11958         send_full_gc_notification (n_initial, alloc_factor);
11959     }
11960 }
11961
11962 void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p)
11963 {
11964     if (!full_gc_approach_event_set)
11965     {
11966         assert (full_gc_approach_event.IsValid());
11967         FIRE_EVENT(GCFullNotify_V1, gen_num, due_to_alloc_p);
11968
11969         full_gc_end_event.Reset();
11970         full_gc_approach_event.Set();
11971         full_gc_approach_event_set = true;
11972     }
11973 }
11974
11975 wait_full_gc_status gc_heap::full_gc_wait (GCEvent *event, int time_out_ms)
11976 {
11977     if (fgn_maxgen_percent == 0)
11978     {
11979         return wait_full_gc_na;
11980     }
11981
11982     uint32_t wait_result = user_thread_wait(event, FALSE, time_out_ms);
11983
11984     if ((wait_result == WAIT_OBJECT_0) || (wait_result == WAIT_TIMEOUT))
11985     {
11986         if (fgn_maxgen_percent == 0)
11987         {
11988             return wait_full_gc_cancelled;
11989         }
11990         
11991         if (wait_result == WAIT_OBJECT_0)
11992         {
11993 #ifdef BACKGROUND_GC
11994             if (fgn_last_gc_was_concurrent)
11995             {
11996                 fgn_last_gc_was_concurrent = FALSE;
11997                 return wait_full_gc_na;
11998             }
11999             else
12000 #endif //BACKGROUND_GC
12001             {
12002                 return wait_full_gc_success;
12003             }
12004         }
12005         else
12006         {
12007             return wait_full_gc_timeout;
12008         }
12009     }
12010     else
12011     {
12012         return wait_full_gc_failed;
12013     }
12014 }
12015
12016 size_t gc_heap::get_full_compact_gc_count()
12017 {
12018     return full_gc_counts[gc_type_compacting];
12019 }
12020
12021 // DTREVIEW - we should check this in dt_low_ephemeral_space_p
12022 // as well.
12023 inline
12024 BOOL gc_heap::short_on_end_of_seg (int gen_number,
12025                                    heap_segment* seg,
12026                                    int align_const)
12027 {
12028     UNREFERENCED_PARAMETER(gen_number);
12029     uint8_t* allocated = heap_segment_allocated(seg);
12030
12031     BOOL sufficient_p = sufficient_space_end_seg (allocated, 
12032                                                   heap_segment_reserved (seg), 
12033                                                   end_space_after_gc(),
12034                                                   tuning_deciding_short_on_seg);
12035     if (!sufficient_p)
12036     {
12037         if (sufficient_gen0_space_p)
12038         {
12039             dprintf (GTC_LOG, ("gen0 has enough free space"));
12040         }
12041
12042         sufficient_p = sufficient_gen0_space_p;
12043     }
12044
12045     return !sufficient_p;
12046 }
12047
12048 #ifdef _MSC_VER
12049 #pragma warning(disable:4706) // "assignment within conditional expression" is intentional in this function.
12050 #endif // _MSC_VER
12051
12052 inline
12053 BOOL gc_heap::a_fit_free_list_p (int gen_number, 
12054                                  size_t size, 
12055                                  alloc_context* acontext,
12056                                  int align_const)
12057 {
12058     BOOL can_fit = FALSE;
12059     generation* gen = generation_of (gen_number);
12060     allocator* gen_allocator = generation_allocator (gen);
12061     size_t sz_list = gen_allocator->first_bucket_size();
12062     for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
12063     {
12064         if ((size < sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
12065         {
12066             uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
12067             uint8_t* prev_free_item = 0;
12068
12069             while (free_list != 0)
12070             {
12071                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
12072                 size_t free_list_size = unused_array_size (free_list);
12073                 if ((size + Align (min_obj_size, align_const)) <= free_list_size)
12074                 {
12075                     dprintf (3, ("Found adequate unused area: [%Ix, size: %Id",
12076                                  (size_t)free_list, free_list_size));
12077
12078                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
12079                     // We ask for more Align (min_obj_size)
12080                     // to make sure that we can insert a free object
12081                     // in adjust_limit will set the limit lower
12082                     size_t limit = limit_from_size (size, free_list_size, gen_number, align_const);
12083
12084                     uint8_t*  remain = (free_list + limit);
12085                     size_t remain_size = (free_list_size - limit);
12086                     if (remain_size >= Align(min_free_list, align_const))
12087                     {
12088                         make_unused_array (remain, remain_size);
12089                         gen_allocator->thread_item_front (remain, remain_size);
12090                         assert (remain_size >= Align (min_obj_size, align_const));
12091                     }
12092                     else
12093                     {
12094                         //absorb the entire free list
12095                         limit += remain_size;
12096                     }
12097                     generation_free_list_space (gen) -= limit;
12098
12099                     adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
12100
12101                     can_fit = TRUE;
12102                     goto end;
12103                 }
12104                 else if (gen_allocator->discard_if_no_fit_p())
12105                 {
12106                     assert (prev_free_item == 0);
12107                     dprintf (3, ("couldn't use this free area, discarding"));
12108                     generation_free_obj_space (gen) += free_list_size;
12109
12110                     gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
12111                     generation_free_list_space (gen) -= free_list_size;
12112                 }
12113                 else
12114                 {
12115                     prev_free_item = free_list;
12116                 }
12117                 free_list = free_list_slot (free_list); 
12118             }
12119         }
12120         sz_list = sz_list * 2;
12121     }
12122 end:
12123     return can_fit;
12124 }
12125
12126
12127 #ifdef BACKGROUND_GC
12128 void gc_heap::bgc_loh_alloc_clr (uint8_t* alloc_start,
12129                                  size_t size, 
12130                                  alloc_context* acontext,
12131                                  int align_const, 
12132                                  int lock_index,
12133                                  BOOL check_used_p,
12134                                  heap_segment* seg)
12135 {
12136     make_unused_array (alloc_start, size);
12137
12138 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
12139     if (g_fEnableAppDomainMonitoring)
12140     {
12141         GCToEEInterface::RecordAllocatedBytesForHeap(size, heap_number);
12142     }
12143 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
12144
12145     size_t size_of_array_base = sizeof(ArrayBase);
12146
12147     bgc_alloc_lock->loh_alloc_done_with_index (lock_index);
12148
12149     // clear memory while not holding the lock. 
12150     size_t size_to_skip = size_of_array_base;
12151     size_t size_to_clear = size - size_to_skip - plug_skew;
12152     size_t saved_size_to_clear = size_to_clear;
12153     if (check_used_p)
12154     {
12155         uint8_t* end = alloc_start + size - plug_skew;
12156         uint8_t* used = heap_segment_used (seg);
12157         if (used < end)
12158         {
12159             if ((alloc_start + size_to_skip) < used)
12160             {
12161                 size_to_clear = used - (alloc_start + size_to_skip);
12162             }
12163             else
12164             {
12165                 size_to_clear = 0;
12166             }
12167             dprintf (2, ("bgc loh: setting used to %Ix", end));
12168             heap_segment_used (seg) = end;
12169         }
12170
12171         dprintf (2, ("bgc loh: used: %Ix, alloc: %Ix, end of alloc: %Ix, clear %Id bytes",
12172                      used, alloc_start, end, size_to_clear));
12173     }
12174     else
12175     {
12176         dprintf (2, ("bgc loh: [%Ix-[%Ix(%Id)", alloc_start, alloc_start+size, size));
12177     }
12178
12179 #ifdef VERIFY_HEAP
12180     // since we filled in 0xcc for free object when we verify heap,
12181     // we need to make sure we clear those bytes.
12182     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
12183     {
12184         if (size_to_clear < saved_size_to_clear)
12185         {
12186             size_to_clear = saved_size_to_clear;
12187         }
12188     }
12189 #endif //VERIFY_HEAP
12190     
12191     dprintf (SPINLOCK_LOG, ("[%d]Lmsl to clear large obj", heap_number));
12192     add_saved_spinlock_info (true, me_release, mt_clr_large_mem);
12193     leave_spin_lock (&more_space_lock_loh);
12194     memclr (alloc_start + size_to_skip, size_to_clear);
12195
12196     bgc_alloc_lock->loh_alloc_set (alloc_start);
12197
12198     acontext->alloc_ptr = alloc_start;
12199     acontext->alloc_limit = (alloc_start + size - Align (min_obj_size, align_const));
12200
12201     // need to clear the rest of the object before we hand it out.
12202     clear_unused_array(alloc_start, size);
12203 }
12204 #endif //BACKGROUND_GC
12205
12206 BOOL gc_heap::a_fit_free_list_large_p (size_t size, 
12207                                        alloc_context* acontext,
12208                                        int align_const)
12209 {
12210     BOOL can_fit = FALSE;
12211     int gen_number = max_generation + 1;
12212     generation* gen = generation_of (gen_number);
12213     allocator* loh_allocator = generation_allocator (gen); 
12214
12215 #ifdef FEATURE_LOH_COMPACTION
12216     size_t loh_pad = Align (loh_padding_obj_size, align_const);
12217 #endif //FEATURE_LOH_COMPACTION
12218
12219 #ifdef BACKGROUND_GC
12220     int cookie = -1;
12221 #endif //BACKGROUND_GC
12222     size_t sz_list = loh_allocator->first_bucket_size();
12223     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
12224     {
12225         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
12226         {
12227             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
12228             uint8_t* prev_free_item = 0;
12229             while (free_list != 0)
12230             {
12231                 dprintf (3, ("considering free list %Ix", (size_t)free_list));
12232
12233                 size_t free_list_size = unused_array_size(free_list);
12234
12235 #ifdef FEATURE_LOH_COMPACTION
12236                 if ((size + loh_pad) <= free_list_size)
12237 #else
12238                 if (((size + Align (min_obj_size, align_const)) <= free_list_size)||
12239                     (size == free_list_size))
12240 #endif //FEATURE_LOH_COMPACTION
12241                 {
12242 #ifdef BACKGROUND_GC
12243                     cookie = bgc_alloc_lock->loh_alloc_set (free_list);
12244                     bgc_track_loh_alloc();
12245 #endif //BACKGROUND_GC
12246
12247                     //unlink the free_item
12248                     loh_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
12249
12250                     // Substract min obj size because limit_from_size adds it. Not needed for LOH
12251                     size_t limit = limit_from_size (size - Align(min_obj_size, align_const), free_list_size, 
12252                                                     gen_number, align_const);
12253
12254 #ifdef FEATURE_LOH_COMPACTION
12255                     make_unused_array (free_list, loh_pad);
12256                     limit -= loh_pad;
12257                     free_list += loh_pad;
12258                     free_list_size -= loh_pad;
12259 #endif //FEATURE_LOH_COMPACTION
12260
12261                     uint8_t*  remain = (free_list + limit);
12262                     size_t remain_size = (free_list_size - limit);
12263                     if (remain_size != 0)
12264                     {
12265                         assert (remain_size >= Align (min_obj_size, align_const));
12266                         make_unused_array (remain, remain_size);
12267                     }
12268                     if (remain_size >= Align(min_free_list, align_const))
12269                     {
12270                         loh_thread_gap_front (remain, remain_size, gen);
12271                         assert (remain_size >= Align (min_obj_size, align_const));
12272                     }
12273                     else
12274                     {
12275                         generation_free_obj_space (gen) += remain_size;
12276                     }
12277                     generation_free_list_space (gen) -= free_list_size;
12278                     dprintf (3, ("found fit on loh at %Ix", free_list));
12279 #ifdef BACKGROUND_GC
12280                     if (cookie != -1)
12281                     {
12282                         bgc_loh_alloc_clr (free_list, limit, acontext, align_const, cookie, FALSE, 0);
12283                     }
12284                     else
12285 #endif //BACKGROUND_GC
12286                     {
12287                         adjust_limit_clr (free_list, limit, acontext, 0, align_const, gen_number);
12288                     }
12289
12290                     //fix the limit to compensate for adjust_limit_clr making it too short 
12291                     acontext->alloc_limit += Align (min_obj_size, align_const);
12292                     can_fit = TRUE;
12293                     goto exit;
12294                 }
12295                 prev_free_item = free_list;
12296                 free_list = free_list_slot (free_list); 
12297             }
12298         }
12299         sz_list = sz_list * 2;
12300     }
12301 exit:
12302     return can_fit;
12303 }
12304
12305 #ifdef _MSC_VER
12306 #pragma warning(default:4706)
12307 #endif // _MSC_VER
12308
12309 BOOL gc_heap::a_fit_segment_end_p (int gen_number,
12310                                    heap_segment* seg,
12311                                    size_t size, 
12312                                    alloc_context* acontext,
12313                                    int align_const,
12314                                    BOOL* commit_failed_p)
12315 {
12316     *commit_failed_p = FALSE;
12317     size_t limit = 0;
12318     bool hard_limit_short_seg_end_p = false;
12319 #ifdef BACKGROUND_GC
12320     int cookie = -1;
12321 #endif //BACKGROUND_GC
12322
12323     uint8_t*& allocated = ((gen_number == 0) ?
12324                         alloc_allocated : 
12325                         heap_segment_allocated(seg));
12326
12327     size_t pad = Align (min_obj_size, align_const);
12328
12329 #ifdef FEATURE_LOH_COMPACTION
12330     size_t loh_pad = Align (loh_padding_obj_size, align_const);
12331     if (gen_number == (max_generation + 1))
12332     {
12333         pad += loh_pad;
12334     }
12335 #endif //FEATURE_LOH_COMPACTION
12336
12337     uint8_t* end = heap_segment_committed (seg) - pad;
12338
12339     if (a_size_fit_p (size, allocated, end, align_const))
12340     {
12341         limit = limit_from_size (size, 
12342                                  (end - allocated), 
12343                                  gen_number, align_const);
12344         goto found_fit;
12345     }
12346
12347     end = heap_segment_reserved (seg) - pad;
12348
12349     if (a_size_fit_p (size, allocated, end, align_const))
12350     {
12351         limit = limit_from_size (size, 
12352                                  (end - allocated), 
12353                                  gen_number, align_const);
12354
12355         if (grow_heap_segment (seg, (allocated + limit), &hard_limit_short_seg_end_p))
12356         {
12357             goto found_fit;
12358         }
12359         else
12360         {
12361             if (!hard_limit_short_seg_end_p)
12362             {
12363                 dprintf (2, ("can't grow segment, doing a full gc"));
12364                 *commit_failed_p = TRUE;
12365             }
12366             else
12367             {
12368                 assert (heap_hard_limit);
12369             }
12370         }
12371     }
12372
12373     goto found_no_fit;
12374
12375 found_fit:
12376
12377 #ifdef BACKGROUND_GC
12378     if (gen_number != 0)
12379     {
12380         cookie = bgc_alloc_lock->loh_alloc_set (allocated);
12381         bgc_track_loh_alloc();
12382     }
12383 #endif //BACKGROUND_GC
12384
12385     uint8_t* old_alloc;
12386     old_alloc = allocated;
12387 #ifdef FEATURE_LOH_COMPACTION
12388     if (gen_number == (max_generation + 1))
12389     {
12390         make_unused_array (old_alloc, loh_pad);
12391         old_alloc += loh_pad;
12392         allocated += loh_pad;
12393         limit -= loh_pad;
12394     }
12395 #endif //FEATURE_LOH_COMPACTION
12396
12397 #if defined (VERIFY_HEAP) && defined (_DEBUG)
12398         ((void**) allocated)[-1] = 0;     //clear the sync block
12399 #endif //VERIFY_HEAP && _DEBUG
12400     allocated += limit;
12401
12402     dprintf (3, ("found fit at end of seg: %Ix", old_alloc));
12403
12404 #ifdef BACKGROUND_GC
12405     if (cookie != -1)
12406     {
12407         bgc_loh_alloc_clr (old_alloc, limit, acontext, align_const, cookie, TRUE, seg);
12408     }
12409     else
12410 #endif //BACKGROUND_GC
12411     {
12412         adjust_limit_clr (old_alloc, limit, acontext, seg, align_const, gen_number);
12413     }
12414
12415     return TRUE;
12416
12417 found_no_fit:
12418
12419     return FALSE;
12420 }
12421
12422 BOOL gc_heap::loh_a_fit_segment_end_p (int gen_number,
12423                                        size_t size, 
12424                                        alloc_context* acontext,
12425                                        int align_const,
12426                                        BOOL* commit_failed_p,
12427                                        oom_reason* oom_r)
12428 {
12429     *commit_failed_p = FALSE;
12430     heap_segment* seg = generation_allocation_segment (generation_of (gen_number));
12431     BOOL can_allocate_p = FALSE;
12432
12433     while (seg)
12434     {
12435 #ifdef BACKGROUND_GC
12436         if (seg->flags & heap_segment_flags_loh_delete)
12437         {
12438             dprintf (3, ("h%d skipping seg %Ix to be deleted", heap_number, (size_t)seg));
12439         }
12440         else
12441 #endif //BACKGROUND_GC
12442         {
12443             if (a_fit_segment_end_p (gen_number, seg, (size - Align (min_obj_size, align_const)), 
12444                                         acontext, align_const, commit_failed_p))
12445             {
12446                 acontext->alloc_limit += Align (min_obj_size, align_const);
12447                 can_allocate_p = TRUE;
12448                 break;
12449             }
12450
12451             if (*commit_failed_p)
12452             {
12453                 *oom_r = oom_cant_commit;
12454                 break;
12455             }
12456         }
12457
12458         seg = heap_segment_next_rw (seg);
12459     }
12460
12461     return can_allocate_p;
12462 }
12463
12464 #ifdef BACKGROUND_GC
12465 inline
12466 void gc_heap::wait_for_background (alloc_wait_reason awr, bool loh_p)
12467 {
12468     GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
12469
12470     dprintf (2, ("BGC is already in progress, waiting for it to finish"));
12471     add_saved_spinlock_info (loh_p, me_release, mt_wait_bgc);
12472     leave_spin_lock (msl);
12473     background_gc_wait (awr);
12474     enter_spin_lock (msl);
12475     add_saved_spinlock_info (loh_p, me_acquire, mt_wait_bgc);
12476 }
12477
12478 void gc_heap::wait_for_bgc_high_memory (alloc_wait_reason awr, bool loh_p)
12479 {
12480     if (recursive_gc_sync::background_running_p())
12481     {
12482         uint32_t memory_load;
12483         get_memory_info (&memory_load);
12484         if (memory_load >= m_high_memory_load_th)
12485         {
12486             dprintf (GTC_LOG, ("high mem - wait for BGC to finish, wait reason: %d", awr));
12487             wait_for_background (awr, loh_p);
12488         }
12489     }
12490 }
12491
12492 #endif //BACKGROUND_GC
12493
12494 // We request to trigger an ephemeral GC but we may get a full compacting GC.
12495 // return TRUE if that's the case.
12496 BOOL gc_heap::trigger_ephemeral_gc (gc_reason gr)
12497 {
12498 #ifdef BACKGROUND_GC
12499     wait_for_bgc_high_memory (awr_loh_oos_bgc, false);
12500 #endif //BACKGROUND_GC
12501
12502     BOOL did_full_compact_gc = FALSE;
12503
12504     dprintf (2, ("triggering a gen1 GC"));
12505     size_t last_full_compact_gc_count = get_full_compact_gc_count();
12506     vm_heap->GarbageCollectGeneration(max_generation - 1, gr);
12507
12508 #ifdef MULTIPLE_HEAPS
12509     enter_spin_lock (&more_space_lock_soh);
12510     add_saved_spinlock_info (false, me_acquire, mt_t_eph_gc);
12511 #endif //MULTIPLE_HEAPS
12512
12513     size_t current_full_compact_gc_count = get_full_compact_gc_count();
12514
12515     if (current_full_compact_gc_count > last_full_compact_gc_count)
12516     {
12517         dprintf (2, ("attempted to trigger an ephemeral GC and got a full compacting GC"));
12518         did_full_compact_gc = TRUE;
12519     }
12520
12521     return did_full_compact_gc;
12522 }
12523
12524 BOOL gc_heap::soh_try_fit (int gen_number,
12525                            size_t size, 
12526                            alloc_context* acontext,
12527                            int align_const,
12528                            BOOL* commit_failed_p,
12529                            BOOL* short_seg_end_p)
12530 {
12531     BOOL can_allocate = TRUE;
12532     if (short_seg_end_p)
12533     {
12534         *short_seg_end_p = FALSE;
12535     }
12536
12537     can_allocate = a_fit_free_list_p (gen_number, size, acontext, align_const);
12538     if (!can_allocate)
12539     {
12540         if (short_seg_end_p)
12541         {
12542             *short_seg_end_p = short_on_end_of_seg (gen_number, ephemeral_heap_segment, align_const);
12543         }
12544         // If the caller doesn't care, we always try to fit at the end of seg;
12545         // otherwise we would only try if we are actually not short at end of seg.
12546         if (!short_seg_end_p || !(*short_seg_end_p))
12547         {
12548             can_allocate = a_fit_segment_end_p (gen_number, ephemeral_heap_segment, size, 
12549                                                 acontext, align_const, commit_failed_p);
12550         }
12551     }
12552
12553     return can_allocate;
12554 }
12555
12556 allocation_state gc_heap::allocate_small (int gen_number,
12557                                           size_t size, 
12558                                           alloc_context* acontext,
12559                                           int align_const)
12560 {
12561 #if defined (BACKGROUND_GC) && !defined (MULTIPLE_HEAPS)
12562     if (recursive_gc_sync::background_running_p())
12563     {
12564         background_soh_alloc_count++;
12565         if ((background_soh_alloc_count % bgc_alloc_spin_count) == 0)
12566         {
12567             add_saved_spinlock_info (false, me_release, mt_alloc_small);
12568             leave_spin_lock (&more_space_lock_soh);
12569             bool cooperative_mode = enable_preemptive();
12570             GCToOSInterface::Sleep (bgc_alloc_spin);
12571             disable_preemptive (cooperative_mode);
12572             enter_spin_lock (&more_space_lock_soh);
12573             add_saved_spinlock_info (false, me_acquire, mt_alloc_small);
12574         }
12575         else
12576         {
12577             //GCToOSInterface::YieldThread (0);
12578         }
12579     }
12580 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
12581
12582     gc_reason gr = reason_oos_soh;
12583     oom_reason oom_r = oom_no_failure;
12584
12585     // No variable values should be "carried over" from one state to the other. 
12586     // That's why there are local variable for each state
12587
12588     allocation_state soh_alloc_state = a_state_start;
12589
12590     // If we can get a new seg it means allocation will succeed.
12591     while (1)
12592     {
12593         dprintf (3, ("[h%d]soh state is %s", heap_number, allocation_state_str[soh_alloc_state]));
12594
12595         switch (soh_alloc_state)
12596         {
12597             case a_state_can_allocate:
12598             case a_state_cant_allocate:
12599             {
12600                 goto exit;
12601             }
12602             case a_state_start:
12603             {
12604                 soh_alloc_state = a_state_try_fit;
12605                 break;
12606             }
12607             case a_state_try_fit:
12608             {
12609                 BOOL commit_failed_p = FALSE;
12610                 BOOL can_use_existing_p = FALSE;
12611
12612                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12613                                                   align_const, &commit_failed_p,
12614                                                   NULL);
12615                 soh_alloc_state = (can_use_existing_p ?
12616                                         a_state_can_allocate : 
12617                                         (commit_failed_p ? 
12618                                             a_state_trigger_full_compact_gc :
12619                                             a_state_trigger_ephemeral_gc));
12620                 break;
12621             }
12622             case a_state_try_fit_after_bgc:
12623             {
12624                 BOOL commit_failed_p = FALSE;
12625                 BOOL can_use_existing_p = FALSE;
12626                 BOOL short_seg_end_p = FALSE;
12627
12628                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12629                                                   align_const, &commit_failed_p,
12630                                                   &short_seg_end_p);
12631                 soh_alloc_state = (can_use_existing_p ? 
12632                                         a_state_can_allocate : 
12633                                         (short_seg_end_p ? 
12634                                             a_state_trigger_2nd_ephemeral_gc : 
12635                                             a_state_trigger_full_compact_gc));
12636                 break;
12637             }
12638             case a_state_try_fit_after_cg:
12639             {
12640                 BOOL commit_failed_p = FALSE;
12641                 BOOL can_use_existing_p = FALSE;
12642                 BOOL short_seg_end_p = FALSE;
12643
12644                 can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12645                                                   align_const, &commit_failed_p,
12646                                                   &short_seg_end_p);
12647
12648                 if (can_use_existing_p)
12649                 {
12650                     soh_alloc_state = a_state_can_allocate;
12651                 }
12652 #ifdef MULTIPLE_HEAPS
12653                 else if (gen0_allocated_after_gc_p)
12654                 {
12655                     // some other threads already grabbed the more space lock and allocated
12656                     // so we should attempt an ephemeral GC again.
12657                     soh_alloc_state = a_state_trigger_ephemeral_gc; 
12658                 }
12659 #endif //MULTIPLE_HEAPS
12660                 else if (short_seg_end_p)
12661                 {
12662                     soh_alloc_state = a_state_cant_allocate;
12663                     oom_r = oom_budget;
12664                 }
12665                 else 
12666                 {
12667                     assert (commit_failed_p);
12668                     soh_alloc_state = a_state_cant_allocate;
12669                     oom_r = oom_cant_commit;
12670                 }
12671                 break;
12672             }
12673             case a_state_check_and_wait_for_bgc:
12674             {
12675                 BOOL bgc_in_progress_p = FALSE;
12676                 BOOL did_full_compacting_gc = FALSE;
12677
12678                 bgc_in_progress_p = check_and_wait_for_bgc (awr_gen0_oos_bgc, &did_full_compacting_gc, false);
12679                 soh_alloc_state = (did_full_compacting_gc ? 
12680                                         a_state_try_fit_after_cg : 
12681                                         a_state_try_fit_after_bgc);
12682                 break;
12683             }
12684             case a_state_trigger_ephemeral_gc:
12685             {
12686                 BOOL commit_failed_p = FALSE;
12687                 BOOL can_use_existing_p = FALSE;
12688                 BOOL short_seg_end_p = FALSE;
12689                 BOOL bgc_in_progress_p = FALSE;
12690                 BOOL did_full_compacting_gc = FALSE;
12691
12692                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12693                 if (did_full_compacting_gc)
12694                 {
12695                     soh_alloc_state = a_state_try_fit_after_cg;
12696                 }
12697                 else
12698                 {
12699                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12700                                                       align_const, &commit_failed_p,
12701                                                       &short_seg_end_p);
12702 #ifdef BACKGROUND_GC
12703                     bgc_in_progress_p = recursive_gc_sync::background_running_p();
12704 #endif //BACKGROUND_GC
12705
12706                     if (can_use_existing_p)
12707                     {
12708                         soh_alloc_state = a_state_can_allocate;
12709                     }
12710                     else
12711                     {
12712                         if (short_seg_end_p)
12713                         {
12714                             if (should_expand_in_full_gc)
12715                             {
12716                                 dprintf (2, ("gen1 GC wanted to expand!"));
12717                                 soh_alloc_state = a_state_trigger_full_compact_gc;
12718                             }
12719                             else
12720                             {
12721                                 soh_alloc_state = (bgc_in_progress_p ? 
12722                                                         a_state_check_and_wait_for_bgc : 
12723                                                         a_state_trigger_full_compact_gc);
12724                             }
12725                         }
12726                         else if (commit_failed_p)
12727                         {
12728                             soh_alloc_state = a_state_trigger_full_compact_gc;
12729                         }
12730                         else
12731                         {
12732 #ifdef MULTIPLE_HEAPS
12733                             // some other threads already grabbed the more space lock and allocated
12734                             // so we should attempt an ephemeral GC again.
12735                             assert (gen0_allocated_after_gc_p);
12736                             soh_alloc_state = a_state_trigger_ephemeral_gc; 
12737 #else //MULTIPLE_HEAPS
12738                             assert (!"shouldn't get here");
12739 #endif //MULTIPLE_HEAPS
12740                         }
12741                     }
12742                 }
12743                 break;
12744             }
12745             case a_state_trigger_2nd_ephemeral_gc:
12746             {
12747                 BOOL commit_failed_p = FALSE;
12748                 BOOL can_use_existing_p = FALSE;
12749                 BOOL short_seg_end_p = FALSE;
12750                 BOOL did_full_compacting_gc = FALSE;
12751
12752
12753                 did_full_compacting_gc = trigger_ephemeral_gc (gr);
12754                 
12755                 if (did_full_compacting_gc)
12756                 {
12757                     soh_alloc_state = a_state_try_fit_after_cg;
12758                 }
12759                 else
12760                 {
12761                     can_use_existing_p = soh_try_fit (gen_number, size, acontext,
12762                                                       align_const, &commit_failed_p,
12763                                                       &short_seg_end_p);
12764                     if (short_seg_end_p || commit_failed_p)
12765                     {
12766                         soh_alloc_state = a_state_trigger_full_compact_gc;
12767                     }
12768                     else
12769                     {
12770                         assert (can_use_existing_p);
12771                         soh_alloc_state = a_state_can_allocate;
12772                     }
12773                 }
12774                 break;
12775             }
12776             case a_state_trigger_full_compact_gc:
12777             {
12778                 if (fgn_maxgen_percent)
12779                 {
12780                     dprintf (2, ("FGN: SOH doing last GC before we throw OOM"));
12781                     send_full_gc_notification (max_generation, FALSE);
12782                 }
12783
12784                 BOOL got_full_compacting_gc = FALSE;
12785
12786                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r, false);
12787                 soh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
12788                 break;
12789             }
12790             default:
12791             {
12792                 assert (!"Invalid state!");
12793                 break;
12794             }
12795         }
12796     }
12797
12798 exit:
12799     if (soh_alloc_state == a_state_cant_allocate)
12800     {
12801         assert (oom_r != oom_no_failure);
12802         handle_oom (heap_number, 
12803                     oom_r, 
12804                     size,
12805                     heap_segment_allocated (ephemeral_heap_segment),
12806                     heap_segment_reserved (ephemeral_heap_segment));
12807
12808         add_saved_spinlock_info (false, me_release, mt_alloc_small_cant);
12809         leave_spin_lock (&more_space_lock_soh);
12810     }
12811
12812     assert ((soh_alloc_state == a_state_can_allocate) ||
12813             (soh_alloc_state == a_state_cant_allocate) ||
12814             (soh_alloc_state == a_state_retry_allocate));
12815
12816     return soh_alloc_state;
12817 }
12818
12819 #ifdef BACKGROUND_GC
12820 inline
12821 void gc_heap::bgc_track_loh_alloc()
12822 {
12823     if (current_c_gc_state == c_gc_state_planning)
12824     {
12825         Interlocked::Increment (&loh_alloc_thread_count);
12826         dprintf (3, ("h%d: inc lc: %d", heap_number, (int32_t)loh_alloc_thread_count));
12827     }
12828 }
12829
12830 inline
12831 void gc_heap::bgc_untrack_loh_alloc()
12832 {
12833     if (current_c_gc_state == c_gc_state_planning)
12834     {
12835         Interlocked::Decrement (&loh_alloc_thread_count);
12836         dprintf (3, ("h%d: dec lc: %d", heap_number, (int32_t)loh_alloc_thread_count));
12837     }
12838 }
12839
12840 BOOL gc_heap::bgc_loh_should_allocate()
12841 {
12842     size_t min_gc_size = dd_min_size (dynamic_data_of (max_generation + 1));
12843
12844     if ((bgc_begin_loh_size + bgc_loh_size_increased) < (min_gc_size * 10))
12845     {
12846         return TRUE;
12847     }
12848
12849     if (((bgc_begin_loh_size / end_loh_size) >= 2) || (bgc_loh_size_increased >= bgc_begin_loh_size))
12850     {
12851         if ((bgc_begin_loh_size / end_loh_size) > 2)
12852         {
12853             dprintf (3, ("alloc-ed too much before bgc started"));
12854         }
12855         else
12856         {
12857             dprintf (3, ("alloc-ed too much after bgc started"));
12858         }
12859         return FALSE;
12860     }
12861     else
12862     {
12863         bgc_alloc_spin_loh = (uint32_t)(((float)bgc_loh_size_increased / (float)bgc_begin_loh_size) * 10);
12864         return TRUE;
12865     }
12866 }
12867 #endif //BACKGROUND_GC
12868
12869 size_t gc_heap::get_large_seg_size (size_t size)
12870 {
12871     size_t default_seg_size = min_loh_segment_size;
12872 #ifdef SEG_MAPPING_TABLE
12873     size_t align_size =  default_seg_size;
12874 #else //SEG_MAPPING_TABLE
12875     size_t align_size =  default_seg_size / 2;
12876 #endif //SEG_MAPPING_TABLE
12877     int align_const = get_alignment_constant (FALSE);
12878     size_t large_seg_size = align_on_page (
12879         max (default_seg_size,
12880             ((size + 2 * Align(min_obj_size, align_const) + OS_PAGE_SIZE +
12881             align_size) / align_size * align_size)));
12882     return large_seg_size;
12883 }
12884
12885 BOOL gc_heap::loh_get_new_seg (generation* gen,
12886                                size_t size,
12887                                int align_const,
12888                                BOOL* did_full_compact_gc,
12889                                oom_reason* oom_r)
12890 {
12891     UNREFERENCED_PARAMETER(gen);
12892     UNREFERENCED_PARAMETER(align_const);
12893
12894     *did_full_compact_gc = FALSE;
12895
12896     size_t seg_size = get_large_seg_size (size);
12897
12898     heap_segment* new_seg = get_large_segment (seg_size, did_full_compact_gc);
12899
12900     if (new_seg)
12901     {
12902         loh_alloc_since_cg += seg_size;
12903     }
12904     else
12905     {
12906         *oom_r = oom_loh;
12907     }
12908
12909     return (new_seg != 0);
12910 }
12911
12912 // PERF TODO: this is too aggressive; and in hard limit we should
12913 // count the actual allocated bytes instead of only updating it during
12914 // getting a new seg.
12915 BOOL gc_heap::retry_full_compact_gc (size_t size)
12916 {
12917     size_t seg_size = get_large_seg_size (size);
12918
12919     if (loh_alloc_since_cg >= (2 * (uint64_t)seg_size))
12920     {
12921         return TRUE;
12922     }
12923
12924 #ifdef MULTIPLE_HEAPS
12925     uint64_t total_alloc_size = 0;
12926     for (int i = 0; i < n_heaps; i++)
12927     {
12928         total_alloc_size += g_heaps[i]->loh_alloc_since_cg;
12929     }
12930
12931     if (total_alloc_size >= (2 * (uint64_t)seg_size))
12932     {
12933         return TRUE;
12934     }
12935 #endif //MULTIPLE_HEAPS
12936
12937     return FALSE;
12938 }
12939
12940 BOOL gc_heap::check_and_wait_for_bgc (alloc_wait_reason awr,
12941                                       BOOL* did_full_compact_gc,
12942                                       bool loh_p)
12943 {
12944     BOOL bgc_in_progress = FALSE;
12945     *did_full_compact_gc = FALSE;
12946 #ifdef BACKGROUND_GC
12947     if (recursive_gc_sync::background_running_p())
12948     {
12949         bgc_in_progress = TRUE;
12950         size_t last_full_compact_gc_count = get_full_compact_gc_count();
12951         wait_for_background (awr, loh_p);
12952         size_t current_full_compact_gc_count = get_full_compact_gc_count();
12953         if (current_full_compact_gc_count > last_full_compact_gc_count)
12954         {
12955             *did_full_compact_gc = TRUE;
12956         }
12957     }
12958 #endif //BACKGROUND_GC
12959
12960     return bgc_in_progress;
12961 }
12962
12963 BOOL gc_heap::loh_try_fit (int gen_number,
12964                            size_t size, 
12965                            alloc_context* acontext,
12966                            int align_const,
12967                            BOOL* commit_failed_p,
12968                            oom_reason* oom_r)
12969 {
12970     BOOL can_allocate = TRUE;
12971
12972     if (!a_fit_free_list_large_p (size, acontext, align_const))
12973     {
12974         can_allocate = loh_a_fit_segment_end_p (gen_number, size, 
12975                                                 acontext, align_const, 
12976                                                 commit_failed_p, oom_r);
12977
12978 #ifdef BACKGROUND_GC
12979         if (can_allocate && recursive_gc_sync::background_running_p())
12980         {
12981             bgc_loh_size_increased += size;
12982         }
12983 #endif //BACKGROUND_GC
12984     }
12985 #ifdef BACKGROUND_GC
12986     else
12987     {
12988         if (recursive_gc_sync::background_running_p())
12989         {
12990             bgc_loh_allocated_in_free += size;
12991         }
12992     }
12993 #endif //BACKGROUND_GC
12994
12995     return can_allocate;
12996 }
12997
12998 BOOL gc_heap::trigger_full_compact_gc (gc_reason gr, 
12999                                        oom_reason* oom_r,
13000                                        bool loh_p)
13001 {
13002     BOOL did_full_compact_gc = FALSE;
13003
13004     size_t last_full_compact_gc_count = get_full_compact_gc_count();
13005
13006     // Set this so the next GC will be a full compacting GC.
13007     if (!last_gc_before_oom)
13008     {
13009         last_gc_before_oom = TRUE;
13010     }
13011
13012 #ifdef BACKGROUND_GC
13013     if (recursive_gc_sync::background_running_p())
13014     {
13015         wait_for_background (((gr == reason_oos_soh) ? awr_gen0_oos_bgc : awr_loh_oos_bgc), loh_p);
13016         dprintf (2, ("waited for BGC - done"));
13017     }
13018 #endif //BACKGROUND_GC
13019
13020     GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
13021     size_t current_full_compact_gc_count = get_full_compact_gc_count();
13022     if (current_full_compact_gc_count > last_full_compact_gc_count)
13023     {
13024         dprintf (3, ("a full compacting GC triggered while waiting for BGC (%d->%d)", last_full_compact_gc_count, current_full_compact_gc_count));
13025         assert (current_full_compact_gc_count > last_full_compact_gc_count);
13026         did_full_compact_gc = TRUE;
13027         goto exit;
13028     }
13029
13030     dprintf (3, ("h%d full GC", heap_number));
13031
13032     trigger_gc_for_alloc (max_generation, gr, msl, loh_p, mt_t_full_gc);
13033
13034     current_full_compact_gc_count = get_full_compact_gc_count();
13035
13036     if (current_full_compact_gc_count == last_full_compact_gc_count)
13037     {
13038         dprintf (2, ("attempted to trigger a full compacting GC but didn't get it"));
13039         // We requested a full GC but didn't get because of the elevation logic
13040         // which means we should fail.
13041         *oom_r = oom_unproductive_full_gc;
13042     }
13043     else
13044     {
13045         dprintf (3, ("h%d: T full compacting GC (%d->%d)", 
13046             heap_number, 
13047             last_full_compact_gc_count, 
13048             current_full_compact_gc_count));
13049
13050         assert (current_full_compact_gc_count > last_full_compact_gc_count);
13051         did_full_compact_gc = TRUE;
13052     }
13053
13054 exit:
13055     return did_full_compact_gc;
13056 }
13057
13058 #ifdef RECORD_LOH_STATE
13059 void gc_heap::add_saved_loh_state (allocation_state loh_state_to_save, EEThreadId thread_id)
13060 {
13061     // When the state is can_allocate we already have released the more
13062     // space lock. So we are not logging states here since this code
13063     // is not thread safe.
13064     if (loh_state_to_save != a_state_can_allocate)
13065     {
13066         last_loh_states[loh_state_index].alloc_state = loh_state_to_save;
13067         last_loh_states[loh_state_index].thread_id = thread_id;
13068         loh_state_index++;
13069
13070         if (loh_state_index == max_saved_loh_states)
13071         {
13072             loh_state_index = 0;
13073         }
13074
13075         assert (loh_state_index < max_saved_loh_states);
13076     }
13077 }
13078 #endif //RECORD_LOH_STATE
13079
13080 bool gc_heap::should_retry_other_heap (size_t size)
13081 {
13082 #ifdef MULTIPLE_HEAPS
13083     if (heap_hard_limit)
13084     {
13085         size_t total_heap_committed_recorded = 
13086             current_total_committed - current_total_committed_bookkeeping;
13087         size_t min_size = dd_min_size (g_heaps[0]->dynamic_data_of (max_generation + 1));
13088         size_t slack_space = max (commit_min_th, min_size);
13089         bool retry_p = ((total_heap_committed_recorded + size) < (heap_hard_limit - slack_space));
13090         dprintf (1, ("%Id - %Id - total committed %Id - size %Id = %Id, %s",
13091             heap_hard_limit, slack_space, total_heap_committed_recorded, size,
13092             (heap_hard_limit - slack_space - total_heap_committed_recorded - size),
13093             (retry_p ? "retry" : "no retry")));
13094         return retry_p;
13095     }
13096     else
13097 #endif //MULTIPLE_HEAPS
13098     {
13099         return false;
13100     }
13101 }
13102
13103 allocation_state gc_heap::allocate_large (int gen_number,
13104                                           size_t size, 
13105                                           alloc_context* acontext,
13106                                           int align_const)
13107 {
13108 #ifdef BACKGROUND_GC
13109     if (recursive_gc_sync::background_running_p())
13110     {
13111         background_loh_alloc_count++;
13112         //if ((background_loh_alloc_count % bgc_alloc_spin_count_loh) == 0)
13113         {
13114             if (bgc_loh_should_allocate())
13115             {
13116                 if (!bgc_alloc_spin_loh)
13117                 {
13118                     add_saved_spinlock_info (true, me_release, mt_alloc_large);
13119                     leave_spin_lock (&more_space_lock_loh);
13120                     bool cooperative_mode = enable_preemptive();
13121                     GCToOSInterface::YieldThread (bgc_alloc_spin_loh);
13122                     disable_preemptive (cooperative_mode);
13123                     enter_spin_lock (&more_space_lock_loh);
13124                     add_saved_spinlock_info (true, me_acquire, mt_alloc_large);
13125                     dprintf (SPINLOCK_LOG, ("[%d]spin Emsl loh", heap_number));
13126                 }
13127             }
13128             else
13129             {
13130                 wait_for_background (awr_loh_alloc_during_bgc, true);
13131             }
13132         }
13133     }
13134 #endif //BACKGROUND_GC
13135
13136     gc_reason gr = reason_oos_loh;
13137     generation* gen = generation_of (gen_number);
13138     oom_reason oom_r = oom_no_failure;
13139     size_t current_full_compact_gc_count = 0;
13140
13141     // No variable values should be "carried over" from one state to the other. 
13142     // That's why there are local variable for each state
13143     allocation_state loh_alloc_state = a_state_start;
13144 #ifdef RECORD_LOH_STATE
13145     EEThreadId current_thread_id;
13146     current_thread_id.SetToCurrentThread();
13147 #endif //RECORD_LOH_STATE
13148
13149     // If we can get a new seg it means allocation will succeed.
13150     while (1)
13151     {
13152         dprintf (3, ("[h%d]loh state is %s", heap_number, allocation_state_str[loh_alloc_state]));
13153
13154 #ifdef RECORD_LOH_STATE
13155         add_saved_loh_state (loh_alloc_state, current_thread_id);
13156 #endif //RECORD_LOH_STATE
13157         switch (loh_alloc_state)
13158         {
13159             case a_state_can_allocate:
13160             case a_state_cant_allocate:
13161             {
13162                 goto exit;
13163             }
13164             case a_state_start:
13165             {
13166                 loh_alloc_state = a_state_try_fit;
13167                 break;
13168             }
13169             case a_state_try_fit:
13170             {
13171                 BOOL commit_failed_p = FALSE;
13172                 BOOL can_use_existing_p = FALSE;
13173
13174                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
13175                                                   align_const, &commit_failed_p, &oom_r);
13176                 loh_alloc_state = (can_use_existing_p ?
13177                                         a_state_can_allocate : 
13178                                         (commit_failed_p ? 
13179                                             a_state_trigger_full_compact_gc :
13180                                             a_state_acquire_seg));
13181                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13182                 break;
13183             }
13184             case a_state_try_fit_new_seg:
13185             {
13186                 BOOL commit_failed_p = FALSE;
13187                 BOOL can_use_existing_p = FALSE;
13188
13189                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
13190                                                   align_const, &commit_failed_p, &oom_r);
13191                 // Even after we got a new seg it doesn't necessarily mean we can allocate,
13192                 // another LOH allocating thread could have beat us to acquire the msl so 
13193                 // we need to try again.
13194                 loh_alloc_state = (can_use_existing_p ? a_state_can_allocate : a_state_try_fit);
13195                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13196                 break;
13197             }
13198             case a_state_try_fit_after_cg:
13199             {
13200                 BOOL commit_failed_p = FALSE;
13201                 BOOL can_use_existing_p = FALSE;
13202
13203                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
13204                                                   align_const, &commit_failed_p, &oom_r);
13205                 // If we failed to commit, we bail right away 'cause we already did a 
13206                 // full compacting GC.
13207                 loh_alloc_state = (can_use_existing_p ?
13208                                         a_state_can_allocate : 
13209                                         (commit_failed_p ? 
13210                                             a_state_cant_allocate :
13211                                             a_state_acquire_seg_after_cg));
13212                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13213                 break;
13214             }
13215             case a_state_try_fit_after_bgc:
13216             {
13217                 BOOL commit_failed_p = FALSE;
13218                 BOOL can_use_existing_p = FALSE;
13219
13220                 can_use_existing_p = loh_try_fit (gen_number, size, acontext, 
13221                                                   align_const, &commit_failed_p, &oom_r);
13222                 loh_alloc_state = (can_use_existing_p ?
13223                                         a_state_can_allocate : 
13224                                         (commit_failed_p ? 
13225                                             a_state_trigger_full_compact_gc :
13226                                             a_state_acquire_seg_after_bgc));
13227                 assert ((loh_alloc_state == a_state_can_allocate) == (acontext->alloc_ptr != 0));
13228                 break;
13229             }
13230             case a_state_acquire_seg:
13231             {
13232                 BOOL can_get_new_seg_p = FALSE;
13233                 BOOL did_full_compacting_gc = FALSE;
13234
13235                 current_full_compact_gc_count = get_full_compact_gc_count();
13236
13237                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
13238                 loh_alloc_state = (can_get_new_seg_p ? 
13239                                         a_state_try_fit_new_seg : 
13240                                         (did_full_compacting_gc ? 
13241                                             a_state_check_retry_seg :
13242                                             a_state_check_and_wait_for_bgc));
13243                 break;
13244             }
13245             case a_state_acquire_seg_after_cg:
13246             {
13247                 BOOL can_get_new_seg_p = FALSE;
13248                 BOOL did_full_compacting_gc = FALSE;
13249
13250                 current_full_compact_gc_count = get_full_compact_gc_count();
13251
13252                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r);
13253                 // Since we release the msl before we try to allocate a seg, other
13254                 // threads could have allocated a bunch of segments before us so
13255                 // we might need to retry.
13256                 loh_alloc_state = (can_get_new_seg_p ? 
13257                                         a_state_try_fit_after_cg : 
13258                                         a_state_check_retry_seg);
13259                 break;
13260             }
13261             case a_state_acquire_seg_after_bgc:
13262             {
13263                 BOOL can_get_new_seg_p = FALSE;
13264                 BOOL did_full_compacting_gc = FALSE;
13265              
13266                 current_full_compact_gc_count = get_full_compact_gc_count();
13267
13268                 can_get_new_seg_p = loh_get_new_seg (gen, size, align_const, &did_full_compacting_gc, &oom_r); 
13269                 loh_alloc_state = (can_get_new_seg_p ? 
13270                                         a_state_try_fit_new_seg : 
13271                                         (did_full_compacting_gc ? 
13272                                             a_state_check_retry_seg :
13273                                             a_state_trigger_full_compact_gc));
13274                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13275                 break;
13276             }
13277             case a_state_check_and_wait_for_bgc:
13278             {
13279                 BOOL bgc_in_progress_p = FALSE;
13280                 BOOL did_full_compacting_gc = FALSE;
13281
13282                 bgc_in_progress_p = check_and_wait_for_bgc (awr_loh_oos_bgc, &did_full_compacting_gc, true);
13283                 loh_alloc_state = (!bgc_in_progress_p ?
13284                                         a_state_trigger_full_compact_gc : 
13285                                         (did_full_compacting_gc ? 
13286                                             a_state_try_fit_after_cg :
13287                                             a_state_try_fit_after_bgc));
13288                 break;
13289             }
13290             case a_state_trigger_full_compact_gc:
13291             {
13292                 if (fgn_maxgen_percent)
13293                 {
13294                     dprintf (2, ("FGN: LOH doing last GC before we throw OOM"));
13295                     send_full_gc_notification (max_generation, FALSE);
13296                 }
13297
13298                 BOOL got_full_compacting_gc = FALSE;
13299
13300                 got_full_compacting_gc = trigger_full_compact_gc (gr, &oom_r, true);
13301                 loh_alloc_state = (got_full_compacting_gc ? a_state_try_fit_after_cg : a_state_cant_allocate);
13302                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13303                 break;
13304             }
13305             case a_state_check_retry_seg:
13306             {
13307                 BOOL should_retry_gc = retry_full_compact_gc (size);
13308                 BOOL should_retry_get_seg = FALSE;
13309                 if (!should_retry_gc)
13310                 {
13311                     size_t last_full_compact_gc_count = current_full_compact_gc_count;
13312                     current_full_compact_gc_count = get_full_compact_gc_count();
13313                     if (current_full_compact_gc_count > last_full_compact_gc_count)
13314                     {
13315                         should_retry_get_seg = TRUE;
13316                     }
13317                 }
13318     
13319                 loh_alloc_state = (should_retry_gc ? 
13320                                         a_state_trigger_full_compact_gc : 
13321                                         (should_retry_get_seg ?
13322                                             a_state_try_fit_after_cg :
13323                                             a_state_cant_allocate));
13324                 assert ((loh_alloc_state != a_state_cant_allocate) || (oom_r != oom_no_failure));
13325                 break;
13326             }
13327             default:
13328             {
13329                 assert (!"Invalid state!");
13330                 break;
13331             }
13332         }
13333     }
13334
13335 exit:
13336     if (loh_alloc_state == a_state_cant_allocate)
13337     {
13338         assert (oom_r != oom_no_failure);
13339         if (should_retry_other_heap (size))
13340         {
13341             loh_alloc_state = a_state_retry_allocate;
13342         }
13343         else
13344         {
13345             handle_oom (heap_number, 
13346                         oom_r, 
13347                         size,
13348                         0,
13349                         0);
13350         }
13351         add_saved_spinlock_info (true, me_release, mt_alloc_large_cant);
13352         leave_spin_lock (&more_space_lock_loh);
13353     }
13354
13355     assert ((loh_alloc_state == a_state_can_allocate) ||
13356             (loh_alloc_state == a_state_cant_allocate) ||
13357             (loh_alloc_state == a_state_retry_allocate));
13358     return loh_alloc_state;
13359 }
13360
13361 // BGC's final mark phase will acquire the msl, so release it here and re-acquire.
13362 void gc_heap::trigger_gc_for_alloc (int gen_number, gc_reason gr, 
13363                                     GCSpinLock* msl, bool loh_p, 
13364                                     msl_take_state take_state)
13365 {
13366 #ifdef BACKGROUND_GC
13367     if (loh_p)
13368     {
13369         add_saved_spinlock_info (loh_p, me_release, take_state);
13370         leave_spin_lock (msl);
13371     }
13372 #endif //BACKGROUND_GC
13373
13374     vm_heap->GarbageCollectGeneration (gen_number, gr);
13375
13376 #ifdef MULTIPLE_HEAPS
13377     if (!loh_p)
13378     {
13379         enter_spin_lock (msl);
13380         add_saved_spinlock_info (loh_p, me_acquire, take_state);
13381     }
13382 #endif //MULTIPLE_HEAPS
13383
13384 #ifdef BACKGROUND_GC
13385     if (loh_p)
13386     {
13387         enter_spin_lock (msl);
13388         add_saved_spinlock_info (loh_p, me_acquire, take_state);
13389     }
13390 #endif //BACKGROUND_GC
13391 }
13392
13393 allocation_state gc_heap::try_allocate_more_space (alloc_context* acontext, size_t size,
13394                                    int gen_number)
13395 {
13396     if (gc_heap::gc_started)
13397     {
13398         wait_for_gc_done();
13399         return a_state_retry_allocate;
13400     }
13401
13402     bool loh_p = (gen_number > 0);
13403     GCSpinLock* msl = loh_p ? &more_space_lock_loh : &more_space_lock_soh;
13404
13405 #ifdef SYNCHRONIZATION_STATS
13406     int64_t msl_acquire_start = GCToOSInterface::QueryPerformanceCounter();
13407 #endif //SYNCHRONIZATION_STATS
13408     enter_spin_lock (msl);
13409     add_saved_spinlock_info (loh_p, me_acquire, mt_try_alloc);
13410     dprintf (SPINLOCK_LOG, ("[%d]Emsl for alloc", heap_number));
13411 #ifdef SYNCHRONIZATION_STATS
13412     int64_t msl_acquire = GCToOSInterface::QueryPerformanceCounter() - msl_acquire_start;
13413     total_msl_acquire += msl_acquire;
13414     num_msl_acquired++;
13415     if (msl_acquire > 200)
13416     {
13417         num_high_msl_acquire++;
13418     }
13419     else
13420     {
13421         num_low_msl_acquire++;
13422     }
13423 #endif //SYNCHRONIZATION_STATS
13424
13425     /*
13426     // We are commenting this out 'cause we don't see the point - we already
13427     // have checked gc_started when we were acquiring the msl - no need to check
13428     // again. This complicates the logic in bgc_suspend_EE 'cause that one would
13429     // need to release msl which causes all sorts of trouble.
13430     if (gc_heap::gc_started)
13431     {
13432 #ifdef SYNCHRONIZATION_STATS
13433         good_suspension++;
13434 #endif //SYNCHRONIZATION_STATS
13435         BOOL fStress = (g_pConfig->GetGCStressLevel() & GCConfig::GCSTRESS_TRANSITION) != 0;
13436         if (!fStress)
13437         {
13438             //Rendez vous early (MP scaling issue)
13439             //dprintf (1, ("[%d]waiting for gc", heap_number));
13440             wait_for_gc_done();
13441 #ifdef MULTIPLE_HEAPS
13442             return -1;
13443 #endif //MULTIPLE_HEAPS
13444         }
13445     }
13446     */
13447
13448     dprintf (3, ("requested to allocate %d bytes on gen%d", size, gen_number));
13449
13450     int align_const = get_alignment_constant (gen_number != (max_generation+1));
13451
13452     if (fgn_maxgen_percent)
13453     {
13454         check_for_full_gc (gen_number, size);
13455     }
13456
13457     if (!(new_allocation_allowed (gen_number)))
13458     {
13459         if (fgn_maxgen_percent && (gen_number == 0))
13460         {
13461             // We only check gen0 every so often, so take this opportunity to check again.
13462             check_for_full_gc (gen_number, size);
13463         }
13464
13465 #ifdef BACKGROUND_GC
13466         wait_for_bgc_high_memory (awr_gen0_alloc, loh_p);
13467 #endif //BACKGROUND_GC
13468
13469 #ifdef SYNCHRONIZATION_STATS
13470         bad_suspension++;
13471 #endif //SYNCHRONIZATION_STATS
13472         dprintf (/*100*/ 2, ("running out of budget on gen%d, gc", gen_number));
13473
13474         if (!settings.concurrent || (gen_number == 0))
13475         {
13476             trigger_gc_for_alloc (0, ((gen_number == 0) ? reason_alloc_soh : reason_alloc_loh),
13477                                   msl, loh_p, mt_try_budget);
13478         }
13479     }
13480
13481     allocation_state can_allocate = ((gen_number == 0) ?
13482         allocate_small (gen_number, size, acontext, align_const) :
13483         allocate_large (gen_number, size, acontext, align_const));
13484    
13485     if (can_allocate == a_state_can_allocate)
13486     {
13487         size_t alloc_context_bytes = acontext->alloc_limit + Align (min_obj_size, align_const) - acontext->alloc_ptr;
13488         int etw_allocation_index = ((gen_number == 0) ? 0 : 1);
13489
13490         etw_allocation_running_amount[etw_allocation_index] += alloc_context_bytes;
13491
13492         allocated_since_last_gc += alloc_context_bytes;
13493
13494         if (etw_allocation_running_amount[etw_allocation_index] > etw_allocation_tick)
13495         {
13496 #ifdef FEATURE_REDHAWK
13497             FIRE_EVENT(GCAllocationTick_V1, (uint32_t)etw_allocation_running_amount[etw_allocation_index],
13498                                             (gen_number == 0) ? gc_etw_alloc_soh : gc_etw_alloc_loh);
13499 #else
13500             // Unfortunately some of the ETW macros do not check whether the ETW feature is enabled.
13501             // The ones that do are much less efficient.
13502 #if defined(FEATURE_EVENT_TRACE)
13503             if (EVENT_ENABLED(GCAllocationTick_V3))
13504             {
13505                 fire_etw_allocation_event (etw_allocation_running_amount[etw_allocation_index], gen_number, acontext->alloc_ptr);
13506             }
13507 #endif //FEATURE_EVENT_TRACE
13508 #endif //FEATURE_REDHAWK
13509             etw_allocation_running_amount[etw_allocation_index] = 0;
13510         }
13511     }
13512
13513     return can_allocate;
13514 }
13515
13516 #ifdef MULTIPLE_HEAPS
13517 void gc_heap::balance_heaps (alloc_context* acontext)
13518 {
13519     if (acontext->alloc_count < 4)
13520     {
13521         if (acontext->alloc_count == 0)
13522         {
13523             acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, 0) ));
13524             gc_heap* hp = acontext->get_home_heap()->pGenGCHeap;
13525             dprintf (3, ("First allocation for context %Ix on heap %d\n", (size_t)acontext, (size_t)hp->heap_number));
13526             acontext->set_alloc_heap(acontext->get_home_heap());
13527             hp->alloc_context_count++;
13528         }
13529     }
13530     else
13531     {
13532         BOOL set_home_heap = FALSE;
13533         int hint = 0;
13534
13535         if (heap_select::can_find_heap_fast())
13536         {
13537             if (acontext->get_home_heap() != NULL)
13538                 hint = acontext->get_home_heap()->pGenGCHeap->heap_number;
13539             if (acontext->get_home_heap() != GCHeap::GetHeap(hint = heap_select::select_heap(acontext, hint)) || ((acontext->alloc_count & 15) == 0))
13540             {
13541                 set_home_heap = TRUE;
13542             }
13543         }
13544         else
13545         {
13546             // can't use gdt
13547             if ((acontext->alloc_count & 3) == 0)
13548                 set_home_heap = TRUE;
13549         }
13550
13551         if (set_home_heap)
13552         {
13553 /*
13554             // Since we are balancing up to MAX_SUPPORTED_CPUS, no need for this.
13555             if (n_heaps > MAX_SUPPORTED_CPUS)
13556             {
13557                 // on machines with many processors cache affinity is really king, so don't even try
13558                 // to balance on these.
13559                 acontext->home_heap = GCHeap::GetHeap( heap_select::select_heap(acontext, hint) );
13560                 acontext->alloc_heap = acontext->home_heap;
13561             }
13562             else
13563 */
13564             {
13565                 gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13566
13567                 dynamic_data* dd = org_hp->dynamic_data_of (0);
13568                 ptrdiff_t org_size = dd_new_allocation (dd);
13569                 int org_alloc_context_count;
13570                 int max_alloc_context_count;
13571                 gc_heap* max_hp;
13572                 ptrdiff_t max_size;
13573                 size_t delta = dd_min_size (dd)/4;
13574
13575                 int start, end, finish;
13576                 heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13577                 finish = start + n_heaps;
13578
13579 try_again:
13580                 do
13581                 {
13582                     max_hp = org_hp;
13583                     max_size = org_size + delta;
13584                     acontext->set_home_heap(GCHeap::GetHeap( heap_select::select_heap(acontext, hint) ));
13585
13586                     if (org_hp == acontext->get_home_heap()->pGenGCHeap)
13587                         max_size = max_size + delta;
13588
13589                     org_alloc_context_count = org_hp->alloc_context_count;
13590                     max_alloc_context_count = org_alloc_context_count;
13591                     if (max_alloc_context_count > 1)
13592                         max_size /= max_alloc_context_count;
13593
13594                     for (int i = start; i < end; i++)
13595                     {
13596                         gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13597                         dd = hp->dynamic_data_of (0);
13598                         ptrdiff_t size = dd_new_allocation (dd);
13599                         if (hp == acontext->get_home_heap()->pGenGCHeap)
13600                             size = size + delta;
13601                         int hp_alloc_context_count = hp->alloc_context_count;
13602                         if (hp_alloc_context_count > 0)
13603                             size /= (hp_alloc_context_count + 1);
13604                         if (size > max_size)
13605                         {
13606                             max_hp = hp;
13607                             max_size = size;
13608                             max_alloc_context_count = hp_alloc_context_count;
13609                         }
13610                     }
13611                 }
13612                 while (org_alloc_context_count != org_hp->alloc_context_count ||
13613                        max_alloc_context_count != max_hp->alloc_context_count);
13614
13615                 if ((max_hp == org_hp) && (end < finish))
13616                 {   
13617                     start = end; end = finish; 
13618                     delta = dd_min_size(dd)/2; // Make it twice as hard to balance to remote nodes on NUMA.
13619                     goto try_again;
13620                 }
13621
13622                 if (max_hp != org_hp)
13623                 {
13624                     org_hp->alloc_context_count--;
13625                     max_hp->alloc_context_count++;
13626                     acontext->set_alloc_heap(GCHeap::GetHeap(max_hp->heap_number));
13627                     if (!gc_thread_no_affinitize_p)
13628                     {
13629                         if (GCToOSInterface::CanEnableGCCPUGroups())
13630                         {   //only set ideal processor when max_hp and org_hp are in the same cpu
13631                             //group. DO NOT MOVE THREADS ACROSS CPU GROUPS
13632                             uint16_t org_gn = heap_select::find_cpu_group_from_heap_no(org_hp->heap_number);
13633                             uint16_t max_gn = heap_select::find_cpu_group_from_heap_no(max_hp->heap_number);
13634                             if (org_gn == max_gn) //only set within CPU group, so SetThreadIdealProcessor is enough
13635                             {   
13636                                 uint16_t group_proc_no = heap_select::find_group_proc_from_heap_no(max_hp->heap_number);
13637
13638                                 GCThreadAffinity affinity;
13639                                 affinity.Processor = group_proc_no;
13640                                 affinity.Group = org_gn;
13641                                 if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13642                                 {
13643                                     dprintf (3, ("Failed to set the ideal processor and group for heap %d.",
13644                                                 org_hp->heap_number));
13645                                 }
13646                             }
13647                         }
13648                         else 
13649                         {
13650                             uint16_t proc_no = heap_select::find_proc_no_from_heap_no(max_hp->heap_number);
13651
13652                             GCThreadAffinity affinity;
13653                             affinity.Processor = proc_no;
13654                             affinity.Group = GCThreadAffinity::None;
13655
13656                             if (!GCToOSInterface::SetCurrentThreadIdealAffinity(&affinity))
13657                             {
13658                                 dprintf (3, ("Failed to set the ideal processor for heap %d.",
13659                                             org_hp->heap_number));
13660                             }
13661                         }
13662                     }
13663                     dprintf (3, ("Switching context %p (home heap %d) ", 
13664                                  acontext,
13665                         acontext->get_home_heap()->pGenGCHeap->heap_number));
13666                     dprintf (3, (" from heap %d (%Id free bytes, %d contexts) ", 
13667                                  org_hp->heap_number,
13668                                  org_size,
13669                                  org_alloc_context_count));
13670                     dprintf (3, (" to heap %d (%Id free bytes, %d contexts)\n", 
13671                                  max_hp->heap_number,
13672                                  dd_new_allocation(max_hp->dynamic_data_of(0)),
13673                                                    max_alloc_context_count));
13674                 }
13675             }
13676         }
13677     }
13678     acontext->alloc_count++;
13679 }
13680
13681 gc_heap* gc_heap::balance_heaps_loh (alloc_context* acontext, size_t alloc_size)
13682 {
13683     gc_heap* org_hp = acontext->get_alloc_heap()->pGenGCHeap;
13684     dprintf (3, ("[h%d] LA: %Id", org_hp->heap_number, alloc_size));
13685
13686     //if (size > 128*1024)
13687     if (1)
13688     {
13689         dynamic_data* dd = org_hp->dynamic_data_of (max_generation + 1);
13690
13691         ptrdiff_t org_size = dd_new_allocation (dd);
13692         gc_heap* max_hp;
13693         ptrdiff_t max_size;
13694         size_t delta = dd_min_size (dd) * 4;
13695
13696         int start, end, finish;
13697         heap_select::get_heap_range_for_heap(org_hp->heap_number, &start, &end);
13698         finish = start + n_heaps;
13699
13700 try_again:
13701         {
13702             max_hp = org_hp;
13703             max_size = org_size + delta;
13704             dprintf (3, ("orig hp: %d, max size: %d",
13705                 org_hp->heap_number,
13706                 max_size));
13707
13708             for (int i = start; i < end; i++)
13709             {
13710                 gc_heap* hp = GCHeap::GetHeap(i%n_heaps)->pGenGCHeap;
13711                 dd = hp->dynamic_data_of (max_generation + 1);
13712                 ptrdiff_t size = dd_new_allocation (dd);
13713                 dprintf (3, ("hp: %d, size: %d",
13714                     hp->heap_number,
13715                     size));
13716                 if (size > max_size)
13717                 {
13718                     max_hp = hp;
13719                     max_size = size;
13720                     dprintf (3, ("max hp: %d, max size: %d",
13721                         max_hp->heap_number,
13722                         max_size));
13723                 }
13724             }
13725         }
13726
13727         if ((max_hp == org_hp) && (end < finish))
13728         {
13729             start = end; end = finish;
13730             delta = dd_min_size(dd) * 4;   // Need to tuning delta
13731             goto try_again;
13732         }
13733
13734         if (max_hp != org_hp)
13735         {
13736             dprintf (3, ("loh: %d(%Id)->%d(%Id)", 
13737                 org_hp->heap_number, dd_new_allocation (org_hp->dynamic_data_of (max_generation + 1)),
13738                 max_hp->heap_number, dd_new_allocation (max_hp->dynamic_data_of (max_generation + 1))));
13739         }
13740
13741         return max_hp;
13742     }
13743     else
13744     {
13745         return org_hp;
13746     }
13747 }
13748 #endif //MULTIPLE_HEAPS
13749
13750 BOOL gc_heap::allocate_more_space(alloc_context* acontext, size_t size,
13751                                   int alloc_generation_number)
13752 {
13753     allocation_state status;
13754     do
13755     { 
13756 #ifdef MULTIPLE_HEAPS
13757         if (alloc_generation_number == 0)
13758         {
13759             balance_heaps (acontext);
13760             status = acontext->get_alloc_heap()->pGenGCHeap->try_allocate_more_space (acontext, size, alloc_generation_number);
13761         }
13762         else
13763         {
13764             gc_heap* alloc_heap = balance_heaps_loh (acontext, size);
13765             status = alloc_heap->try_allocate_more_space (acontext, size, alloc_generation_number);
13766             if (status == a_state_retry_allocate)
13767             {
13768                 dprintf (3, ("LOH h%d alloc retry!", alloc_heap->heap_number));
13769             }
13770         }
13771 #else
13772         status = try_allocate_more_space (acontext, size, alloc_generation_number);
13773 #endif //MULTIPLE_HEAPS
13774     }
13775     while (status == a_state_retry_allocate);
13776     
13777     return (status == a_state_can_allocate);
13778 }
13779
13780 inline
13781 CObjectHeader* gc_heap::allocate (size_t jsize, alloc_context* acontext)
13782 {
13783     size_t size = Align (jsize);
13784     assert (size >= Align (min_obj_size));
13785     {
13786     retry:
13787         uint8_t*  result = acontext->alloc_ptr;
13788         acontext->alloc_ptr+=size;
13789         if (acontext->alloc_ptr <= acontext->alloc_limit)
13790         {
13791             CObjectHeader* obj = (CObjectHeader*)result;
13792             assert (obj != 0);
13793             return obj;
13794         }
13795         else
13796         {
13797             acontext->alloc_ptr -= size;
13798
13799 #ifdef _MSC_VER
13800 #pragma inline_depth(0)
13801 #endif //_MSC_VER
13802
13803             if (! allocate_more_space (acontext, size, 0))
13804                 return 0;
13805
13806 #ifdef _MSC_VER
13807 #pragma inline_depth(20)
13808 #endif //_MSC_VER
13809
13810             goto retry;
13811         }
13812     }
13813 }
13814
13815 void  gc_heap::leave_allocation_segment (generation* gen)
13816 {
13817     adjust_limit (0, 0, gen, max_generation);
13818 }
13819
13820 void gc_heap::init_free_and_plug()
13821 {
13822 #ifdef FREE_USAGE_STATS
13823     for (int i = 0; i <= settings.condemned_generation; i++)
13824     {
13825         generation* gen = generation_of (i);
13826         memset (gen->gen_free_spaces, 0, sizeof (gen->gen_free_spaces));
13827         memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13828         memset (gen->gen_current_pinned_free_spaces, 0, sizeof (gen->gen_current_pinned_free_spaces));
13829     }
13830
13831     if (settings.condemned_generation != max_generation)
13832     {
13833         for (int i = (settings.condemned_generation + 1); i <= max_generation; i++)
13834         {
13835             generation* gen = generation_of (i);
13836             memset (gen->gen_plugs, 0, sizeof (gen->gen_plugs));
13837         }
13838     }
13839 #endif //FREE_USAGE_STATS
13840 }
13841
13842 void gc_heap::print_free_and_plug (const char* msg)
13843 {
13844 #if defined(FREE_USAGE_STATS) && defined(SIMPLE_DPRINTF)
13845     int older_gen = ((settings.condemned_generation == max_generation) ? max_generation : (settings.condemned_generation + 1));
13846     for (int i = 0; i <= older_gen; i++)
13847     {
13848         generation* gen = generation_of (i);
13849         for (int j = 0; j < NUM_GEN_POWER2; j++)
13850         {
13851             if ((gen->gen_free_spaces[j] != 0) || (gen->gen_plugs[j] != 0))
13852             {
13853                 dprintf (2, ("[%s][h%d][%s#%d]gen%d: 2^%d: F: %Id, P: %Id", 
13854                     msg, 
13855                     heap_number, 
13856                     (settings.concurrent ? "BGC" : "GC"),
13857                     settings.gc_index,
13858                     i,
13859                     (j + 9), gen->gen_free_spaces[j], gen->gen_plugs[j]));
13860             }
13861         }
13862     }
13863 #else
13864     UNREFERENCED_PARAMETER(msg);
13865 #endif //FREE_USAGE_STATS && SIMPLE_DPRINTF
13866 }
13867
13868 void gc_heap::add_gen_plug (int gen_number, size_t plug_size)
13869 {
13870 #ifdef FREE_USAGE_STATS
13871     dprintf (3, ("adding plug size %Id to gen%d", plug_size, gen_number));
13872     generation* gen = generation_of (gen_number);
13873     size_t sz = BASE_GEN_SIZE;
13874     int i = 0;
13875
13876     for (; i < NUM_GEN_POWER2; i++)
13877     {
13878         if (plug_size < sz)
13879         {
13880             break;
13881         }
13882         sz = sz * 2;
13883     }
13884     
13885     (gen->gen_plugs[i])++;
13886 #else
13887     UNREFERENCED_PARAMETER(gen_number);
13888     UNREFERENCED_PARAMETER(plug_size);
13889 #endif //FREE_USAGE_STATS
13890 }
13891
13892 void gc_heap::add_item_to_current_pinned_free (int gen_number, size_t free_size)
13893 {
13894 #ifdef FREE_USAGE_STATS
13895     generation* gen = generation_of (gen_number);
13896     size_t sz = BASE_GEN_SIZE;
13897     int i = 0;
13898
13899     for (; i < NUM_GEN_POWER2; i++)
13900     {
13901         if (free_size < sz)
13902         {
13903             break;
13904         }
13905         sz = sz * 2;
13906     }
13907     
13908     (gen->gen_current_pinned_free_spaces[i])++;
13909     generation_pinned_free_obj_space (gen) += free_size;
13910     dprintf (3, ("left pin free %Id(2^%d) to gen%d, total %Id bytes (%Id)", 
13911         free_size, (i + 10), gen_number, 
13912         generation_pinned_free_obj_space (gen),
13913         gen->gen_current_pinned_free_spaces[i]));
13914 #else
13915     UNREFERENCED_PARAMETER(gen_number);
13916     UNREFERENCED_PARAMETER(free_size);
13917 #endif //FREE_USAGE_STATS
13918 }
13919
13920 void gc_heap::add_gen_free (int gen_number, size_t free_size)
13921 {
13922 #ifdef FREE_USAGE_STATS
13923     dprintf (3, ("adding free size %Id to gen%d", free_size, gen_number));
13924     generation* gen = generation_of (gen_number);
13925     size_t sz = BASE_GEN_SIZE;
13926     int i = 0;
13927
13928     for (; i < NUM_GEN_POWER2; i++)
13929     {
13930         if (free_size < sz)
13931         {
13932             break;
13933         }
13934         sz = sz * 2;
13935     }
13936     
13937     (gen->gen_free_spaces[i])++;
13938 #else
13939     UNREFERENCED_PARAMETER(gen_number);
13940     UNREFERENCED_PARAMETER(free_size);
13941 #endif //FREE_USAGE_STATS
13942 }
13943
13944 void gc_heap::remove_gen_free (int gen_number, size_t free_size)
13945 {
13946 #ifdef FREE_USAGE_STATS
13947     dprintf (3, ("removing free %Id from gen%d", free_size, gen_number));
13948     generation* gen = generation_of (gen_number);
13949     size_t sz = BASE_GEN_SIZE;
13950     int i = 0;
13951
13952     for (; i < NUM_GEN_POWER2; i++)
13953     {
13954         if (free_size < sz)
13955         {
13956             break;
13957         }
13958         sz = sz * 2;
13959     }
13960     
13961     (gen->gen_free_spaces[i])--;
13962 #else
13963     UNREFERENCED_PARAMETER(gen_number);
13964     UNREFERENCED_PARAMETER(free_size);
13965 #endif //FREE_USAGE_STATS
13966 }
13967
13968 uint8_t* gc_heap::allocate_in_older_generation (generation* gen, size_t size,
13969                                              int from_gen_number,
13970                                              uint8_t* old_loc REQD_ALIGN_AND_OFFSET_DCL)
13971 {
13972     size = Align (size);
13973     assert (size >= Align (min_obj_size));
13974     assert (from_gen_number < max_generation);
13975     assert (from_gen_number >= 0);
13976     assert (generation_of (from_gen_number + 1) == gen);
13977
13978     allocator* gen_allocator = generation_allocator (gen);
13979     BOOL discard_p = gen_allocator->discard_if_no_fit_p ();
13980     int pad_in_front = ((old_loc != 0) && ((from_gen_number+1) != max_generation)) ? USE_PADDING_FRONT : 0;
13981
13982     size_t real_size = size + Align (min_obj_size);
13983     if (pad_in_front)
13984         real_size += Align (min_obj_size);
13985
13986     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
13987                        generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front)))
13988     {
13989         size_t sz_list = gen_allocator->first_bucket_size();
13990         for (unsigned int a_l_idx = 0; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
13991         {
13992             if ((real_size < (sz_list / 2)) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
13993             {
13994                 uint8_t* free_list = gen_allocator->alloc_list_head_of (a_l_idx);
13995                 uint8_t* prev_free_item = 0;
13996                 while (free_list != 0)
13997                 {
13998                     dprintf (3, ("considering free list %Ix", (size_t)free_list));
13999
14000                     size_t free_list_size = unused_array_size (free_list);
14001
14002                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + free_list_size),
14003                                     old_loc, USE_PADDING_TAIL | pad_in_front))
14004                     {
14005                         dprintf (4, ("F:%Ix-%Id",
14006                                      (size_t)free_list, free_list_size));
14007
14008                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, !discard_p);
14009                         generation_free_list_space (gen) -= free_list_size;
14010                         remove_gen_free (gen->gen_num, free_list_size);
14011
14012                         adjust_limit (free_list, free_list_size, gen, from_gen_number+1);
14013                         generation_allocate_end_seg_p (gen) = FALSE;
14014                         goto finished;
14015                     }
14016                     // We do first fit on bucket 0 because we are not guaranteed to find a fit there.
14017                     else if (discard_p || (a_l_idx == 0))
14018                     {
14019                         dprintf (3, ("couldn't use this free area, discarding"));
14020                         generation_free_obj_space (gen) += free_list_size;
14021
14022                         gen_allocator->unlink_item (a_l_idx, free_list, prev_free_item, FALSE);
14023                         generation_free_list_space (gen) -= free_list_size;
14024                         remove_gen_free (gen->gen_num, free_list_size);
14025                     }
14026                     else
14027                     {
14028                         prev_free_item = free_list;
14029                     }
14030                     free_list = free_list_slot (free_list); 
14031                 }
14032             }
14033             sz_list = sz_list * 2;
14034         }
14035         //go back to the beginning of the segment list 
14036         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
14037         if (seg != generation_allocation_segment (gen))
14038         {
14039             leave_allocation_segment (gen);
14040             generation_allocation_segment (gen) = seg;
14041         }
14042         while (seg != ephemeral_heap_segment)
14043         {
14044             if (size_fit_p(size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
14045                            heap_segment_committed (seg), old_loc, USE_PADDING_TAIL | pad_in_front))
14046             {
14047                 dprintf (3, ("using what's left in committed"));
14048                 adjust_limit (heap_segment_plan_allocated (seg),
14049                               heap_segment_committed (seg) -
14050                               heap_segment_plan_allocated (seg),
14051                               gen, from_gen_number+1);
14052                 generation_allocate_end_seg_p (gen) = TRUE;
14053                 // dformat (t, 3, "Expanding segment allocation");
14054                 heap_segment_plan_allocated (seg) =
14055                     heap_segment_committed (seg);
14056                 goto finished;
14057             }
14058             else
14059             {
14060                 if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, heap_segment_plan_allocated (seg),
14061                                 heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
14062                     grow_heap_segment (seg, heap_segment_plan_allocated (seg), old_loc, size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG))
14063                 {
14064                     dprintf (3, ("using what's left in reserved"));
14065                     adjust_limit (heap_segment_plan_allocated (seg),
14066                                   heap_segment_committed (seg) -
14067                                   heap_segment_plan_allocated (seg),
14068                                   gen, from_gen_number+1);
14069                     generation_allocate_end_seg_p (gen) = TRUE;
14070                     heap_segment_plan_allocated (seg) =
14071                         heap_segment_committed (seg);
14072
14073                     goto finished;
14074                 }
14075                 else
14076                 {
14077                     leave_allocation_segment (gen);
14078                     heap_segment*   next_seg = heap_segment_next_rw (seg);
14079                     if (next_seg)
14080                     {
14081                         dprintf (3, ("getting next segment"));
14082                         generation_allocation_segment (gen) = next_seg;
14083                         generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
14084                         generation_allocation_limit (gen) = generation_allocation_pointer (gen);
14085                     }
14086                     else
14087                     {
14088                         size = 0;
14089                         goto finished;
14090                     }
14091                 }
14092             }
14093             seg = generation_allocation_segment (gen);
14094         }
14095         //No need to fix the last region. Will be done later
14096         size = 0;
14097         goto finished;
14098     }
14099     finished:
14100     if (0 == size)
14101     {
14102         return 0;
14103     }
14104     else
14105     {
14106         uint8_t*  result = generation_allocation_pointer (gen);
14107         size_t pad = 0;
14108
14109 #ifdef SHORT_PLUGS
14110         if ((pad_in_front & USE_PADDING_FRONT) &&
14111             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14112              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14113         {
14114             pad = Align (min_obj_size);
14115             set_plug_padded (old_loc);
14116         }
14117 #endif //SHORT_PLUGS
14118
14119 #ifdef FEATURE_STRUCTALIGN
14120         _ASSERTE(!old_loc || alignmentOffset != 0);
14121         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14122         if (old_loc != 0)
14123         {
14124             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14125             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14126             pad += pad1;
14127         }
14128 #else // FEATURE_STRUCTALIGN
14129         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14130         {
14131             pad += switch_alignment_size (is_plug_padded (old_loc));
14132             set_node_realigned (old_loc);
14133             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14134                          (size_t)old_loc, (size_t)(result+pad)));
14135             assert (same_large_alignment_p (result + pad, old_loc));
14136         }
14137 #endif // FEATURE_STRUCTALIGN
14138         dprintf (3, ("Allocate %Id bytes", size));
14139
14140         if ((old_loc == 0) || (pad != 0))
14141         {
14142             //allocating a non plug or a gap, so reset the start region
14143             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14144         }
14145
14146         generation_allocation_pointer (gen) += size + pad;
14147         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14148         if (generation_allocate_end_seg_p (gen))
14149         {
14150             generation_end_seg_allocated (gen) += size;
14151         }
14152         else
14153         {
14154             generation_free_list_allocated (gen) += size;
14155         }
14156         generation_allocation_size (gen) += size;
14157
14158         dprintf (3, ("aio: ptr: %Ix, limit: %Ix, sr: %Ix", 
14159             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14160             generation_allocation_context_start_region (gen)));
14161
14162         return result + pad;;
14163     }
14164 }
14165
14166 void gc_heap::repair_allocation_in_expanded_heap (generation* consing_gen)
14167 {
14168     //make sure that every generation has a planned allocation start
14169     int  gen_number = max_generation - 1;
14170     while (gen_number>= 0)
14171     {
14172         generation* gen = generation_of (gen_number);
14173         if (0 == generation_plan_allocation_start (gen))
14174         {
14175             realloc_plan_generation_start (gen, consing_gen);
14176
14177             assert (generation_plan_allocation_start (gen));
14178         }
14179         gen_number--;
14180     }
14181
14182     // now we know the planned allocation size
14183     size_t  size = (generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
14184     heap_segment* seg = generation_allocation_segment (consing_gen);
14185     if (generation_allocation_limit (consing_gen) == heap_segment_plan_allocated (seg))
14186     {
14187         if (size != 0)
14188         {
14189             heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
14190         }
14191     }
14192     else
14193     {
14194         assert (settings.condemned_generation == max_generation);
14195         uint8_t* first_address = generation_allocation_limit (consing_gen);
14196         //look through the pinned plugs for relevant ones.
14197         //Look for the right pinned plug to start from.
14198         size_t mi = 0;
14199         mark* m = 0;
14200         while (mi != mark_stack_tos)
14201         {
14202             m = pinned_plug_of (mi);
14203             if ((pinned_plug (m) == first_address))
14204                 break;
14205             else
14206                 mi++;
14207         }
14208         assert (mi != mark_stack_tos);
14209         pinned_len (m) = size;
14210     }
14211 }
14212
14213 //tododefrag optimize for new segment (plan_allocated == mem)
14214 uint8_t* gc_heap::allocate_in_expanded_heap (generation* gen,
14215                                           size_t size,
14216                                           BOOL& adjacentp,
14217                                           uint8_t* old_loc,
14218 #ifdef SHORT_PLUGS
14219                                           BOOL set_padding_on_saved_p,
14220                                           mark* pinned_plug_entry,
14221 #endif //SHORT_PLUGS
14222                                           BOOL consider_bestfit,
14223                                           int active_new_gen_number
14224                                           REQD_ALIGN_AND_OFFSET_DCL)
14225 {
14226     UNREFERENCED_PARAMETER(active_new_gen_number);
14227     dprintf (3, ("aie: P: %Ix, size: %Ix", old_loc, size));
14228
14229     size = Align (size);
14230     assert (size >= Align (min_obj_size));
14231     int pad_in_front = ((old_loc != 0) && (active_new_gen_number != max_generation)) ? USE_PADDING_FRONT : 0;
14232
14233     if (consider_bestfit && use_bestfit)
14234     {
14235         assert (bestfit_seg);
14236         dprintf (SEG_REUSE_LOG_1, ("reallocating 0x%Ix in expanded heap, size: %Id", 
14237                     old_loc, size));
14238         return bestfit_seg->fit (old_loc, 
14239 #ifdef SHORT_PLUGS
14240                                  set_padding_on_saved_p,
14241                                  pinned_plug_entry,
14242 #endif //SHORT_PLUGS
14243                                  size REQD_ALIGN_AND_OFFSET_ARG);
14244     }
14245
14246     heap_segment* seg = generation_allocation_segment (gen);
14247
14248     if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14249                        generation_allocation_limit (gen), old_loc,
14250                        ((generation_allocation_limit (gen) !=
14251                           heap_segment_plan_allocated (seg))? USE_PADDING_TAIL : 0) | pad_in_front)))
14252     {
14253         dprintf (3, ("aie: can't fit: ptr: %Ix, limit: %Ix", generation_allocation_pointer (gen),
14254             generation_allocation_limit (gen)));
14255
14256         adjacentp = FALSE;
14257         uint8_t* first_address = (generation_allocation_limit (gen) ?
14258                                generation_allocation_limit (gen) :
14259                                heap_segment_mem (seg));
14260         assert (in_range_for_segment (first_address, seg));
14261
14262         uint8_t* end_address   = heap_segment_reserved (seg);
14263
14264         dprintf (3, ("aie: first_addr: %Ix, gen alloc limit: %Ix, end_address: %Ix",
14265             first_address, generation_allocation_limit (gen), end_address));
14266
14267         size_t mi = 0;
14268         mark* m = 0;
14269
14270         if (heap_segment_allocated (seg) != heap_segment_mem (seg))
14271         {
14272             assert (settings.condemned_generation == max_generation);
14273             //look through the pinned plugs for relevant ones.
14274             //Look for the right pinned plug to start from.
14275             while (mi != mark_stack_tos)
14276             {
14277                 m = pinned_plug_of (mi);
14278                 if ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address))
14279                 {
14280                     dprintf (3, ("aie: found pin: %Ix", pinned_plug (m)));
14281                     break;
14282                 }
14283                 else
14284                     mi++;
14285             }
14286             if (mi != mark_stack_tos)
14287             {
14288                 //fix old free list.
14289                 size_t  hsize = (generation_allocation_limit (gen) - generation_allocation_pointer (gen));
14290                 {
14291                     dprintf(3,("gc filling up hole"));
14292                     ptrdiff_t mi1 = (ptrdiff_t)mi;
14293                     while ((mi1 >= 0) &&
14294                            (pinned_plug (pinned_plug_of(mi1)) != generation_allocation_limit (gen)))
14295                     {
14296                         dprintf (3, ("aie: checking pin %Ix", pinned_plug (pinned_plug_of(mi1))));
14297                         mi1--;
14298                     }
14299                     if (mi1 >= 0)
14300                     {
14301                         size_t saved_pinned_len = pinned_len (pinned_plug_of(mi1));
14302                         pinned_len (pinned_plug_of(mi1)) = hsize;
14303                         dprintf (3, ("changing %Ix len %Ix->%Ix", 
14304                             pinned_plug (pinned_plug_of(mi1)), 
14305                             saved_pinned_len, pinned_len (pinned_plug_of(mi1))));
14306                     }
14307                 }
14308             }
14309         }
14310         else
14311         {
14312             assert (generation_allocation_limit (gen) ==
14313                     generation_allocation_pointer (gen));
14314             mi = mark_stack_tos;
14315         }
14316
14317         while ((mi != mark_stack_tos) && in_range_for_segment (pinned_plug (m), seg))
14318         {
14319             size_t len = pinned_len (m);
14320             uint8_t*  free_list = (pinned_plug (m) - len);
14321             dprintf (3, ("aie: testing free item: %Ix->%Ix(%Ix)", 
14322                 free_list, (free_list + len), len));
14323             if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, free_list, (free_list + len), old_loc, USE_PADDING_TAIL | pad_in_front))
14324             {
14325                 dprintf (3, ("aie: Found adequate unused area: %Ix, size: %Id",
14326                             (size_t)free_list, len));
14327                 {
14328                     generation_allocation_pointer (gen) = free_list;
14329                     generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14330                     generation_allocation_limit (gen) = (free_list + len);
14331                 }
14332                 goto allocate_in_free;
14333             }
14334             mi++;
14335             m = pinned_plug_of (mi);
14336         }
14337
14338         //switch to the end of the segment.
14339         generation_allocation_pointer (gen) = heap_segment_plan_allocated (seg);
14340         generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14341         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14342         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14343         dprintf (3, ("aie: switching to end of seg: %Ix->%Ix(%Ix)", 
14344             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14345             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
14346
14347         if (!size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14348                          generation_allocation_limit (gen), old_loc, USE_PADDING_TAIL | pad_in_front))
14349         {
14350             dprintf (3, ("aie: ptr: %Ix, limit: %Ix, can't alloc", generation_allocation_pointer (gen),
14351                 generation_allocation_limit (gen)));
14352             assert (!"Can't allocate if no free space");
14353             return 0;
14354         }
14355     }
14356     else
14357     {
14358         adjacentp = TRUE;
14359     }
14360
14361 allocate_in_free:
14362     {
14363         uint8_t*  result = generation_allocation_pointer (gen);
14364         size_t pad = 0;
14365
14366 #ifdef SHORT_PLUGS
14367         if ((pad_in_front & USE_PADDING_FRONT) &&
14368             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14369              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14370
14371         {
14372             pad = Align (min_obj_size);
14373             set_padding_in_expand (old_loc, set_padding_on_saved_p, pinned_plug_entry);
14374         }
14375 #endif //SHORT_PLUGS
14376
14377 #ifdef FEATURE_STRUCTALIGN
14378         _ASSERTE(!old_loc || alignmentOffset != 0);
14379         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14380         if (old_loc != 0)
14381         {
14382             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14383             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14384             pad += pad1;
14385             adjacentp = FALSE;
14386         }
14387 #else // FEATURE_STRUCTALIGN
14388         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14389         {
14390             pad += switch_alignment_size (is_plug_padded (old_loc));
14391             set_node_realigned (old_loc);
14392             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14393                          (size_t)old_loc, (size_t)(result+pad)));
14394             assert (same_large_alignment_p (result + pad, old_loc));
14395             adjacentp = FALSE;
14396         }
14397 #endif // FEATURE_STRUCTALIGN
14398
14399         if ((old_loc == 0) || (pad != 0))
14400         {
14401             //allocating a non plug or a gap, so reset the start region
14402             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14403         }
14404
14405         generation_allocation_pointer (gen) += size + pad;
14406         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14407         dprintf (3, ("Allocated in expanded heap %Ix:%Id", (size_t)(result+pad), size));
14408
14409         dprintf (3, ("aie: ptr: %Ix, limit: %Ix, sr: %Ix", 
14410             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14411             generation_allocation_context_start_region (gen)));
14412
14413         return result + pad;
14414     }
14415 }
14416
14417 generation*  gc_heap::ensure_ephemeral_heap_segment (generation* consing_gen)
14418 {
14419     heap_segment* seg = generation_allocation_segment (consing_gen);
14420     if (seg != ephemeral_heap_segment)
14421     {
14422         assert (generation_allocation_pointer (consing_gen)>= heap_segment_mem (seg));
14423         assert (generation_allocation_pointer (consing_gen)<= heap_segment_committed (seg));
14424
14425         //fix the allocated size of the segment.
14426         heap_segment_plan_allocated (seg) = generation_allocation_pointer (consing_gen);
14427
14428         generation* new_consing_gen = generation_of (max_generation - 1);
14429         generation_allocation_pointer (new_consing_gen) =
14430                 heap_segment_mem (ephemeral_heap_segment);
14431         generation_allocation_limit (new_consing_gen) =
14432             generation_allocation_pointer (new_consing_gen);
14433         generation_allocation_context_start_region (new_consing_gen) = 
14434             generation_allocation_pointer (new_consing_gen);
14435         generation_allocation_segment (new_consing_gen) = ephemeral_heap_segment;
14436
14437         return new_consing_gen;
14438     }
14439     else
14440         return consing_gen;
14441 }
14442
14443 uint8_t* gc_heap::allocate_in_condemned_generations (generation* gen,
14444                                                   size_t size,
14445                                                   int from_gen_number,
14446 #ifdef SHORT_PLUGS
14447                                                   BOOL* convert_to_pinned_p,
14448                                                   uint8_t* next_pinned_plug,
14449                                                   heap_segment* current_seg,
14450 #endif //SHORT_PLUGS
14451                                                   uint8_t* old_loc
14452                                                   REQD_ALIGN_AND_OFFSET_DCL)
14453 {
14454     // Make sure that the youngest generation gap hasn't been allocated
14455     if (settings.promotion)
14456     {
14457         assert (generation_plan_allocation_start (youngest_generation) == 0);
14458     }
14459
14460     size = Align (size);
14461     assert (size >= Align (min_obj_size));
14462     int to_gen_number = from_gen_number;
14463     if (from_gen_number != (int)max_generation)
14464     {
14465         to_gen_number = from_gen_number + (settings.promotion ? 1 : 0);
14466     }
14467
14468     dprintf (3, ("aic gen%d: s: %Id", gen->gen_num, size));
14469
14470     int pad_in_front = ((old_loc != 0) && (to_gen_number != max_generation)) ? USE_PADDING_FRONT : 0;
14471     
14472     if ((from_gen_number != -1) && (from_gen_number != (int)max_generation) && settings.promotion)
14473     {
14474         generation_condemned_allocated (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14475         generation_allocation_size (generation_of (from_gen_number + (settings.promotion ? 1 : 0))) += size;
14476     }
14477 retry:
14478     {
14479         heap_segment* seg = generation_allocation_segment (gen);
14480         if (! (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14481                            generation_allocation_limit (gen), old_loc,
14482                            ((generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))?USE_PADDING_TAIL:0)|pad_in_front)))
14483         {
14484             if ((! (pinned_plug_que_empty_p()) &&
14485                  (generation_allocation_limit (gen) ==
14486                   pinned_plug (oldest_pin()))))
14487             {
14488                 size_t entry = deque_pinned_plug();
14489                 mark* pinned_plug_entry = pinned_plug_of (entry);
14490                 size_t len = pinned_len (pinned_plug_entry);
14491                 uint8_t* plug = pinned_plug (pinned_plug_entry);
14492                 set_new_pin_info (pinned_plug_entry, generation_allocation_pointer (gen));
14493
14494 #ifdef FREE_USAGE_STATS
14495                 generation_allocated_in_pinned_free (gen) += generation_allocated_since_last_pin (gen);
14496                 dprintf (3, ("allocated %Id so far within pin %Ix, total->%Id", 
14497                     generation_allocated_since_last_pin (gen), 
14498                     plug,
14499                     generation_allocated_in_pinned_free (gen)));
14500                 generation_allocated_since_last_pin (gen) = 0;
14501
14502                 add_item_to_current_pinned_free (gen->gen_num, pinned_len (pinned_plug_of (entry)));
14503 #endif //FREE_USAGE_STATS
14504
14505                 dprintf (3, ("mark stack bos: %Id, tos: %Id, aic: p %Ix len: %Ix->%Ix", 
14506                     mark_stack_bos, mark_stack_tos, plug, len, pinned_len (pinned_plug_of (entry))));
14507
14508                 assert(mark_stack_array[entry].len == 0 ||
14509                        mark_stack_array[entry].len >= Align(min_obj_size));
14510                 generation_allocation_pointer (gen) = plug + len;
14511                 generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14512                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14513                 set_allocator_next_pin (gen);
14514
14515                 //Add the size of the pinned plug to the right pinned allocations
14516                 //find out which gen this pinned plug came from 
14517                 int frgn = object_gennum (plug);
14518                 if ((frgn != (int)max_generation) && settings.promotion)
14519                 {
14520                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
14521                     int togn = object_gennum_plan (plug);
14522                     if (frgn < togn)
14523                     {
14524                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
14525                     }
14526                 }
14527                 goto retry;
14528             }
14529             
14530             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
14531             {
14532                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14533                 dprintf (3, ("changed limit to plan alloc: %Ix", generation_allocation_limit (gen)));
14534             }
14535             else
14536             {
14537                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
14538                 {
14539                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14540                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14541                     dprintf (3, ("changed limit to commit: %Ix", generation_allocation_limit (gen)));
14542                 }
14543                 else
14544                 {
14545 #ifndef RESPECT_LARGE_ALIGNMENT
14546                     assert (gen != youngest_generation);
14547 #endif //RESPECT_LARGE_ALIGNMENT
14548
14549                     if (size_fit_p (size REQD_ALIGN_AND_OFFSET_ARG, generation_allocation_pointer (gen),
14550                                     heap_segment_reserved (seg), old_loc, USE_PADDING_TAIL | pad_in_front) &&
14551                         (grow_heap_segment (seg, generation_allocation_pointer (gen), old_loc,
14552                                             size, pad_in_front REQD_ALIGN_AND_OFFSET_ARG)))
14553                     {
14554                         dprintf (3, ("Expanded segment allocation by committing more memory"));
14555                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
14556                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
14557                     }
14558                     else
14559                     {
14560                         heap_segment*   next_seg = heap_segment_next (seg);
14561                         assert (generation_allocation_pointer (gen)>=
14562                                 heap_segment_mem (seg));
14563                         // Verify that all pinned plugs for this segment are consumed
14564                         if (!pinned_plug_que_empty_p() &&
14565                             ((pinned_plug (oldest_pin()) <
14566                               heap_segment_allocated (seg)) &&
14567                              (pinned_plug (oldest_pin()) >=
14568                               generation_allocation_pointer (gen))))
14569                         {
14570                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
14571                                          pinned_plug (oldest_pin())));
14572                             FATAL_GC_ERROR();
14573                         }
14574                         assert (generation_allocation_pointer (gen)>=
14575                                 heap_segment_mem (seg));
14576                         assert (generation_allocation_pointer (gen)<=
14577                                 heap_segment_committed (seg));
14578                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
14579
14580                         if (next_seg)
14581                         {
14582                             generation_allocation_segment (gen) = next_seg;
14583                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
14584                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
14585                             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14586                         }
14587                         else
14588                         {
14589                             return 0; //should only happen during allocation of generation 0 gap
14590                             // in that case we are going to grow the heap anyway
14591                         }
14592                     }
14593                 }
14594             }
14595             set_allocator_next_pin (gen);
14596
14597             goto retry;
14598         }
14599     }
14600
14601     {
14602         assert (generation_allocation_pointer (gen)>=
14603                 heap_segment_mem (generation_allocation_segment (gen)));
14604         uint8_t* result = generation_allocation_pointer (gen);
14605         size_t pad = 0;
14606 #ifdef SHORT_PLUGS
14607         if ((pad_in_front & USE_PADDING_FRONT) &&
14608             (((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))==0) ||
14609              ((generation_allocation_pointer (gen) - generation_allocation_context_start_region (gen))>=DESIRED_PLUG_LENGTH)))
14610         {
14611             ptrdiff_t dist = old_loc - result;
14612             if (dist == 0)
14613             {
14614                 dprintf (3, ("old alloc: %Ix, same as new alloc, not padding", old_loc));
14615                 pad = 0;
14616             }
14617             else
14618             {
14619                 if ((dist > 0) && (dist < (ptrdiff_t)Align (min_obj_size)))
14620                 {
14621                     dprintf (3, ("old alloc: %Ix, only %d bytes > new alloc! Shouldn't happen", old_loc, dist));
14622                     FATAL_GC_ERROR();
14623                 }
14624
14625                 pad = Align (min_obj_size);
14626                 set_plug_padded (old_loc);
14627             }
14628         }
14629 #endif //SHORT_PLUGS
14630 #ifdef FEATURE_STRUCTALIGN
14631         _ASSERTE(!old_loc || alignmentOffset != 0);
14632         _ASSERTE(old_loc || requiredAlignment == DATA_ALIGNMENT);
14633         if ((old_loc != 0))
14634         {
14635             size_t pad1 = ComputeStructAlignPad(result+pad, requiredAlignment, alignmentOffset);
14636             set_node_aligninfo (old_loc, requiredAlignment, pad1);
14637             pad += pad1;
14638         }
14639 #else // FEATURE_STRUCTALIGN
14640         if (!((old_loc == 0) || same_large_alignment_p (old_loc, result+pad)))
14641         {
14642             pad += switch_alignment_size (is_plug_padded (old_loc));
14643             set_node_realigned(old_loc);
14644             dprintf (3, ("Allocation realignment old_loc: %Ix, new_loc:%Ix",
14645                          (size_t)old_loc, (size_t)(result+pad)));
14646             assert (same_large_alignment_p (result + pad, old_loc));
14647         }
14648 #endif // FEATURE_STRUCTALIGN
14649
14650 #ifdef SHORT_PLUGS
14651         if ((next_pinned_plug != 0) && (pad != 0) && (generation_allocation_segment (gen) == current_seg))
14652         {
14653             assert (old_loc != 0);
14654             ptrdiff_t dist_to_next_pin = (ptrdiff_t)(next_pinned_plug - (generation_allocation_pointer (gen) + size + pad));
14655             assert (dist_to_next_pin >= 0);
14656
14657             if ((dist_to_next_pin >= 0) && (dist_to_next_pin < (ptrdiff_t)Align (min_obj_size)))
14658             {
14659                 dprintf (3, ("%Ix->(%Ix,%Ix),%Ix(%Ix)(%Ix),NP->PP", 
14660                     old_loc, 
14661                     generation_allocation_pointer (gen),
14662                     generation_allocation_limit (gen),
14663                     next_pinned_plug,
14664                     size, 
14665                     dist_to_next_pin));
14666                 clear_plug_padded (old_loc);
14667                 pad = 0;
14668                 *convert_to_pinned_p = TRUE;
14669                 record_interesting_data_point (idp_converted_pin);
14670
14671                 return 0;
14672             }
14673         }
14674 #endif //SHORT_PLUGS
14675
14676         if ((old_loc == 0) || (pad != 0))
14677         {
14678             //allocating a non plug or a gap, so reset the start region
14679             generation_allocation_context_start_region (gen) = generation_allocation_pointer (gen);
14680         }
14681
14682         generation_allocation_pointer (gen) += size + pad;
14683         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
14684
14685 #ifdef FREE_USAGE_STATS
14686         generation_allocated_since_last_pin (gen) += size;
14687 #endif //FREE_USAGE_STATS
14688
14689         dprintf (3, ("aic: ptr: %Ix, limit: %Ix, sr: %Ix", 
14690             generation_allocation_pointer (gen), generation_allocation_limit (gen),
14691             generation_allocation_context_start_region (gen)));
14692
14693         assert (result + pad);
14694         return result + pad;
14695     }
14696 }
14697
14698 inline int power (int x, int y)
14699 {
14700     int z = 1;
14701     for (int i = 0; i < y; i++)
14702     {
14703         z = z*x;
14704     }
14705     return z;
14706 }
14707
14708 int gc_heap::joined_generation_to_condemn (BOOL should_evaluate_elevation, 
14709                                            int initial_gen,
14710                                            int current_gen,
14711                                            BOOL* blocking_collection_p
14712                                            STRESS_HEAP_ARG(int n_original))
14713 {
14714     int n = current_gen;
14715 #ifdef MULTIPLE_HEAPS
14716     BOOL joined_last_gc_before_oom = FALSE;
14717     for (int i = 0; i < n_heaps; i++)
14718     {
14719         if (g_heaps[i]->last_gc_before_oom)
14720         {
14721             dprintf (GTC_LOG, ("h%d is setting blocking to TRUE", i));
14722             joined_last_gc_before_oom = TRUE;
14723             break;
14724         }
14725     }
14726 #else
14727     BOOL joined_last_gc_before_oom = last_gc_before_oom;
14728 #endif //MULTIPLE_HEAPS
14729
14730     if (joined_last_gc_before_oom && settings.pause_mode != pause_low_latency)
14731     {
14732         assert (*blocking_collection_p);
14733     }
14734
14735     if (should_evaluate_elevation && (n == max_generation))
14736     {
14737         dprintf (GTC_LOG, ("lock: %d(%d)", 
14738             (settings.should_lock_elevation ? 1 : 0), 
14739             settings.elevation_locked_count));
14740
14741         if (settings.should_lock_elevation)
14742         {
14743             settings.elevation_locked_count++;
14744             if (settings.elevation_locked_count == 6)
14745             {
14746                 settings.elevation_locked_count = 0;
14747             }
14748             else
14749             {
14750                 n = max_generation - 1;
14751                 settings.elevation_reduced = TRUE;
14752             }
14753         }
14754         else
14755         {
14756             settings.elevation_locked_count = 0;
14757         }
14758     }
14759     else
14760     {
14761         settings.should_lock_elevation = FALSE;
14762         settings.elevation_locked_count = 0;
14763     }
14764
14765     if (provisional_mode_triggered && (n == max_generation))
14766     {
14767         // There are a few cases where we should not reduce the generation.
14768         if ((initial_gen == max_generation) || (settings.reason == reason_alloc_loh))
14769         {
14770             // If we are doing a full GC in the provisional mode, we always
14771             // make it blocking because we don't want to get into a situation
14772             // where foreground GCs are asking for a compacting full GC right away
14773             // and not getting it.
14774             dprintf (GTC_LOG, ("full GC induced, not reducing gen"));
14775             *blocking_collection_p = TRUE;
14776         }
14777         else if (should_expand_in_full_gc || joined_last_gc_before_oom)
14778         {
14779             dprintf (GTC_LOG, ("need full blocking GCs to expand heap or avoid OOM, not reducing gen"));
14780             assert (*blocking_collection_p);
14781         }
14782         else
14783         {
14784             dprintf (GTC_LOG, ("reducing gen in PM: %d->%d->%d", initial_gen, n, (max_generation - 1)));
14785             n = max_generation - 1;
14786         }
14787     }
14788
14789     if (should_expand_in_full_gc)
14790     {
14791         should_expand_in_full_gc = FALSE;
14792     }
14793
14794     if (heap_hard_limit)
14795     {
14796         // If we have already consumed 90% of the limit, we should check to see if we should compact LOH.
14797         // TODO: should unify this with gen2.
14798         dprintf (GTC_LOG, ("committed %Id is %d%% of limit %Id", 
14799             current_total_committed, (int)((float)current_total_committed * 100.0 / (float)heap_hard_limit),
14800             heap_hard_limit));
14801         if ((current_total_committed * 10) >= (heap_hard_limit * 9))
14802         {
14803             bool full_compact_gc_p = false;
14804
14805             size_t loh_frag = get_total_gen_fragmentation (max_generation + 1);
14806             
14807             // If the LOH frag is >= 1/8 it's worth compacting it
14808             if ((loh_frag * 8) >= heap_hard_limit)
14809             {
14810                 dprintf (GTC_LOG, ("loh frag: %Id > 1/8 of limit %Id", loh_frag, (heap_hard_limit / 8)));
14811                 full_compact_gc_p = true;
14812             }
14813             else
14814             {
14815                 // If there's not much fragmentation but it looks like it'll be productive to
14816                 // collect LOH, do that.
14817                 size_t est_loh_reclaim = get_total_gen_estimated_reclaim (max_generation + 1);
14818                 full_compact_gc_p = ((est_loh_reclaim * 8) >= heap_hard_limit);
14819                 dprintf (GTC_LOG, ("loh est reclaim: %Id, 1/8 of limit %Id", est_loh_reclaim, (heap_hard_limit / 8)));
14820             }
14821
14822             if (full_compact_gc_p)
14823             {
14824                 n = max_generation;
14825                 *blocking_collection_p = TRUE;
14826                 settings.loh_compaction = TRUE;
14827                 dprintf (GTC_LOG, ("compacting LOH due to hard limit"));
14828             }
14829         }
14830     }
14831
14832     if ((n == max_generation) && (*blocking_collection_p == FALSE))
14833     {
14834         // If we are doing a gen2 we should reset elevation regardless and let the gen2
14835         // decide if we should lock again or in the bgc case by design we will not retract
14836         // gen1 start.
14837         settings.should_lock_elevation = FALSE;
14838         settings.elevation_locked_count = 0;
14839         dprintf (1, ("doing bgc, reset elevation"));
14840     }
14841
14842 #ifdef STRESS_HEAP
14843 #ifdef BACKGROUND_GC
14844     // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
14845     // generations to be collected,
14846     //
14847     // [LOCALGC TODO] STRESS_HEAP is not defined for a standalone GC so there are multiple
14848     // things that need to be fixed in this code block.
14849     if (n_original != max_generation &&
14850         g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
14851     {
14852 #ifndef FEATURE_REDHAWK
14853         // for the GC stress mix mode throttle down gen2 collections
14854         if (g_pConfig->IsGCStressMix())
14855         {
14856             size_t current_gc_count = 0;
14857
14858 #ifdef MULTIPLE_HEAPS
14859             current_gc_count = (size_t)dd_collection_count (g_heaps[0]->dynamic_data_of (0));
14860 #else
14861             current_gc_count = (size_t)dd_collection_count (dynamic_data_of (0));
14862 #endif //MULTIPLE_HEAPS
14863             // in gc stress, only escalate every 10th non-gen2 collection to a gen2...
14864             if ((current_gc_count % 10) == 0)
14865             {
14866                 n = max_generation;
14867             }
14868         }
14869         // for traditional GC stress
14870         else
14871 #endif // !FEATURE_REDHAWK
14872         if (*blocking_collection_p)
14873         {
14874             // We call StressHeap() a lot for Concurrent GC Stress. However,
14875             // if we can not do a concurrent collection, no need to stress anymore.
14876             // @TODO: Enable stress when the memory pressure goes down again
14877             GCStressPolicy::GlobalDisable();
14878         }
14879         else
14880         {
14881             n = max_generation;
14882         }
14883     }
14884 #endif //BACKGROUND_GC
14885 #endif //STRESS_HEAP
14886
14887     return n;
14888 }
14889
14890 inline
14891 size_t get_survived_size (gc_history_per_heap* hist)
14892 {
14893     size_t surv_size = 0;
14894     gc_generation_data* gen_data;
14895
14896     for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
14897     {
14898         gen_data = &(hist->gen_data[gen_number]); 
14899         surv_size += (gen_data->size_after - 
14900                       gen_data->free_list_space_after - 
14901                       gen_data->free_obj_space_after);
14902     }
14903
14904     return surv_size;
14905 }
14906
14907 size_t gc_heap::get_total_survived_size()
14908 {
14909     size_t total_surv_size = 0;
14910 #ifdef MULTIPLE_HEAPS
14911     for (int i = 0; i < gc_heap::n_heaps; i++)
14912     {
14913         gc_heap* hp = gc_heap::g_heaps[i];
14914         gc_history_per_heap* current_gc_data_per_heap = hp->get_gc_data_per_heap();
14915         total_surv_size += get_survived_size (current_gc_data_per_heap);
14916     }
14917 #else
14918     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
14919     total_surv_size = get_survived_size (current_gc_data_per_heap);
14920 #endif //MULTIPLE_HEAPS
14921     return total_surv_size;
14922 }
14923
14924 size_t gc_heap::get_total_allocated_since_last_gc()
14925 {
14926     size_t total_allocated_size = 0;
14927 #ifdef MULTIPLE_HEAPS
14928     for (int i = 0; i < gc_heap::n_heaps; i++)
14929     {
14930         gc_heap* hp = gc_heap::g_heaps[i];
14931         total_allocated_size += hp->allocated_since_last_gc;
14932         hp->allocated_since_last_gc = 0;
14933     }
14934 #else
14935     total_allocated_size = allocated_since_last_gc;
14936     allocated_since_last_gc = 0;
14937 #endif //MULTIPLE_HEAPS
14938     return total_allocated_size;
14939 }
14940
14941 // Gets what's allocated on both SOH and LOH that hasn't been collected.
14942 size_t gc_heap::get_current_allocated()
14943 {
14944     dynamic_data* dd = dynamic_data_of (0);
14945     size_t current_alloc = dd_desired_allocation (dd) - dd_new_allocation (dd);
14946     dd = dynamic_data_of (max_generation + 1);
14947     current_alloc += dd_desired_allocation (dd) - dd_new_allocation (dd);
14948
14949     return current_alloc;
14950 }
14951
14952 size_t gc_heap::get_total_allocated()
14953 {
14954     size_t total_current_allocated = 0;
14955 #ifdef MULTIPLE_HEAPS
14956     for (int i = 0; i < gc_heap::n_heaps; i++)
14957     {
14958         gc_heap* hp = gc_heap::g_heaps[i];
14959         total_current_allocated += hp->get_current_allocated();
14960     }
14961 #else
14962     total_current_allocated = get_current_allocated();
14963 #endif //MULTIPLE_HEAPS
14964     return total_current_allocated;
14965 }
14966
14967 size_t gc_heap::current_generation_size (int gen_number)
14968 {
14969     dynamic_data* dd = dynamic_data_of (gen_number);
14970     size_t gen_size = (dd_current_size (dd) + dd_desired_allocation (dd)
14971                         - dd_new_allocation (dd));
14972
14973     return gen_size;
14974 }
14975
14976 #ifdef _PREFAST_
14977 #pragma warning(push)
14978 #pragma warning(disable:6326) // "Potential comparison of a constant with another constant" is intentional in this function.
14979 #endif //_PREFAST_
14980
14981 /*
14982     This is called by when we are actually doing a GC, or when we are just checking whether
14983     we would do a full blocking GC, in which case check_only_p is TRUE.
14984
14985     The difference between calling this with check_only_p TRUE and FALSE is that when it's
14986     TRUE: 
14987             settings.reason is ignored
14988             budgets are not checked (since they are checked before this is called)
14989             it doesn't change anything non local like generation_skip_ratio
14990 */
14991 int gc_heap::generation_to_condemn (int n_initial, 
14992                                     BOOL* blocking_collection_p, 
14993                                     BOOL* elevation_requested_p,
14994                                     BOOL check_only_p)
14995 {
14996     gc_mechanisms temp_settings = settings;
14997     gen_to_condemn_tuning temp_condemn_reasons;
14998     gc_mechanisms* local_settings = (check_only_p ? &temp_settings : &settings);
14999     gen_to_condemn_tuning* local_condemn_reasons = (check_only_p ? &temp_condemn_reasons : &gen_to_condemn_reasons);
15000     if (!check_only_p)
15001     {
15002         if ((local_settings->reason == reason_oos_soh) || (local_settings->reason == reason_oos_loh))
15003         {
15004             assert (n_initial >= 1);
15005         }
15006
15007         assert (settings.reason != reason_empty);
15008     }
15009
15010     local_condemn_reasons->init();
15011
15012     int n = n_initial;
15013     int n_alloc = n;
15014     if (heap_number == 0)
15015     {
15016         dprintf (GTC_LOG, ("init: %d(%d)", n_initial, settings.reason));
15017     }
15018     int i = 0;
15019     int temp_gen = 0;
15020     BOOL low_memory_detected = g_low_memory_status;
15021     uint32_t memory_load = 0;
15022     uint64_t available_physical = 0;
15023     uint64_t available_page_file = 0;
15024     BOOL check_memory = FALSE;
15025     BOOL high_fragmentation  = FALSE;
15026     BOOL v_high_memory_load  = FALSE;
15027     BOOL high_memory_load    = FALSE;
15028     BOOL low_ephemeral_space = FALSE;
15029     BOOL evaluate_elevation  = TRUE;
15030     *elevation_requested_p   = FALSE;
15031     *blocking_collection_p   = FALSE;
15032
15033     BOOL check_max_gen_alloc = TRUE;
15034
15035 #ifdef STRESS_HEAP
15036     int orig_gen = n;
15037 #endif //STRESS_HEAP
15038
15039     if (!check_only_p)
15040     {
15041         dd_fragmentation (dynamic_data_of (0)) = 
15042             generation_free_list_space (youngest_generation) + 
15043             generation_free_obj_space (youngest_generation);
15044
15045         dd_fragmentation (dynamic_data_of (max_generation + 1)) = 
15046             generation_free_list_space (large_object_generation) + 
15047             generation_free_obj_space (large_object_generation);
15048
15049         //save new_allocation
15050         for (i = 0; i <= max_generation+1; i++)
15051         {
15052             dynamic_data* dd = dynamic_data_of (i);
15053             dprintf (GTC_LOG, ("h%d: g%d: l: %Id (%Id)", 
15054                             heap_number, i,
15055                             dd_new_allocation (dd),
15056                             dd_desired_allocation (dd)));
15057             dd_gc_new_allocation (dd) = dd_new_allocation (dd);
15058         }
15059
15060         local_condemn_reasons->set_gen (gen_initial, n);
15061         temp_gen = n;
15062
15063 #ifdef BACKGROUND_GC
15064         if (recursive_gc_sync::background_running_p())
15065         {
15066             dprintf (GTC_LOG, ("bgc in prog, 1"));
15067             check_max_gen_alloc = FALSE;
15068         }
15069 #endif //BACKGROUND_GC
15070
15071         if (check_max_gen_alloc)
15072         {
15073             //figure out if large objects need to be collected.
15074             if (get_new_allocation (max_generation+1) <= 0)
15075             {
15076                 n = max_generation;
15077                 local_condemn_reasons->set_gen (gen_alloc_budget, n);
15078             }
15079         }
15080
15081         //figure out which generation ran out of allocation
15082         for (i = n+1; i <= (check_max_gen_alloc ? max_generation : (max_generation - 1)); i++)
15083         {
15084             if (get_new_allocation (i) <= 0)
15085             {
15086                 n = i;
15087             }
15088             else
15089                 break;
15090         }
15091     }
15092
15093     if (n > temp_gen)
15094     {
15095         local_condemn_reasons->set_gen (gen_alloc_budget, n);
15096     }
15097
15098     dprintf (GTC_LOG, ("h%d: g%d budget", heap_number, ((get_new_allocation (max_generation+1) <= 0) ? 3 : n)));
15099
15100     n_alloc = n;
15101
15102 #if defined(BACKGROUND_GC) && !defined(MULTIPLE_HEAPS)
15103     //time based tuning
15104     // if enough time has elapsed since the last gc
15105     // and the number of gc is too low (1/10 of lower gen) then collect
15106     // This should also be enabled if we have memory concerns
15107     int n_time_max = max_generation;
15108
15109     if (!check_only_p)
15110     {
15111         if (recursive_gc_sync::background_running_p())
15112         {
15113             n_time_max = max_generation - 1;
15114         }
15115     }
15116
15117     if ((local_settings->pause_mode == pause_interactive) ||
15118         (local_settings->pause_mode == pause_sustained_low_latency))
15119     {
15120         dynamic_data* dd0 = dynamic_data_of (0);
15121         size_t now = GetHighPrecisionTimeStamp();
15122         temp_gen = n;
15123         for (i = (temp_gen+1); i <= n_time_max; i++)
15124         {
15125             dynamic_data* dd = dynamic_data_of (i);
15126             if ((now > dd_time_clock(dd) + dd_time_clock_interval(dd)) &&
15127                 (dd_gc_clock (dd0) > (dd_gc_clock (dd) + dd_gc_clock_interval(dd))) &&
15128                 ((n < max_generation) || ((dd_current_size (dd) < dd_max_size (dd0)))))
15129             {
15130                 n = min (i, n_time_max);
15131                 dprintf (GTC_LOG, ("time %d", n));
15132             }
15133         }
15134         if (n > temp_gen)
15135         {
15136             local_condemn_reasons->set_gen (gen_time_tuning, n);
15137         }
15138     }
15139
15140     if (n != n_alloc)
15141     {
15142         dprintf (GTC_LOG, ("Condemning %d based on time tuning and fragmentation", n));
15143     }
15144 #endif //BACKGROUND_GC && !MULTIPLE_HEAPS
15145
15146     if (n < (max_generation - 1))
15147     {
15148         if (dt_low_card_table_efficiency_p (tuning_deciding_condemned_gen))
15149         {
15150             n = max (n, max_generation - 1);
15151             local_settings->promotion = TRUE;
15152             dprintf (GTC_LOG, ("h%d: skip %d, c %d",
15153                         heap_number, generation_skip_ratio, n));
15154             local_condemn_reasons->set_condition (gen_low_card_p);
15155         }
15156     }
15157
15158     if (!check_only_p)
15159     {
15160         generation_skip_ratio = 100;
15161     }
15162
15163     if (dt_low_ephemeral_space_p (check_only_p ? 
15164                                   tuning_deciding_full_gc : 
15165                                   tuning_deciding_condemned_gen))
15166     {
15167         low_ephemeral_space = TRUE;
15168
15169         n = max (n, max_generation - 1);
15170         local_condemn_reasons->set_condition (gen_low_ephemeral_p);
15171         dprintf (GTC_LOG, ("h%d: low eph", heap_number));
15172
15173         if (!provisional_mode_triggered)
15174         {
15175 #ifdef BACKGROUND_GC
15176             if (!gc_can_use_concurrent || (generation_free_list_space (generation_of (max_generation)) == 0))
15177 #endif //BACKGROUND_GC
15178             {
15179                 //It is better to defragment first if we are running out of space for
15180                 //the ephemeral generation but we have enough fragmentation to make up for it
15181                 //in the non ephemeral generation. Essentially we are trading a gen2 for 
15182                 // having to expand heap in ephemeral collections.
15183                 if (dt_high_frag_p (tuning_deciding_condemned_gen, 
15184                                     max_generation - 1, 
15185                                     TRUE))
15186                 {
15187                     high_fragmentation = TRUE;
15188                     local_condemn_reasons->set_condition (gen_max_high_frag_e_p);
15189                     dprintf (GTC_LOG, ("heap%d: gen1 frag", heap_number));
15190                 }
15191             }
15192         }
15193     }
15194
15195     //figure out which ephemeral generation is too fragramented
15196     temp_gen = n;
15197     for (i = n+1; i < max_generation; i++)
15198     {
15199         if (dt_high_frag_p (tuning_deciding_condemned_gen, i))
15200         {
15201             dprintf (GTC_LOG, ("h%d g%d too frag", heap_number, i));
15202             n = i;
15203         }
15204         else
15205             break;
15206     }
15207
15208     if (low_ephemeral_space)
15209     {
15210         //enable promotion
15211         local_settings->promotion = TRUE;
15212     }
15213
15214     if (n > temp_gen)
15215     {
15216         local_condemn_reasons->set_condition (gen_eph_high_frag_p);
15217     }
15218
15219     if (!check_only_p)
15220     {
15221         if (settings.pause_mode == pause_low_latency)
15222         {
15223             if (!is_induced (settings.reason))
15224             {
15225                 n = min (n, max_generation - 1);
15226                 dprintf (GTC_LOG, ("low latency mode is enabled, condemning %d", n));
15227                 evaluate_elevation = FALSE;
15228                 goto exit;
15229             }
15230         }
15231     }
15232
15233     // It's hard to catch when we get to the point that the memory load is so high
15234     // we get an induced GC from the finalizer thread so we are checking the memory load
15235     // for every gen0 GC.
15236     check_memory = (check_only_p ? 
15237                     (n >= 0) : 
15238                     ((n >= 1) || low_memory_detected));
15239
15240     if (check_memory)
15241     {
15242         //find out if we are short on memory
15243         get_memory_info (&memory_load, &available_physical, &available_page_file);
15244         if (heap_number == 0)
15245         {
15246             dprintf (GTC_LOG, ("ml: %d", memory_load));
15247         }
15248         
15249         // Need to get it early enough for all heaps to use.
15250         entry_available_physical_mem = available_physical;
15251         local_settings->entry_memory_load = memory_load;
15252
15253         // @TODO: Force compaction more often under GCSTRESS
15254         if (memory_load >= high_memory_load_th || low_memory_detected)
15255         {
15256 #ifdef SIMPLE_DPRINTF
15257             // stress log can't handle any parameter that's bigger than a void*.
15258             if (heap_number == 0)
15259             {
15260                 dprintf (GTC_LOG, ("tp: %I64d, ap: %I64d", total_physical_mem, available_physical));
15261             }
15262 #endif //SIMPLE_DPRINTF
15263
15264             high_memory_load = TRUE;
15265
15266             if (memory_load >= v_high_memory_load_th || low_memory_detected)
15267             {
15268                 // TODO: Perhaps in 64-bit we should be estimating gen1's fragmentation as well since
15269                 // gen1/gen0 may take a lot more memory than gen2.
15270                 if (!high_fragmentation)
15271                 {
15272                     high_fragmentation = dt_estimate_reclaim_space_p (tuning_deciding_condemned_gen, max_generation);
15273                 }
15274                 v_high_memory_load = TRUE;
15275             }
15276             else
15277             {
15278                 if (!high_fragmentation)
15279                 {
15280                     high_fragmentation = dt_estimate_high_frag_p (tuning_deciding_condemned_gen, max_generation, available_physical);
15281                 }
15282             }
15283
15284             if (high_fragmentation)
15285             {
15286                 if (high_memory_load)
15287                 {
15288                     local_condemn_reasons->set_condition (gen_max_high_frag_m_p);
15289                 }
15290                 else if (v_high_memory_load)
15291                 {
15292                     local_condemn_reasons->set_condition (gen_max_high_frag_vm_p);
15293                 }
15294             }
15295         }
15296     }
15297
15298     dprintf (GTC_LOG, ("h%d: le: %d, hm: %d, vm: %d, f: %d",
15299                  heap_number, low_ephemeral_space, high_memory_load, v_high_memory_load,
15300                  high_fragmentation));
15301
15302     if (should_expand_in_full_gc)
15303     {
15304         dprintf (GTC_LOG, ("h%d: expand_in_full - BLOCK", heap_number));
15305         *blocking_collection_p = TRUE;
15306         evaluate_elevation = FALSE;
15307         n = max_generation;
15308         local_condemn_reasons->set_condition (gen_expand_fullgc_p);
15309     }
15310
15311     if (last_gc_before_oom)
15312     {
15313         dprintf (GTC_LOG, ("h%d: alloc full - BLOCK", heap_number));
15314         n = max_generation;
15315         *blocking_collection_p = TRUE;
15316
15317         if ((local_settings->reason == reason_oos_loh) ||
15318             (local_settings->reason == reason_alloc_loh))
15319         {
15320             evaluate_elevation = FALSE;
15321         }
15322
15323         local_condemn_reasons->set_condition (gen_before_oom);
15324     }
15325
15326     if (!check_only_p)
15327     {
15328         if (is_induced_blocking (settings.reason) && 
15329             n_initial == max_generation
15330             IN_STRESS_HEAP( && !settings.stress_induced ))
15331         {
15332             if (heap_number == 0)
15333             {
15334                 dprintf (GTC_LOG, ("induced - BLOCK"));
15335             }
15336
15337             *blocking_collection_p = TRUE;
15338             local_condemn_reasons->set_condition (gen_induced_fullgc_p);
15339             evaluate_elevation = FALSE;
15340         }
15341
15342         if (settings.reason == reason_induced_noforce)
15343         {
15344             local_condemn_reasons->set_condition (gen_induced_noforce_p);
15345             evaluate_elevation = FALSE;
15346         }
15347     }
15348
15349     if (!provisional_mode_triggered && evaluate_elevation && (low_ephemeral_space || high_memory_load || v_high_memory_load))
15350     {
15351         *elevation_requested_p = TRUE;
15352 #ifdef BIT64
15353         // if we are in high memory load and have consumed 10% of the gen2 budget, do a gen2 now.
15354         if (high_memory_load || v_high_memory_load)
15355         {
15356             dynamic_data* dd_max = dynamic_data_of (max_generation);
15357             if (((float)dd_new_allocation (dd_max) / (float)dd_desired_allocation (dd_max)) < 0.9)
15358             {
15359                 dprintf (GTC_LOG, ("%Id left in gen2 alloc (%Id)", 
15360                     dd_new_allocation (dd_max), dd_desired_allocation (dd_max)));
15361                 n = max_generation;
15362                 local_condemn_reasons->set_condition (gen_almost_max_alloc);
15363             }
15364         }
15365
15366         if (n <= max_generation)
15367         {
15368 #endif // BIT64
15369             if (high_fragmentation)
15370             {
15371                 //elevate to max_generation
15372                 n = max_generation;
15373                 dprintf (GTC_LOG, ("h%d: f full", heap_number));
15374
15375 #ifdef BACKGROUND_GC
15376                 if (high_memory_load || v_high_memory_load)
15377                 {
15378                     // For background GC we want to do blocking collections more eagerly because we don't
15379                     // want to get into the situation where the memory load becomes high while we are in
15380                     // a background GC and we'd have to wait for the background GC to finish to start
15381                     // a blocking collection (right now the implemenation doesn't handle converting 
15382                     // a background GC to a blocking collection midway.
15383                     dprintf (GTC_LOG, ("h%d: bgc - BLOCK", heap_number));
15384                     *blocking_collection_p = TRUE;
15385                 }
15386 #else
15387                 if (v_high_memory_load)
15388                 {
15389                     dprintf (GTC_LOG, ("h%d: - BLOCK", heap_number));
15390                     *blocking_collection_p = TRUE;
15391                 }
15392 #endif //BACKGROUND_GC
15393             }
15394             else
15395             {
15396                 n = max (n, max_generation - 1);
15397                 dprintf (GTC_LOG, ("h%d: nf c %d", heap_number, n));
15398             }
15399 #ifdef BIT64
15400         }
15401 #endif // BIT64
15402     }
15403
15404     if (!provisional_mode_triggered && (n == (max_generation - 1)) && (n_alloc < (max_generation -1)))
15405     {
15406         dprintf (GTC_LOG, ("h%d: budget %d, check 2",
15407                       heap_number, n_alloc));
15408         if (get_new_allocation (max_generation) <= 0)
15409         {
15410             dprintf (GTC_LOG, ("h%d: budget alloc", heap_number));
15411             n = max_generation;
15412             local_condemn_reasons->set_condition (gen_max_gen1);
15413         }
15414     }
15415
15416     //figure out if max_generation is too fragmented -> blocking collection
15417     if (!provisional_mode_triggered && (n == max_generation))
15418     {
15419         if (dt_high_frag_p (tuning_deciding_condemned_gen, n))
15420         {
15421             dprintf (GTC_LOG, ("h%d: g%d too frag", heap_number, n));
15422             local_condemn_reasons->set_condition (gen_max_high_frag_p);
15423             if (local_settings->pause_mode != pause_sustained_low_latency)
15424             {
15425                 *blocking_collection_p = TRUE;
15426             }
15427         }
15428     }
15429
15430 #ifdef BACKGROUND_GC
15431     if (n == max_generation)
15432     {
15433         if (heap_number == 0)
15434         {
15435             BOOL bgc_heap_too_small = TRUE;
15436             size_t gen2size = 0;
15437             size_t gen3size = 0;
15438 #ifdef MULTIPLE_HEAPS
15439             for (int i = 0; i < n_heaps; i++)
15440             {
15441                 if (((g_heaps[i]->current_generation_size (max_generation)) > bgc_min_per_heap) || 
15442                     ((g_heaps[i]->current_generation_size (max_generation + 1)) > bgc_min_per_heap))
15443                 {
15444                     bgc_heap_too_small = FALSE;
15445                     break;
15446                 }
15447             }
15448 #else //MULTIPLE_HEAPS
15449             if ((current_generation_size (max_generation) > bgc_min_per_heap) || 
15450                 (current_generation_size (max_generation + 1) > bgc_min_per_heap))
15451             {
15452                 bgc_heap_too_small = FALSE;
15453             }            
15454 #endif //MULTIPLE_HEAPS
15455
15456             if (bgc_heap_too_small)
15457             {
15458                 dprintf (GTC_LOG, ("gen2 and gen3 too small"));
15459
15460 #ifdef STRESS_HEAP
15461                 // do not turn stress-induced collections into blocking GCs
15462                 if (!settings.stress_induced)
15463 #endif //STRESS_HEAP
15464                 {
15465                     *blocking_collection_p = TRUE;
15466                 }
15467
15468                 local_condemn_reasons->set_condition (gen_gen2_too_small);
15469             }
15470         }
15471     }
15472 #endif //BACKGROUND_GC
15473
15474 exit:
15475     if (!check_only_p)
15476     {
15477 #ifdef STRESS_HEAP
15478 #ifdef BACKGROUND_GC
15479         // We can only do Concurrent GC Stress if the caller did not explicitly ask for all
15480         // generations to be collected,
15481
15482         if (orig_gen != max_generation &&
15483             g_pConfig->GetGCStressLevel() && gc_can_use_concurrent)
15484         {
15485             *elevation_requested_p = FALSE;
15486         }
15487 #endif //BACKGROUND_GC
15488 #endif //STRESS_HEAP
15489
15490         if (check_memory)
15491         {
15492             fgm_result.available_pagefile_mb = (size_t)(available_page_file / (1024 * 1024));
15493         }
15494
15495         local_condemn_reasons->set_gen (gen_final_per_heap, n);
15496         get_gc_data_per_heap()->gen_to_condemn_reasons.init (local_condemn_reasons);
15497
15498 #ifdef DT_LOG
15499         local_condemn_reasons->print (heap_number);
15500 #endif //DT_LOG
15501
15502         if ((local_settings->reason == reason_oos_soh) || 
15503             (local_settings->reason == reason_oos_loh))
15504         {
15505             assert (n >= 1);
15506         }
15507     }
15508
15509     return n;
15510 }
15511
15512 #ifdef _PREFAST_
15513 #pragma warning(pop)
15514 #endif //_PREFAST_
15515
15516 inline
15517 size_t gc_heap::min_reclaim_fragmentation_threshold (uint32_t num_heaps)
15518 {
15519     // if the memory load is higher, the threshold we'd want to collect gets lower.
15520     size_t min_mem_based_on_available = 
15521         (500 - (settings.entry_memory_load - high_memory_load_th) * 40) * 1024 * 1024 / num_heaps;
15522
15523     size_t ten_percent_size = (size_t)((float)generation_size (max_generation) * 0.10);
15524     uint64_t three_percent_mem = mem_one_percent * 3 / num_heaps;
15525
15526 #ifdef SIMPLE_DPRINTF
15527     dprintf (GTC_LOG, ("min av: %Id, 10%% gen2: %Id, 3%% mem: %I64d", 
15528         min_mem_based_on_available, ten_percent_size, three_percent_mem));
15529 #endif //SIMPLE_DPRINTF
15530     return (size_t)(min (min_mem_based_on_available, min (ten_percent_size, three_percent_mem)));
15531 }
15532
15533 inline
15534 uint64_t gc_heap::min_high_fragmentation_threshold(uint64_t available_mem, uint32_t num_heaps)
15535 {
15536     return min (available_mem, (256*1024*1024)) / num_heaps;
15537 }
15538
15539 enum {
15540 CORINFO_EXCEPTION_GC = 0xE0004743 // 'GC'
15541 };
15542
15543
15544 #ifdef BACKGROUND_GC
15545 void gc_heap::init_background_gc ()
15546 {
15547     //reset the allocation so foreground gc can allocate into older (max_generation) generation
15548     generation* gen = generation_of (max_generation);
15549     generation_allocation_pointer (gen)= 0;
15550     generation_allocation_limit (gen) = 0;
15551     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
15552
15553     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
15554
15555     //reset the plan allocation for each segment
15556     for (heap_segment* seg = generation_allocation_segment (gen); seg != ephemeral_heap_segment;
15557         seg = heap_segment_next_rw (seg))
15558     {
15559         heap_segment_plan_allocated (seg) = heap_segment_allocated (seg);
15560     }
15561
15562     if (heap_number == 0)
15563     {
15564         dprintf (2, ("heap%d: bgc lowest: %Ix, highest: %Ix", 
15565             heap_number,
15566             background_saved_lowest_address, 
15567             background_saved_highest_address));
15568     }
15569
15570     gc_lh_block_event.Reset();
15571 }
15572
15573 #endif //BACKGROUND_GC
15574
15575 inline
15576 void fire_drain_mark_list_event (size_t mark_list_objects)
15577 {
15578     FIRE_EVENT(BGCDrainMark, mark_list_objects);
15579 }
15580
15581 inline
15582 void fire_revisit_event (size_t dirtied_pages, 
15583                          size_t marked_objects,
15584                          BOOL large_objects_p)
15585 {
15586     FIRE_EVENT(BGCRevisit, dirtied_pages, marked_objects, large_objects_p);
15587 }
15588
15589 inline
15590 void fire_overflow_event (uint8_t* overflow_min,
15591                           uint8_t* overflow_max,
15592                           size_t marked_objects, 
15593                           int large_objects_p)
15594 {
15595     FIRE_EVENT(BGCOverflow, (uint64_t)overflow_min, (uint64_t)overflow_max, marked_objects, large_objects_p);
15596 }
15597
15598 void gc_heap::concurrent_print_time_delta (const char* msg)
15599 {
15600 #ifdef TRACE_GC
15601     size_t current_time = GetHighPrecisionTimeStamp();
15602     size_t elapsed_time = current_time - time_bgc_last;
15603     time_bgc_last = current_time;
15604
15605     dprintf (2, ("h%d: %s T %Id ms", heap_number, msg, elapsed_time));
15606 #else
15607     UNREFERENCED_PARAMETER(msg);
15608 #endif //TRACE_GC
15609 }
15610
15611 void gc_heap::free_list_info (int gen_num, const char* msg)
15612 {
15613     UNREFERENCED_PARAMETER(gen_num);
15614 #if defined (BACKGROUND_GC) && defined (TRACE_GC)
15615     dprintf (3, ("h%d: %s", heap_number, msg));
15616     for (int i = 0; i <= (max_generation + 1); i++)
15617     {
15618         generation* gen = generation_of (i);
15619         if ((generation_allocation_size (gen) == 0) && 
15620             (generation_free_list_space (gen) == 0) && 
15621             (generation_free_obj_space (gen) == 0))
15622         {
15623             // don't print if everything is 0.
15624         }
15625         else
15626         {
15627             dprintf (3, ("h%d: g%d: a-%Id, fl-%Id, fo-%Id",
15628                 heap_number, i, 
15629                 generation_allocation_size (gen), 
15630                 generation_free_list_space (gen), 
15631                 generation_free_obj_space (gen)));
15632         }
15633     }
15634 #else
15635     UNREFERENCED_PARAMETER(msg);
15636 #endif // BACKGROUND_GC && TRACE_GC
15637 }
15638
15639 void gc_heap::update_collection_counts_for_no_gc()
15640 {
15641     assert (settings.pause_mode == pause_no_gc);
15642
15643     settings.condemned_generation = max_generation;
15644 #ifdef MULTIPLE_HEAPS
15645     for (int i = 0; i < n_heaps; i++)
15646         g_heaps[i]->update_collection_counts();
15647 #else //MULTIPLE_HEAPS
15648     update_collection_counts();
15649 #endif //MULTIPLE_HEAPS
15650
15651     full_gc_counts[gc_type_blocking]++;
15652 }
15653
15654 BOOL gc_heap::should_proceed_with_gc()
15655 {
15656     if (gc_heap::settings.pause_mode == pause_no_gc)
15657     {
15658         if (current_no_gc_region_info.started)
15659         {
15660             // The no_gc mode was already in progress yet we triggered another GC,
15661             // this effectively exits the no_gc mode.
15662             restore_data_for_no_gc();
15663         }
15664         else
15665             return should_proceed_for_no_gc();
15666     }
15667
15668     return TRUE;
15669 }
15670
15671 //internal part of gc used by the serial and concurrent version
15672 void gc_heap::gc1()
15673 {
15674 #ifdef BACKGROUND_GC
15675     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15676 #endif //BACKGROUND_GC
15677
15678 #ifdef TIME_GC
15679     mark_time = plan_time = reloc_time = compact_time = sweep_time = 0;
15680 #endif //TIME_GC
15681
15682     verify_soh_segment_list();
15683
15684     int n = settings.condemned_generation;
15685
15686     if (settings.reason == reason_pm_full_gc)
15687     {
15688         assert (n == max_generation);
15689         init_records();
15690
15691         gen_to_condemn_tuning* local_condemn_reasons = &(get_gc_data_per_heap()->gen_to_condemn_reasons);
15692         local_condemn_reasons->init();
15693         local_condemn_reasons->set_gen (gen_initial, n);
15694         local_condemn_reasons->set_gen (gen_final_per_heap, n);
15695     }
15696
15697     update_collection_counts ();
15698
15699 #ifdef BACKGROUND_GC
15700     bgc_alloc_lock->check();
15701 #endif //BACKGROUND_GC
15702
15703     free_list_info (max_generation, "beginning");
15704
15705     vm_heap->GcCondemnedGeneration = settings.condemned_generation;
15706
15707     assert (g_gc_card_table == card_table);
15708
15709 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
15710     assert (g_gc_card_bundle_table == card_bundle_table);
15711 #endif    
15712
15713     {
15714         if (n == max_generation)
15715         {
15716             gc_low = lowest_address;
15717             gc_high = highest_address;
15718         }
15719         else
15720         {
15721             gc_low = generation_allocation_start (generation_of (n));
15722             gc_high = heap_segment_reserved (ephemeral_heap_segment);
15723         }   
15724 #ifdef BACKGROUND_GC
15725         if (settings.concurrent)
15726         {
15727 #ifdef TRACE_GC
15728             time_bgc_last = GetHighPrecisionTimeStamp();
15729 #endif //TRACE_GC
15730
15731             FIRE_EVENT(BGCBegin);
15732
15733             concurrent_print_time_delta ("BGC");
15734
15735 //#ifdef WRITE_WATCH
15736             //reset_write_watch (FALSE);
15737 //#endif //WRITE_WATCH
15738
15739             concurrent_print_time_delta ("RW");
15740             background_mark_phase();
15741             free_list_info (max_generation, "after mark phase");
15742             
15743             background_sweep();
15744             free_list_info (max_generation, "after sweep phase");
15745         }
15746         else
15747 #endif //BACKGROUND_GC
15748         {
15749             mark_phase (n, FALSE);
15750
15751             GCScan::GcRuntimeStructuresValid (FALSE);
15752             plan_phase (n);
15753             GCScan::GcRuntimeStructuresValid (TRUE);
15754         }
15755     }
15756
15757     size_t end_gc_time = GetHighPrecisionTimeStamp();
15758 //    printf ("generation: %d, elapsed time: %Id\n", n,  end_gc_time - dd_time_clock (dynamic_data_of (0)));
15759
15760     //adjust the allocation size from the pinned quantities. 
15761     for (int gen_number = 0; gen_number <= min (max_generation,n+1); gen_number++)
15762     {
15763         generation* gn = generation_of (gen_number);
15764         if (settings.compaction)
15765         {
15766             generation_pinned_allocated (gn) += generation_pinned_allocation_compact_size (gn);
15767             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_compact_size (gn);
15768         }
15769         else
15770         {
15771             generation_pinned_allocated (gn) += generation_pinned_allocation_sweep_size (gn);
15772             generation_allocation_size (generation_of (gen_number)) += generation_pinned_allocation_sweep_size (gn);
15773         }
15774         generation_pinned_allocation_sweep_size (gn) = 0;
15775         generation_pinned_allocation_compact_size (gn) = 0;
15776     }
15777
15778 #ifdef BACKGROUND_GC
15779     if (settings.concurrent)
15780     {
15781         dynamic_data* dd = dynamic_data_of (n);
15782         dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15783
15784         free_list_info (max_generation, "after computing new dynamic data");
15785
15786         gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
15787
15788         for (int gen_number = 0; gen_number < max_generation; gen_number++)
15789         {
15790             dprintf (2, ("end of BGC: gen%d new_alloc: %Id", 
15791                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15792             current_gc_data_per_heap->gen_data[gen_number].size_after = generation_size (gen_number);
15793             current_gc_data_per_heap->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15794             current_gc_data_per_heap->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15795         }
15796     }
15797     else
15798 #endif //BACKGROUND_GC
15799     {
15800         free_list_info (max_generation, "end");
15801         for (int gen_number = 0; gen_number <= n; gen_number++)
15802         {
15803             dynamic_data* dd = dynamic_data_of (gen_number);
15804             dd_gc_elapsed_time (dd) = end_gc_time - dd_time_clock (dd);
15805             compute_new_dynamic_data (gen_number);
15806         }
15807
15808         if (n != max_generation)
15809         {
15810             int gen_num_for_data = ((n < (max_generation - 1)) ? (n + 1) : (max_generation + 1));
15811             for (int gen_number = (n + 1); gen_number <= gen_num_for_data; gen_number++)
15812             {
15813                 get_gc_data_per_heap()->gen_data[gen_number].size_after = generation_size (gen_number);
15814                 get_gc_data_per_heap()->gen_data[gen_number].free_list_space_after = generation_free_list_space (generation_of (gen_number));
15815                 get_gc_data_per_heap()->gen_data[gen_number].free_obj_space_after = generation_free_obj_space (generation_of (gen_number));
15816             }
15817         }
15818
15819         get_gc_data_per_heap()->maxgen_size_info.running_free_list_efficiency = (uint32_t)(generation_allocator_efficiency (generation_of (max_generation)) * 100);
15820
15821         free_list_info (max_generation, "after computing new dynamic data");
15822         
15823         if (heap_number == 0)
15824         {
15825             dprintf (GTC_LOG, ("GC#%d(gen%d) took %Idms", 
15826                 dd_collection_count (dynamic_data_of (0)), 
15827                 settings.condemned_generation,
15828                 dd_gc_elapsed_time (dynamic_data_of (0))));
15829         }
15830
15831         for (int gen_number = 0; gen_number <= (max_generation + 1); gen_number++)
15832         {
15833             dprintf (2, ("end of FGC/NGC: gen%d new_alloc: %Id", 
15834                          gen_number, dd_desired_allocation (dynamic_data_of (gen_number))));
15835         }
15836     }
15837
15838     if (n < max_generation)
15839     {
15840         compute_promoted_allocation (1 + n);
15841
15842         dynamic_data* dd = dynamic_data_of (1 + n);
15843         size_t new_fragmentation = generation_free_list_space (generation_of (1 + n)) + 
15844                                    generation_free_obj_space (generation_of (1 + n));
15845
15846 #ifdef BACKGROUND_GC
15847         if (current_c_gc_state != c_gc_state_planning)
15848 #endif //BACKGROUND_GC
15849         {
15850             if (settings.promotion)
15851             {
15852                 dd_fragmentation (dd) = new_fragmentation;
15853             }
15854             else
15855             {
15856                 //assert (dd_fragmentation (dd) == new_fragmentation);
15857             }
15858         }
15859     }
15860
15861 #ifdef BACKGROUND_GC
15862     if (!settings.concurrent)
15863 #endif //BACKGROUND_GC
15864     {
15865 #ifndef FEATURE_REDHAWK
15866         // GCToEEInterface::IsGCThread() always returns false on CoreRT, but this assert is useful in CoreCLR.
15867         assert(GCToEEInterface::IsGCThread());
15868 #endif // FEATURE_REDHAWK
15869         adjust_ephemeral_limits();
15870     }
15871
15872 #ifdef BACKGROUND_GC
15873     assert (ephemeral_low == generation_allocation_start (generation_of ( max_generation -1)));
15874     assert (ephemeral_high == heap_segment_reserved (ephemeral_heap_segment));
15875 #endif //BACKGROUND_GC
15876
15877     if (fgn_maxgen_percent)
15878     {
15879         if (settings.condemned_generation == (max_generation - 1))
15880         {
15881             check_for_full_gc (max_generation - 1, 0);
15882         }
15883         else if (settings.condemned_generation == max_generation)
15884         {
15885             if (full_gc_approach_event_set 
15886 #ifdef MULTIPLE_HEAPS
15887                 && (heap_number == 0)
15888 #endif //MULTIPLE_HEAPS
15889                 )
15890             {
15891                 dprintf (2, ("FGN-GC: setting gen2 end event"));
15892
15893                 full_gc_approach_event.Reset();
15894 #ifdef BACKGROUND_GC
15895                 // By definition WaitForFullGCComplete only succeeds if it's full, *blocking* GC, otherwise need to return N/A
15896                 fgn_last_gc_was_concurrent = settings.concurrent ? TRUE : FALSE;
15897 #endif //BACKGROUND_GC
15898                 full_gc_end_event.Set();
15899                 full_gc_approach_event_set = false;            
15900             }
15901         }
15902     }
15903
15904 #ifdef BACKGROUND_GC
15905     if (!settings.concurrent)
15906 #endif //BACKGROUND_GC
15907     {
15908         //decide on the next allocation quantum
15909         if (alloc_contexts_used >= 1)
15910         {
15911             allocation_quantum = Align (min ((size_t)CLR_SIZE,
15912                                             (size_t)max (1024, get_new_allocation (0) / (2 * alloc_contexts_used))),
15913                                             get_alignment_constant(FALSE));
15914             dprintf (3, ("New allocation quantum: %d(0x%Ix)", allocation_quantum, allocation_quantum));
15915         }
15916     }
15917
15918     descr_generations (FALSE);
15919
15920     verify_soh_segment_list();
15921
15922 #ifdef BACKGROUND_GC
15923     add_to_history_per_heap();
15924     if (heap_number == 0)
15925     {
15926         add_to_history();
15927     }
15928 #endif // BACKGROUND_GC
15929
15930 #ifdef GC_STATS
15931     if (GCStatistics::Enabled() && heap_number == 0)
15932         g_GCStatistics.AddGCStats(settings, 
15933             dd_gc_elapsed_time(dynamic_data_of(settings.condemned_generation)));
15934 #endif // GC_STATS
15935
15936 #ifdef TIME_GC
15937     fprintf (stdout, "%d,%d,%d,%d,%d,%d\n",
15938              n, mark_time, plan_time, reloc_time, compact_time, sweep_time);
15939 #endif //TIME_GC
15940
15941 #ifdef BACKGROUND_GC
15942     assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15943 #endif //BACKGROUND_GC
15944
15945 #if defined(VERIFY_HEAP) || (defined (FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
15946     if (FALSE 
15947 #ifdef VERIFY_HEAP
15948         // Note that right now g_pConfig->GetHeapVerifyLevel always returns the same
15949         // value. If we ever allow randomly adjusting this as the process runs,
15950         // we cannot call it this way as joins need to match - we must have the same
15951         // value for all heaps like we do with bgc_heap_walk_for_etw_p.
15952         || (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
15953 #endif
15954 #if defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC)
15955         || (bgc_heap_walk_for_etw_p && settings.concurrent)
15956 #endif
15957         )
15958     {
15959 #ifdef BACKGROUND_GC
15960         bool cooperative_mode = true;
15961
15962         if (settings.concurrent)
15963         {
15964             cooperative_mode = enable_preemptive ();
15965
15966 #ifdef MULTIPLE_HEAPS
15967             bgc_t_join.join(this, gc_join_suspend_ee_verify);
15968             if (bgc_t_join.joined())
15969             {
15970                 bgc_threads_sync_event.Reset();
15971
15972                 dprintf(2, ("Joining BGC threads to suspend EE for verify heap"));
15973                 bgc_t_join.restart();
15974             }
15975             if (heap_number == 0)
15976             {
15977                 suspend_EE();
15978                 bgc_threads_sync_event.Set();
15979             }
15980             else
15981             {
15982                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
15983                 dprintf (2, ("bgc_threads_sync_event is signalled"));
15984             }
15985 #else
15986             suspend_EE();
15987 #endif //MULTIPLE_HEAPS
15988
15989             //fix the allocation area so verify_heap can proceed.
15990             fix_allocation_contexts (FALSE);
15991         }
15992 #endif //BACKGROUND_GC
15993
15994 #ifdef BACKGROUND_GC
15995         assert (settings.concurrent == (uint32_t)(bgc_thread_id.IsCurrentThread()));
15996 #ifdef FEATURE_EVENT_TRACE
15997         if (bgc_heap_walk_for_etw_p && settings.concurrent)
15998         {
15999             GCToEEInterface::DiagWalkBGCSurvivors(__this);
16000
16001 #ifdef MULTIPLE_HEAPS
16002             bgc_t_join.join(this, gc_join_after_profiler_heap_walk);
16003             if (bgc_t_join.joined())
16004             {
16005                 bgc_t_join.restart();
16006             }
16007 #endif // MULTIPLE_HEAPS
16008         }
16009 #endif // FEATURE_EVENT_TRACE
16010 #endif //BACKGROUND_GC
16011
16012 #ifdef VERIFY_HEAP
16013         if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
16014             verify_heap (FALSE);
16015 #endif // VERIFY_HEAP
16016
16017 #ifdef BACKGROUND_GC
16018         if (settings.concurrent)
16019         {
16020             repair_allocation_contexts (TRUE);
16021
16022 #ifdef MULTIPLE_HEAPS
16023             bgc_t_join.join(this, gc_join_restart_ee_verify);
16024             if (bgc_t_join.joined())
16025             {
16026                 bgc_threads_sync_event.Reset();
16027
16028                 dprintf(2, ("Joining BGC threads to restart EE after verify heap"));
16029                 bgc_t_join.restart();
16030             }
16031             if (heap_number == 0)
16032             {
16033                 restart_EE();
16034                 bgc_threads_sync_event.Set();
16035             }
16036             else
16037             {
16038                 bgc_threads_sync_event.Wait(INFINITE, FALSE);
16039                 dprintf (2, ("bgc_threads_sync_event is signalled"));
16040             }
16041 #else
16042             restart_EE();
16043 #endif //MULTIPLE_HEAPS
16044
16045             disable_preemptive (cooperative_mode);
16046         }
16047 #endif //BACKGROUND_GC
16048     }
16049 #endif // defined(VERIFY_HEAP) || (defined(FEATURE_EVENT_TRACE) && defined(BACKGROUND_GC))
16050
16051 #ifdef MULTIPLE_HEAPS
16052     if (!settings.concurrent)
16053     {
16054         gc_t_join.join(this, gc_join_done);
16055         if (gc_t_join.joined ())
16056         {
16057             gc_heap::internal_gc_done = false;
16058
16059             //equalize the new desired size of the generations
16060             int limit = settings.condemned_generation;
16061             if (limit == max_generation)
16062             {
16063                 limit = max_generation+1;
16064             }
16065             for (int gen = 0; gen <= limit; gen++)
16066             {
16067                 size_t total_desired = 0;
16068
16069                 for (int i = 0; i < gc_heap::n_heaps; i++)
16070                 {
16071                     gc_heap* hp = gc_heap::g_heaps[i];
16072                     dynamic_data* dd = hp->dynamic_data_of (gen);
16073                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
16074                     if (temp_total_desired < total_desired)
16075                     {
16076                         // we overflowed.
16077                         total_desired = (size_t)MAX_PTR;
16078                         break;
16079                     }
16080                     total_desired = temp_total_desired;
16081                 }
16082
16083                 size_t desired_per_heap = Align (total_desired/gc_heap::n_heaps,
16084                                                     get_alignment_constant ((gen != (max_generation+1))));
16085
16086                 if (gen == 0)
16087                 {
16088 #if 1 //subsumed by the linear allocation model 
16089                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
16090                     // apply some smoothing.
16091                     static size_t smoothed_desired_per_heap = 0;
16092                     size_t smoothing = 3; // exponential smoothing factor
16093                     if (smoothing  > VolatileLoad(&settings.gc_index))
16094                         smoothing  = VolatileLoad(&settings.gc_index);
16095                     smoothed_desired_per_heap = desired_per_heap / smoothing + ((smoothed_desired_per_heap / smoothing) * (smoothing-1));
16096                     dprintf (1, ("sn = %Id  n = %Id", smoothed_desired_per_heap, desired_per_heap));
16097                     desired_per_heap = Align(smoothed_desired_per_heap, get_alignment_constant (true));
16098 #endif //0
16099
16100                     if (!heap_hard_limit)
16101                     {
16102                         // if desired_per_heap is close to min_gc_size, trim it
16103                         // down to min_gc_size to stay in the cache
16104                         gc_heap* hp = gc_heap::g_heaps[0];
16105                         dynamic_data* dd = hp->dynamic_data_of (gen);
16106                         size_t min_gc_size = dd_min_size(dd);
16107                         // if min GC size larger than true on die cache, then don't bother
16108                         // limiting the desired size
16109                         if ((min_gc_size <= GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)) &&
16110                             desired_per_heap <= 2*min_gc_size)
16111                         {
16112                             desired_per_heap = min_gc_size;
16113                         }
16114                     }
16115 #ifdef BIT64
16116                     desired_per_heap = joined_youngest_desired (desired_per_heap);
16117                     dprintf (2, ("final gen0 new_alloc: %Id", desired_per_heap));
16118 #endif // BIT64
16119                     gc_data_global.final_youngest_desired = desired_per_heap;
16120                 }
16121 #if 1 //subsumed by the linear allocation model 
16122                 if (gen == (max_generation + 1))
16123                 {
16124                     // to avoid spikes in mem usage due to short terms fluctuations in survivorship,
16125                     // apply some smoothing.
16126                     static size_t smoothed_desired_per_heap_loh = 0;
16127                     size_t smoothing = 3; // exponential smoothing factor
16128                     size_t loh_count = dd_collection_count (dynamic_data_of (max_generation));
16129                     if (smoothing  > loh_count)
16130                         smoothing  = loh_count;
16131                     smoothed_desired_per_heap_loh = desired_per_heap / smoothing + ((smoothed_desired_per_heap_loh / smoothing) * (smoothing-1));
16132                     dprintf (2, ("smoothed_desired_per_heap_loh  = %Id  desired_per_heap = %Id", smoothed_desired_per_heap_loh, desired_per_heap));
16133                     desired_per_heap = Align(smoothed_desired_per_heap_loh, get_alignment_constant (false));
16134                 }
16135 #endif //0
16136                 for (int i = 0; i < gc_heap::n_heaps; i++)
16137                 {
16138                     gc_heap* hp = gc_heap::g_heaps[i];
16139                     dynamic_data* dd = hp->dynamic_data_of (gen);
16140                     dd_desired_allocation (dd) = desired_per_heap;
16141                     dd_gc_new_allocation (dd) = desired_per_heap;
16142                     dd_new_allocation (dd) = desired_per_heap;
16143
16144                     if (gen == 0)
16145                     {
16146                         hp->fgn_last_alloc = desired_per_heap;
16147                     }
16148                 }
16149             }
16150
16151 #ifdef FEATURE_LOH_COMPACTION
16152             BOOL all_heaps_compacted_p = TRUE;
16153 #endif //FEATURE_LOH_COMPACTION
16154             for (int i = 0; i < gc_heap::n_heaps; i++)
16155             {
16156                 gc_heap* hp = gc_heap::g_heaps[i];
16157                 hp->decommit_ephemeral_segment_pages();
16158                 hp->rearrange_large_heap_segments();
16159 #ifdef FEATURE_LOH_COMPACTION
16160                 all_heaps_compacted_p &= hp->loh_compacted_p;
16161 #endif //FEATURE_LOH_COMPACTION
16162             }
16163
16164 #ifdef FEATURE_LOH_COMPACTION
16165             check_loh_compact_mode (all_heaps_compacted_p);
16166 #endif //FEATURE_LOH_COMPACTION
16167
16168             fire_pevents();
16169             pm_full_gc_init_or_clear();
16170
16171             gc_t_join.restart();
16172         }
16173         alloc_context_count = 0;
16174         heap_select::mark_heap (heap_number);
16175     }
16176
16177 #else
16178     gc_data_global.final_youngest_desired = 
16179         dd_desired_allocation (dynamic_data_of (0));
16180
16181     check_loh_compact_mode (loh_compacted_p);
16182
16183     decommit_ephemeral_segment_pages();
16184     fire_pevents();
16185
16186     if (!(settings.concurrent))
16187     {
16188         rearrange_large_heap_segments();
16189         do_post_gc();
16190     }
16191
16192     pm_full_gc_init_or_clear();
16193
16194 #ifdef BACKGROUND_GC
16195     recover_bgc_settings();
16196 #endif //BACKGROUND_GC
16197 #endif //MULTIPLE_HEAPS
16198 }
16199
16200 void gc_heap::save_data_for_no_gc()
16201 {
16202     current_no_gc_region_info.saved_pause_mode = settings.pause_mode;
16203 #ifdef MULTIPLE_HEAPS
16204     // This is to affect heap balancing. 
16205     for (int i = 0; i < n_heaps; i++)
16206     {
16207         current_no_gc_region_info.saved_gen0_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (0));
16208         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = min_balance_threshold;
16209         current_no_gc_region_info.saved_gen3_min_size = dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1));
16210         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = 0;
16211     }
16212 #endif //MULTIPLE_HEAPS
16213 }
16214
16215 void gc_heap::restore_data_for_no_gc()
16216 {
16217     gc_heap::settings.pause_mode = current_no_gc_region_info.saved_pause_mode;
16218 #ifdef MULTIPLE_HEAPS
16219     for (int i = 0; i < n_heaps; i++)
16220     {
16221         dd_min_size (g_heaps[i]->dynamic_data_of (0)) = current_no_gc_region_info.saved_gen0_min_size;
16222         dd_min_size (g_heaps[i]->dynamic_data_of (max_generation + 1)) = current_no_gc_region_info.saved_gen3_min_size;
16223     }
16224 #endif //MULTIPLE_HEAPS
16225 }
16226
16227 start_no_gc_region_status gc_heap::prepare_for_no_gc_region (uint64_t total_size,
16228                                                              BOOL loh_size_known, 
16229                                                              uint64_t loh_size,
16230                                                              BOOL disallow_full_blocking)
16231 {
16232     if (current_no_gc_region_info.started)
16233     {
16234         return start_no_gc_in_progress;
16235     }
16236
16237     start_no_gc_region_status status = start_no_gc_success;
16238
16239     save_data_for_no_gc();
16240     settings.pause_mode = pause_no_gc;
16241     current_no_gc_region_info.start_status = start_no_gc_success;
16242
16243     uint64_t allocation_no_gc_loh = 0;
16244     uint64_t allocation_no_gc_soh = 0;
16245     assert(total_size != 0);
16246     if (loh_size_known)
16247     {
16248         assert(loh_size != 0);
16249         assert(loh_size <= total_size);
16250         allocation_no_gc_loh = loh_size;
16251         allocation_no_gc_soh = total_size - loh_size;
16252     }
16253     else
16254     {
16255         allocation_no_gc_soh = total_size;
16256         allocation_no_gc_loh = total_size;
16257     }
16258
16259     int soh_align_const = get_alignment_constant (TRUE);
16260     size_t max_soh_allocated = soh_segment_size - segment_info_size - eph_gen_starts_size;
16261     size_t size_per_heap = 0;
16262     const double scale_factor = 1.05;
16263
16264     int num_heaps = 1;
16265 #ifdef MULTIPLE_HEAPS
16266     num_heaps = n_heaps;
16267 #endif // MULTIPLE_HEAPS
16268
16269     uint64_t total_allowed_soh_allocation = max_soh_allocated * num_heaps;
16270     // [LOCALGC TODO]
16271     // In theory, the upper limit here is the physical memory of the machine, not
16272     // SIZE_T_MAX. This is not true today because total_physical_mem can be
16273     // larger than SIZE_T_MAX if running in wow64 on a machine with more than
16274     // 4GB of RAM. Once Local GC code divergence is resolved and code is flowing
16275     // more freely between branches, it would be good to clean this up to use
16276     // total_physical_mem instead of SIZE_T_MAX.
16277     assert(total_allowed_soh_allocation <= SIZE_T_MAX);
16278     uint64_t total_allowed_loh_allocation = SIZE_T_MAX;
16279     uint64_t total_allowed_soh_alloc_scaled = allocation_no_gc_soh > 0 ? static_cast<uint64_t>(total_allowed_soh_allocation / scale_factor) : 0;
16280     uint64_t total_allowed_loh_alloc_scaled = allocation_no_gc_loh > 0 ? static_cast<uint64_t>(total_allowed_loh_allocation / scale_factor) : 0;
16281
16282     if (allocation_no_gc_soh > total_allowed_soh_alloc_scaled ||
16283         allocation_no_gc_loh > total_allowed_loh_alloc_scaled)
16284     {
16285         status = start_no_gc_too_large;
16286         goto done;
16287     }
16288
16289     if (allocation_no_gc_soh > 0)
16290     {
16291         allocation_no_gc_soh = static_cast<uint64_t>(allocation_no_gc_soh * scale_factor);
16292         allocation_no_gc_soh = min (allocation_no_gc_soh, total_allowed_soh_alloc_scaled);
16293     }
16294
16295     if (allocation_no_gc_loh > 0)
16296     {
16297         allocation_no_gc_loh = static_cast<uint64_t>(allocation_no_gc_loh * scale_factor);
16298         allocation_no_gc_loh = min (allocation_no_gc_loh, total_allowed_loh_alloc_scaled);
16299     }
16300
16301     if (disallow_full_blocking)
16302         current_no_gc_region_info.minimal_gc_p = TRUE;
16303
16304     if (allocation_no_gc_soh != 0)
16305     {
16306         current_no_gc_region_info.soh_allocation_size = static_cast<size_t>(allocation_no_gc_soh);
16307         size_per_heap = current_no_gc_region_info.soh_allocation_size;
16308 #ifdef MULTIPLE_HEAPS
16309         size_per_heap /= n_heaps;
16310         for (int i = 0; i < n_heaps; i++)
16311         {
16312             // due to heap balancing we need to allow some room before we even look to balance to another heap.
16313             g_heaps[i]->soh_allocation_no_gc = min (Align ((size_per_heap + min_balance_threshold), soh_align_const), max_soh_allocated);
16314         }
16315 #else //MULTIPLE_HEAPS
16316         soh_allocation_no_gc = min (Align (size_per_heap, soh_align_const), max_soh_allocated);
16317 #endif //MULTIPLE_HEAPS
16318     }
16319
16320     if (allocation_no_gc_loh != 0)
16321     {
16322         current_no_gc_region_info.loh_allocation_size = static_cast<size_t>(allocation_no_gc_loh);
16323         size_per_heap = current_no_gc_region_info.loh_allocation_size;
16324 #ifdef MULTIPLE_HEAPS
16325         size_per_heap /= n_heaps;
16326         for (int i = 0; i < n_heaps; i++)
16327             g_heaps[i]->loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
16328 #else //MULTIPLE_HEAPS
16329         loh_allocation_no_gc = Align (size_per_heap, get_alignment_constant (FALSE));
16330 #endif //MULTIPLE_HEAPS
16331     }
16332
16333 done:
16334     if (status != start_no_gc_success)
16335         restore_data_for_no_gc();
16336     return status;
16337 }
16338
16339 void gc_heap::handle_failure_for_no_gc()
16340 {
16341     gc_heap::restore_data_for_no_gc();
16342     // sets current_no_gc_region_info.started to FALSE here.
16343     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
16344 }
16345
16346 start_no_gc_region_status gc_heap::get_start_no_gc_region_status()
16347 {
16348     return current_no_gc_region_info.start_status;
16349 }
16350
16351 void gc_heap::record_gcs_during_no_gc()
16352 {
16353     if (current_no_gc_region_info.started)
16354     {
16355         current_no_gc_region_info.num_gcs++;
16356         if (is_induced (settings.reason))
16357             current_no_gc_region_info.num_gcs_induced++;
16358     }
16359 }
16360
16361 BOOL gc_heap::find_loh_free_for_no_gc()
16362 {
16363     allocator* loh_allocator = generation_allocator (generation_of (max_generation + 1));
16364     size_t sz_list = loh_allocator->first_bucket_size();
16365     size_t size = loh_allocation_no_gc;
16366     for (unsigned int a_l_idx = 0; a_l_idx < loh_allocator->number_of_buckets(); a_l_idx++)
16367     {
16368         if ((size < sz_list) || (a_l_idx == (loh_allocator->number_of_buckets()-1)))
16369         {
16370             uint8_t* free_list = loh_allocator->alloc_list_head_of (a_l_idx);
16371             while (free_list)
16372             {
16373                 size_t free_list_size = unused_array_size(free_list);
16374
16375                 if (free_list_size > loh_allocation_no_gc)
16376                 {
16377                     dprintf (3, ("free item %Ix(%Id) for no gc", (size_t)free_list, free_list_size));
16378                     return TRUE;
16379                 }
16380
16381                 free_list = free_list_slot (free_list); 
16382             }
16383         }
16384         sz_list = sz_list * 2;
16385     }
16386
16387     return FALSE;
16388 }
16389
16390 BOOL gc_heap::find_loh_space_for_no_gc()
16391 {
16392     saved_loh_segment_no_gc = 0;
16393
16394     if (find_loh_free_for_no_gc())
16395         return TRUE;
16396
16397     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16398
16399     while (seg)
16400     {
16401         size_t remaining = heap_segment_reserved (seg) - heap_segment_allocated (seg);
16402         if (remaining >= loh_allocation_no_gc)
16403         {
16404             saved_loh_segment_no_gc = seg;
16405             break;
16406         }
16407         seg = heap_segment_next (seg);
16408     }
16409
16410     if (!saved_loh_segment_no_gc && current_no_gc_region_info.minimal_gc_p)
16411     {
16412         // If no full GC is allowed, we try to get a new seg right away.
16413         saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc)
16414 #ifdef MULTIPLE_HEAPS
16415                                                       , this
16416 #endif //MULTIPLE_HEAPS
16417                                                       );
16418     }
16419
16420     return (saved_loh_segment_no_gc != 0);
16421 }
16422
16423 BOOL gc_heap::loh_allocated_for_no_gc()
16424 {
16425     if (!saved_loh_segment_no_gc)
16426         return FALSE;
16427
16428     heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16429     do 
16430     {
16431         if (seg == saved_loh_segment_no_gc)
16432         {
16433             return FALSE;
16434         }
16435         seg = heap_segment_next (seg);
16436     } while (seg);
16437
16438     return TRUE;
16439 }
16440
16441 BOOL gc_heap::commit_loh_for_no_gc (heap_segment* seg)
16442 {
16443     uint8_t* end_committed = heap_segment_allocated (seg) + loh_allocation_no_gc;
16444     assert (end_committed <= heap_segment_reserved (seg));
16445     return (grow_heap_segment (seg, end_committed));
16446 }
16447
16448 void gc_heap::thread_no_gc_loh_segments()
16449 {
16450 #ifdef MULTIPLE_HEAPS
16451     for (int i = 0; i < n_heaps; i++)
16452     {
16453         gc_heap* hp = g_heaps[i];
16454         if (hp->loh_allocated_for_no_gc())
16455         {
16456             hp->thread_loh_segment (hp->saved_loh_segment_no_gc);
16457             hp->saved_loh_segment_no_gc = 0;
16458         }
16459     }
16460 #else //MULTIPLE_HEAPS
16461     if (loh_allocated_for_no_gc())
16462     {
16463         thread_loh_segment (saved_loh_segment_no_gc);
16464         saved_loh_segment_no_gc = 0;
16465     }
16466 #endif //MULTIPLE_HEAPS    
16467 }
16468
16469 void gc_heap::set_loh_allocations_for_no_gc()
16470 {
16471     if (current_no_gc_region_info.loh_allocation_size != 0)
16472     {
16473         dynamic_data* dd = dynamic_data_of (max_generation + 1);
16474         dd_new_allocation (dd) = loh_allocation_no_gc;
16475         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16476     }
16477 }
16478
16479 void gc_heap::set_soh_allocations_for_no_gc()
16480 {
16481     if (current_no_gc_region_info.soh_allocation_size != 0)
16482     {
16483         dynamic_data* dd = dynamic_data_of (0);
16484         dd_new_allocation (dd) = soh_allocation_no_gc;
16485         dd_gc_new_allocation (dd) = dd_new_allocation (dd);
16486 #ifdef MULTIPLE_HEAPS
16487         alloc_context_count = 0;
16488 #endif //MULTIPLE_HEAPS
16489     }
16490 }
16491
16492 void gc_heap::set_allocations_for_no_gc()
16493 {
16494 #ifdef MULTIPLE_HEAPS
16495     for (int i = 0; i < n_heaps; i++)
16496     {
16497         gc_heap* hp = g_heaps[i];
16498         hp->set_loh_allocations_for_no_gc();
16499         hp->set_soh_allocations_for_no_gc();
16500     }
16501 #else //MULTIPLE_HEAPS
16502     set_loh_allocations_for_no_gc();
16503     set_soh_allocations_for_no_gc();
16504 #endif //MULTIPLE_HEAPS
16505 }
16506
16507 BOOL gc_heap::should_proceed_for_no_gc()
16508 {
16509     BOOL gc_requested = FALSE;
16510     BOOL loh_full_gc_requested = FALSE;
16511     BOOL soh_full_gc_requested = FALSE;
16512     BOOL no_gc_requested = FALSE;
16513     BOOL get_new_loh_segments = FALSE;
16514
16515     if (current_no_gc_region_info.soh_allocation_size)
16516     {
16517 #ifdef MULTIPLE_HEAPS
16518         for (int i = 0; i < n_heaps; i++)
16519         {
16520             gc_heap* hp = g_heaps[i];
16521             if ((size_t)(heap_segment_reserved (hp->ephemeral_heap_segment) - hp->alloc_allocated) < hp->soh_allocation_no_gc)
16522             {
16523                 gc_requested = TRUE;
16524                 break;
16525             }
16526         }
16527 #else //MULTIPLE_HEAPS
16528         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated) < soh_allocation_no_gc)
16529             gc_requested = TRUE;
16530 #endif //MULTIPLE_HEAPS
16531
16532         if (!gc_requested)
16533         {
16534 #ifdef MULTIPLE_HEAPS
16535             for (int i = 0; i < n_heaps; i++)
16536             {
16537                 gc_heap* hp = g_heaps[i];
16538                 if (!(hp->grow_heap_segment (hp->ephemeral_heap_segment, (hp->alloc_allocated + hp->soh_allocation_no_gc))))
16539                 {
16540                     soh_full_gc_requested = TRUE;
16541                     break;
16542                 }
16543             }
16544 #else //MULTIPLE_HEAPS
16545             if (!grow_heap_segment (ephemeral_heap_segment, (alloc_allocated + soh_allocation_no_gc)))
16546                 soh_full_gc_requested = TRUE;
16547 #endif //MULTIPLE_HEAPS
16548         }
16549     }
16550
16551     if (!current_no_gc_region_info.minimal_gc_p && gc_requested)
16552     {
16553         soh_full_gc_requested = TRUE;
16554     }
16555
16556     no_gc_requested = !(soh_full_gc_requested || gc_requested);
16557
16558     if (soh_full_gc_requested && current_no_gc_region_info.minimal_gc_p)
16559     {
16560         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16561         goto done;
16562     }
16563
16564     if (!soh_full_gc_requested && current_no_gc_region_info.loh_allocation_size)
16565     {
16566         // Check to see if we have enough reserved space. 
16567 #ifdef MULTIPLE_HEAPS
16568         for (int i = 0; i < n_heaps; i++)
16569         {
16570             gc_heap* hp = g_heaps[i];
16571             if (!hp->find_loh_space_for_no_gc())
16572             {
16573                 loh_full_gc_requested = TRUE;
16574                 break;
16575             }
16576         }
16577 #else //MULTIPLE_HEAPS
16578         if (!find_loh_space_for_no_gc())
16579             loh_full_gc_requested = TRUE;
16580 #endif //MULTIPLE_HEAPS
16581
16582         // Check to see if we have committed space.
16583         if (!loh_full_gc_requested)
16584         {
16585 #ifdef MULTIPLE_HEAPS
16586             for (int i = 0; i < n_heaps; i++)
16587             {
16588                 gc_heap* hp = g_heaps[i];
16589                 if (hp->saved_loh_segment_no_gc &&!hp->commit_loh_for_no_gc (hp->saved_loh_segment_no_gc))
16590                 {
16591                     loh_full_gc_requested = TRUE;
16592                     break;
16593                 }
16594             }
16595 #else //MULTIPLE_HEAPS
16596             if (saved_loh_segment_no_gc && !commit_loh_for_no_gc (saved_loh_segment_no_gc))
16597                 loh_full_gc_requested = TRUE;
16598 #endif //MULTIPLE_HEAPS
16599         }
16600     }
16601
16602     if (loh_full_gc_requested || soh_full_gc_requested)
16603     {
16604         if (current_no_gc_region_info.minimal_gc_p)
16605             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16606     }
16607
16608     no_gc_requested = !(loh_full_gc_requested || soh_full_gc_requested || gc_requested);
16609
16610     if (current_no_gc_region_info.start_status == start_no_gc_success)
16611     {
16612         if (no_gc_requested)
16613             set_allocations_for_no_gc();
16614     }
16615
16616 done:
16617
16618     if ((current_no_gc_region_info.start_status == start_no_gc_success) && !no_gc_requested)
16619         return TRUE;
16620     else
16621     {
16622         // We are done with starting the no_gc_region.
16623         current_no_gc_region_info.started = TRUE;
16624         return FALSE;
16625     }
16626 }
16627
16628 end_no_gc_region_status gc_heap::end_no_gc_region()
16629 {
16630     dprintf (1, ("end no gc called"));
16631
16632     end_no_gc_region_status status = end_no_gc_success;
16633
16634     if (!(current_no_gc_region_info.started))
16635         status = end_no_gc_not_in_progress;
16636     if (current_no_gc_region_info.num_gcs_induced)
16637         status = end_no_gc_induced;
16638     else if (current_no_gc_region_info.num_gcs)
16639         status = end_no_gc_alloc_exceeded;
16640
16641     if (settings.pause_mode == pause_no_gc)
16642         restore_data_for_no_gc();
16643
16644     // sets current_no_gc_region_info.started to FALSE here.
16645     memset (&current_no_gc_region_info, 0, sizeof (current_no_gc_region_info));
16646
16647     return status;
16648 }
16649
16650 //update counters
16651 void gc_heap::update_collection_counts ()
16652 {
16653     dynamic_data* dd0 = dynamic_data_of (0);
16654     dd_gc_clock (dd0) += 1;
16655
16656     size_t now = GetHighPrecisionTimeStamp();
16657
16658     for (int i = 0; i <= settings.condemned_generation;i++)
16659     {
16660         dynamic_data* dd = dynamic_data_of (i);
16661         dd_collection_count (dd)++;
16662         //this is needed by the linear allocation model
16663         if (i == max_generation)
16664             dd_collection_count (dynamic_data_of (max_generation+1))++;
16665         dd_gc_clock (dd) = dd_gc_clock (dd0);
16666         dd_time_clock (dd) = now;
16667     }
16668 }
16669
16670 BOOL gc_heap::expand_soh_with_minimal_gc()
16671 {
16672     if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) >= soh_allocation_no_gc)
16673         return TRUE;
16674
16675     heap_segment* new_seg = soh_get_segment_to_expand();
16676     if (new_seg)
16677     {
16678         if (g_gc_card_table != card_table)
16679             copy_brick_card_table();
16680
16681         settings.promotion = TRUE;
16682         settings.demotion = FALSE;
16683         ephemeral_promotion = TRUE;
16684         int condemned_gen_number = max_generation - 1;
16685
16686         generation* gen = 0;
16687         int align_const = get_alignment_constant (TRUE);
16688
16689         for (int i = 0; i <= condemned_gen_number; i++)
16690         {
16691             gen = generation_of (i);
16692             saved_ephemeral_plan_start[i] = generation_allocation_start (gen);
16693             saved_ephemeral_plan_start_size[i] = Align (size (generation_allocation_start (gen)), align_const);
16694         }
16695
16696         // We do need to clear the bricks here as we are converting a bunch of ephemeral objects to gen2
16697         // and need to make sure that there are no left over bricks from the previous GCs for the space 
16698         // we just used for gen0 allocation. We will need to go through the bricks for these objects for 
16699         // ephemeral GCs later.
16700         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
16701              b < brick_of (align_on_brick (heap_segment_allocated (ephemeral_heap_segment)));
16702              b++)
16703         {
16704             set_brick (b, -1);
16705         }
16706
16707         size_t ephemeral_size = (heap_segment_allocated (ephemeral_heap_segment) - 
16708                                 generation_allocation_start (generation_of (max_generation - 1)));
16709         heap_segment_next (ephemeral_heap_segment) = new_seg;
16710         ephemeral_heap_segment = new_seg;
16711         uint8_t*  start = heap_segment_mem (ephemeral_heap_segment);
16712
16713         for (int i = condemned_gen_number; i >= 0; i--)
16714         {
16715             gen = generation_of (i);
16716             size_t gen_start_size = Align (min_obj_size);
16717             make_generation (generation_table[i], ephemeral_heap_segment, start, 0);
16718             generation_plan_allocation_start (gen) = start;
16719             generation_plan_allocation_start_size (gen) = gen_start_size;
16720             start += gen_start_size;
16721         }
16722         heap_segment_used (ephemeral_heap_segment) = start - plug_skew;
16723         heap_segment_plan_allocated (ephemeral_heap_segment) = start;
16724
16725         fix_generation_bounds (condemned_gen_number, generation_of (0));
16726
16727         dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size;
16728         dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation));
16729
16730         adjust_ephemeral_limits();
16731         return TRUE;
16732     }
16733     else
16734         return FALSE;
16735 }
16736
16737 // Only to be done on the thread that calls restart in a join for server GC
16738 // and reset the oom status per heap.
16739 void gc_heap::check_and_set_no_gc_oom()
16740 {
16741 #ifdef MULTIPLE_HEAPS
16742     for (int i = 0; i < n_heaps; i++)
16743     {
16744         gc_heap* hp = g_heaps[i];
16745         if (hp->no_gc_oom_p)
16746         {
16747             current_no_gc_region_info.start_status = start_no_gc_no_memory;
16748             hp->no_gc_oom_p = false;
16749         }
16750     }
16751 #else
16752     if (no_gc_oom_p)
16753     {
16754         current_no_gc_region_info.start_status = start_no_gc_no_memory;
16755         no_gc_oom_p = false;
16756     }
16757 #endif //MULTIPLE_HEAPS
16758 }
16759
16760 void gc_heap::allocate_for_no_gc_after_gc()
16761 {
16762     if (current_no_gc_region_info.minimal_gc_p)
16763         repair_allocation_contexts (TRUE);
16764
16765     no_gc_oom_p = false;
16766
16767     if (current_no_gc_region_info.start_status != start_no_gc_no_memory)
16768     {
16769         if (current_no_gc_region_info.soh_allocation_size != 0)
16770         {
16771             if (((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment)) < soh_allocation_no_gc) ||
16772                 (!grow_heap_segment (ephemeral_heap_segment, (heap_segment_allocated (ephemeral_heap_segment) + soh_allocation_no_gc))))
16773             {
16774                 no_gc_oom_p = true;
16775             }
16776
16777 #ifdef MULTIPLE_HEAPS
16778             gc_t_join.join(this, gc_join_after_commit_soh_no_gc);
16779             if (gc_t_join.joined())
16780             {
16781 #endif //MULTIPLE_HEAPS
16782
16783                 check_and_set_no_gc_oom();
16784
16785 #ifdef MULTIPLE_HEAPS
16786                 gc_t_join.restart();
16787             }
16788 #endif //MULTIPLE_HEAPS
16789         }
16790
16791         if ((current_no_gc_region_info.start_status == start_no_gc_success) &&
16792             !(current_no_gc_region_info.minimal_gc_p) && 
16793             (current_no_gc_region_info.loh_allocation_size != 0))
16794         {
16795             gc_policy = policy_compact;
16796             saved_loh_segment_no_gc = 0;
16797
16798             if (!find_loh_free_for_no_gc())
16799             {
16800                 heap_segment* seg = generation_allocation_segment (generation_of (max_generation + 1));
16801                 BOOL found_seg_p = FALSE;
16802                 while (seg)
16803                 {
16804                     if ((size_t)(heap_segment_reserved (seg) - heap_segment_allocated (seg)) >= loh_allocation_no_gc)
16805                     {
16806                         found_seg_p = TRUE;
16807                         if (!commit_loh_for_no_gc (seg))
16808                         {
16809                             no_gc_oom_p = true;
16810                             break;
16811                         }
16812                     }
16813                     seg = heap_segment_next (seg);
16814                 }
16815
16816                 if (!found_seg_p)
16817                     gc_policy = policy_expand;
16818             }
16819
16820 #ifdef MULTIPLE_HEAPS
16821             gc_t_join.join(this, gc_join_expand_loh_no_gc);
16822             if (gc_t_join.joined())
16823             {
16824                 check_and_set_no_gc_oom();
16825
16826                 if (current_no_gc_region_info.start_status == start_no_gc_success)
16827                 {
16828                     for (int i = 0; i < n_heaps; i++)
16829                     {
16830                         gc_heap* hp = g_heaps[i];
16831                         if (hp->gc_policy == policy_expand)
16832                         {
16833                             hp->saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc), hp);
16834                             if (!(hp->saved_loh_segment_no_gc))
16835                             {
16836                                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16837                                 break;
16838                             }
16839                         }
16840                     }
16841                 }
16842
16843                 gc_t_join.restart();
16844             }
16845 #else //MULTIPLE_HEAPS
16846             check_and_set_no_gc_oom();
16847
16848             if ((current_no_gc_region_info.start_status == start_no_gc_success) && (gc_policy == policy_expand))
16849             {
16850                 saved_loh_segment_no_gc = get_segment_for_loh (get_large_seg_size (loh_allocation_no_gc));
16851                 if (!saved_loh_segment_no_gc)
16852                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16853             }
16854 #endif //MULTIPLE_HEAPS
16855
16856             if ((current_no_gc_region_info.start_status == start_no_gc_success) && saved_loh_segment_no_gc)
16857             {
16858                 if (!commit_loh_for_no_gc (saved_loh_segment_no_gc))
16859                 {
16860                     no_gc_oom_p = true;
16861                 }
16862             }
16863         }
16864     }
16865
16866 #ifdef MULTIPLE_HEAPS
16867     gc_t_join.join(this, gc_join_final_no_gc);
16868     if (gc_t_join.joined())
16869     {
16870 #endif //MULTIPLE_HEAPS
16871
16872         check_and_set_no_gc_oom();
16873
16874         if (current_no_gc_region_info.start_status == start_no_gc_success)
16875         {
16876             set_allocations_for_no_gc();
16877             current_no_gc_region_info.started = TRUE;
16878         }
16879
16880 #ifdef MULTIPLE_HEAPS
16881         gc_t_join.restart();
16882     }
16883 #endif //MULTIPLE_HEAPS
16884 }
16885
16886 void gc_heap::init_records()
16887 {
16888     // An option is to move this to be after we figure out which gen to condemn so we don't 
16889     // need to clear some generations' data 'cause we know they don't change, but that also means 
16890     // we can't simply call memset here. 
16891     memset (&gc_data_per_heap, 0, sizeof (gc_data_per_heap));
16892     gc_data_per_heap.heap_index = heap_number;
16893     if (heap_number == 0)
16894         memset (&gc_data_global, 0, sizeof (gc_data_global));
16895
16896 #ifdef GC_CONFIG_DRIVEN
16897     memset (interesting_data_per_gc, 0, sizeof (interesting_data_per_gc));
16898 #endif //GC_CONFIG_DRIVEN
16899     memset (&fgm_result, 0, sizeof (fgm_result));
16900
16901     for (int i = 0; i <= (max_generation + 1); i++)
16902     {
16903         gc_data_per_heap.gen_data[i].size_before = generation_size (i);
16904         generation* gen = generation_of (i);
16905         gc_data_per_heap.gen_data[i].free_list_space_before = generation_free_list_space (gen);
16906         gc_data_per_heap.gen_data[i].free_obj_space_before = generation_free_obj_space (gen);
16907     }
16908
16909     sufficient_gen0_space_p = FALSE;
16910
16911 #ifdef MULTIPLE_HEAPS
16912     gen0_allocated_after_gc_p = false;
16913 #endif //MULTIPLE_HEAPS
16914
16915 #if defined (_DEBUG) && defined (VERIFY_HEAP)
16916     verify_pinned_queue_p = FALSE;
16917 #endif // _DEBUG && VERIFY_HEAP
16918 }
16919
16920 void gc_heap::pm_full_gc_init_or_clear()
16921 {
16922     // This means the next GC will be a full blocking GC and we need to init.
16923     if (settings.condemned_generation == (max_generation - 1))
16924     {
16925         if (pm_trigger_full_gc)
16926         {
16927 #ifdef MULTIPLE_HEAPS
16928             do_post_gc();
16929 #endif //MULTIPLE_HEAPS
16930             dprintf (GTC_LOG, ("init for PM triggered full GC"));
16931             uint32_t saved_entry_memory_load = settings.entry_memory_load;
16932             settings.init_mechanisms();
16933             settings.reason = reason_pm_full_gc;
16934             settings.condemned_generation = max_generation;
16935             settings.entry_memory_load = saved_entry_memory_load;
16936             // Can't assert this since we only check at the end of gen2 GCs,
16937             // during gen1 the memory load could have already dropped. 
16938             // Although arguably we should just turn off PM then...
16939             //assert (settings.entry_memory_load >= high_memory_load_th);
16940             assert (settings.entry_memory_load > 0);
16941             settings.gc_index += 1;
16942             do_pre_gc();
16943         }
16944     }
16945     // This means we are in the progress of a full blocking GC triggered by
16946     // this PM mode.
16947     else if (settings.reason == reason_pm_full_gc)
16948     {
16949         assert (settings.condemned_generation == max_generation);
16950         assert (pm_trigger_full_gc);
16951         pm_trigger_full_gc = false;
16952
16953         dprintf (GTC_LOG, ("PM triggered full GC done"));
16954     }
16955 }
16956
16957 void gc_heap::garbage_collect_pm_full_gc()
16958 {
16959     assert (settings.condemned_generation == max_generation);
16960     assert (settings.reason == reason_pm_full_gc);
16961     assert (!settings.concurrent);
16962     gc1();
16963 }
16964
16965 void gc_heap::garbage_collect (int n)
16966 {
16967     //reset the number of alloc contexts
16968     alloc_contexts_used = 0;
16969
16970     fix_allocation_contexts (TRUE);
16971 #ifdef MULTIPLE_HEAPS
16972 #ifdef JOIN_STATS
16973     gc_t_join.start_ts(this);
16974 #endif //JOIN_STATS
16975     clear_gen0_bricks();
16976 #endif //MULTIPLE_HEAPS
16977
16978     if ((settings.pause_mode == pause_no_gc) && current_no_gc_region_info.minimal_gc_p)
16979     {
16980 #ifdef MULTIPLE_HEAPS
16981         gc_t_join.join(this, gc_join_minimal_gc);
16982         if (gc_t_join.joined())
16983         {
16984 #endif //MULTIPLE_HEAPS
16985
16986 #ifdef MULTIPLE_HEAPS
16987             // this is serialized because we need to get a segment
16988             for (int i = 0; i < n_heaps; i++)
16989             {
16990                 if (!(g_heaps[i]->expand_soh_with_minimal_gc()))
16991                     current_no_gc_region_info.start_status = start_no_gc_no_memory;
16992             }
16993 #else
16994             if (!expand_soh_with_minimal_gc())
16995                 current_no_gc_region_info.start_status = start_no_gc_no_memory;
16996 #endif //MULTIPLE_HEAPS
16997
16998             update_collection_counts_for_no_gc();
16999
17000 #ifdef MULTIPLE_HEAPS
17001             gc_t_join.restart();
17002         }
17003 #endif //MULTIPLE_HEAPS
17004
17005         goto done;
17006     }
17007
17008     init_records();
17009
17010     settings.reason = gc_trigger_reason;
17011 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
17012     num_pinned_objects = 0;
17013 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
17014
17015 #ifdef STRESS_HEAP
17016     if (settings.reason == reason_gcstress)
17017     {
17018         settings.reason = reason_induced;
17019         settings.stress_induced = TRUE;
17020     }
17021 #endif // STRESS_HEAP
17022
17023 #ifdef MULTIPLE_HEAPS
17024     //align all heaps on the max generation to condemn
17025     dprintf (3, ("Joining for max generation to condemn"));
17026     condemned_generation_num = generation_to_condemn (n, 
17027                                                     &blocking_collection, 
17028                                                     &elevation_requested, 
17029                                                     FALSE);
17030     gc_t_join.join(this, gc_join_generation_determined);
17031     if (gc_t_join.joined())
17032 #endif //MULTIPLE_HEAPS
17033     {
17034 #if !defined(SEG_MAPPING_TABLE) && !defined(FEATURE_BASICFREEZE)
17035         //delete old slots from the segment table
17036         seg_table->delete_old_slots();
17037 #endif //!SEG_MAPPING_TABLE && !FEATURE_BASICFREEZE
17038
17039 #ifdef MULTIPLE_HEAPS
17040         for (int i = 0; i < n_heaps; i++)
17041         {
17042             gc_heap* hp = g_heaps[i];
17043             // check for card table growth
17044             if (g_gc_card_table != hp->card_table)
17045                 hp->copy_brick_card_table();
17046
17047             hp->rearrange_large_heap_segments();
17048 #ifdef BACKGROUND_GC
17049             hp->background_delay_delete_loh_segments();
17050             if (!recursive_gc_sync::background_running_p())
17051                 hp->rearrange_small_heap_segments();
17052 #endif //BACKGROUND_GC
17053         }
17054 #else //MULTIPLE_HEAPS
17055         if (g_gc_card_table != card_table)
17056             copy_brick_card_table();
17057
17058         rearrange_large_heap_segments();
17059 #ifdef BACKGROUND_GC
17060         background_delay_delete_loh_segments();
17061         if (!recursive_gc_sync::background_running_p())
17062             rearrange_small_heap_segments();
17063 #endif //BACKGROUND_GC
17064 #endif //MULTIPLE_HEAPS
17065
17066     BOOL should_evaluate_elevation = TRUE;
17067     BOOL should_do_blocking_collection = FALSE;
17068
17069 #ifdef MULTIPLE_HEAPS
17070     int gen_max = condemned_generation_num;
17071     for (int i = 0; i < n_heaps; i++)
17072     {
17073         if (gen_max < g_heaps[i]->condemned_generation_num)
17074             gen_max = g_heaps[i]->condemned_generation_num;
17075         if (should_evaluate_elevation && !(g_heaps[i]->elevation_requested))
17076             should_evaluate_elevation = FALSE;
17077         if ((!should_do_blocking_collection) && (g_heaps[i]->blocking_collection))
17078             should_do_blocking_collection = TRUE;
17079     }
17080
17081     settings.condemned_generation = gen_max;
17082 #else //MULTIPLE_HEAPS
17083     settings.condemned_generation = generation_to_condemn (n, 
17084                                                         &blocking_collection, 
17085                                                         &elevation_requested, 
17086                                                         FALSE);
17087     should_evaluate_elevation = elevation_requested;
17088     should_do_blocking_collection = blocking_collection;
17089 #endif //MULTIPLE_HEAPS
17090
17091     settings.condemned_generation = joined_generation_to_condemn (
17092                                         should_evaluate_elevation,
17093                                         n,
17094                                         settings.condemned_generation,
17095                                         &should_do_blocking_collection
17096                                         STRESS_HEAP_ARG(n)
17097                                         );
17098
17099     STRESS_LOG1(LF_GCROOTS|LF_GC|LF_GCALLOC, LL_INFO10, 
17100             "condemned generation num: %d\n", settings.condemned_generation);
17101
17102     record_gcs_during_no_gc();
17103
17104     if (settings.condemned_generation > 1)
17105         settings.promotion = TRUE;
17106
17107 #ifdef HEAP_ANALYZE
17108     // At this point we've decided what generation is condemned
17109     // See if we've been requested to analyze survivors after the mark phase
17110     if (GCToEEInterface::AnalyzeSurvivorsRequested(settings.condemned_generation))
17111     {
17112         heap_analyze_enabled = TRUE;
17113     }
17114 #endif // HEAP_ANALYZE
17115
17116         GCToEEInterface::DiagGCStart(settings.condemned_generation, settings.reason == reason_induced);
17117
17118 #ifdef BACKGROUND_GC
17119         if ((settings.condemned_generation == max_generation) &&
17120             (recursive_gc_sync::background_running_p()))
17121         {
17122             //TODO BACKGROUND_GC If we just wait for the end of gc, it won't work
17123             // because we have to collect 0 and 1 properly
17124             // in particular, the allocation contexts are gone.
17125             // For now, it is simpler to collect max_generation-1
17126             settings.condemned_generation = max_generation - 1;
17127             dprintf (GTC_LOG, ("bgc - 1 instead of 2"));
17128         }
17129
17130         if ((settings.condemned_generation == max_generation) &&
17131             (should_do_blocking_collection == FALSE) &&
17132             gc_can_use_concurrent &&
17133             !temp_disable_concurrent_p &&                 
17134             ((settings.pause_mode == pause_interactive) || (settings.pause_mode == pause_sustained_low_latency)))
17135         {
17136             keep_bgc_threads_p = TRUE;
17137             c_write (settings.concurrent,  TRUE);
17138         }
17139 #endif //BACKGROUND_GC
17140
17141         settings.gc_index = (uint32_t)dd_collection_count (dynamic_data_of (0)) + 1;
17142
17143         // Call the EE for start of GC work
17144         // just one thread for MP GC
17145         GCToEEInterface::GcStartWork (settings.condemned_generation,
17146                                 max_generation);            
17147
17148         // TODO: we could fire an ETW event to say this GC as a concurrent GC but later on due to not being able to
17149         // create threads or whatever, this could be a non concurrent GC. Maybe for concurrent GC we should fire
17150         // it in do_background_gc and if it failed to be a CGC we fire it in gc1... in other words, this should be
17151         // fired in gc1.
17152         do_pre_gc();
17153
17154 #ifdef MULTIPLE_HEAPS
17155         gc_start_event.Reset();
17156         //start all threads on the roots.
17157         dprintf(3, ("Starting all gc threads for gc"));
17158         gc_t_join.restart();
17159 #endif //MULTIPLE_HEAPS
17160     }
17161
17162         descr_generations (TRUE);
17163
17164 #ifdef VERIFY_HEAP
17165     if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
17166        !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_POST_GC_ONLY))
17167     {
17168         verify_heap (TRUE);
17169     }
17170     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK)
17171         checkGCWriteBarrier();
17172
17173 #endif // VERIFY_HEAP
17174
17175 #ifdef BACKGROUND_GC
17176     if (settings.concurrent)
17177     {
17178         // We need to save the settings because we'll need to restore it after each FGC.
17179         assert (settings.condemned_generation == max_generation);
17180         settings.compaction = FALSE;
17181         saved_bgc_settings = settings;
17182
17183 #ifdef MULTIPLE_HEAPS
17184         if (heap_number == 0)
17185         {
17186             for (int i = 0; i < n_heaps; i++)
17187             {
17188                 prepare_bgc_thread (g_heaps[i]);
17189             }
17190             dprintf (2, ("setting bgc_threads_sync_event"));
17191             bgc_threads_sync_event.Set();
17192         }
17193         else
17194         {
17195             bgc_threads_sync_event.Wait(INFINITE, FALSE);
17196             dprintf (2, ("bgc_threads_sync_event is signalled"));
17197         }
17198 #else
17199         prepare_bgc_thread(0);
17200 #endif //MULTIPLE_HEAPS
17201
17202 #ifdef MULTIPLE_HEAPS
17203         gc_t_join.join(this, gc_join_start_bgc);
17204         if (gc_t_join.joined())
17205 #endif //MULTIPLE_HEAPS
17206         {
17207             do_concurrent_p = TRUE;
17208             do_ephemeral_gc_p = FALSE;
17209 #ifdef MULTIPLE_HEAPS
17210             dprintf(2, ("Joined to perform a background GC"));
17211
17212             for (int i = 0; i < n_heaps; i++)
17213             {
17214                 gc_heap* hp = g_heaps[i];
17215                 if (!(hp->bgc_thread) || !hp->commit_mark_array_bgc_init (hp->mark_array))
17216                 {
17217                     do_concurrent_p = FALSE;
17218                     break;
17219                 }
17220                 else
17221                 {
17222                     hp->background_saved_lowest_address = hp->lowest_address;
17223                     hp->background_saved_highest_address = hp->highest_address;
17224                 }
17225             }
17226 #else
17227             do_concurrent_p = (!!bgc_thread && commit_mark_array_bgc_init (mark_array));
17228             if (do_concurrent_p)
17229             {
17230                 background_saved_lowest_address = lowest_address;
17231                 background_saved_highest_address = highest_address;
17232             }
17233 #endif //MULTIPLE_HEAPS
17234
17235             if (do_concurrent_p)
17236             {
17237 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
17238                 SoftwareWriteWatch::EnableForGCHeap();
17239 #endif //FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
17240
17241 #ifdef MULTIPLE_HEAPS
17242                 for (int i = 0; i < n_heaps; i++)
17243                     g_heaps[i]->current_bgc_state = bgc_initialized;
17244 #else
17245                 current_bgc_state = bgc_initialized;
17246 #endif //MULTIPLE_HEAPS
17247
17248                 int gen = check_for_ephemeral_alloc();
17249                 // always do a gen1 GC before we start BGC. 
17250                 // This is temporary for testing purpose.
17251                 //int gen = max_generation - 1;
17252                 dont_restart_ee_p = TRUE;
17253                 if (gen == -1)
17254                 {
17255                     // If we decide to not do a GC before the BGC we need to 
17256                     // restore the gen0 alloc context.
17257 #ifdef MULTIPLE_HEAPS
17258                     for (int i = 0; i < n_heaps; i++)
17259                     {
17260                         generation_allocation_pointer (g_heaps[i]->generation_of (0)) =  0;
17261                         generation_allocation_limit (g_heaps[i]->generation_of (0)) = 0;
17262                     }
17263 #else
17264                     generation_allocation_pointer (youngest_generation) =  0;
17265                     generation_allocation_limit (youngest_generation) = 0;
17266 #endif //MULTIPLE_HEAPS
17267                 }
17268                 else
17269                 {
17270                     do_ephemeral_gc_p = TRUE;
17271
17272                     settings.init_mechanisms();
17273                     settings.condemned_generation = gen;
17274                     settings.gc_index = (size_t)dd_collection_count (dynamic_data_of (0)) + 2;
17275                     do_pre_gc();
17276
17277                     // TODO BACKGROUND_GC need to add the profiling stuff here.
17278                     dprintf (GTC_LOG, ("doing gen%d before doing a bgc", gen));
17279                 }
17280
17281                 //clear the cards so they don't bleed in gen 1 during collection
17282                 // shouldn't this always be done at the beginning of any GC?
17283                 //clear_card_for_addresses (
17284                 //    generation_allocation_start (generation_of (0)),
17285                 //    heap_segment_allocated (ephemeral_heap_segment));
17286
17287                 if (!do_ephemeral_gc_p)
17288                 {
17289                     do_background_gc();
17290                 }
17291             }
17292             else
17293             {
17294                 settings.compaction = TRUE;
17295                 c_write (settings.concurrent, FALSE);
17296             }
17297
17298 #ifdef MULTIPLE_HEAPS
17299             gc_t_join.restart();
17300 #endif //MULTIPLE_HEAPS
17301         }
17302
17303         if (do_concurrent_p)
17304         {
17305             // At this point we are sure we'll be starting a BGC, so save its per heap data here.
17306             // global data is only calculated at the end of the GC so we don't need to worry about
17307             // FGCs overwriting it.
17308             memset (&bgc_data_per_heap, 0, sizeof (bgc_data_per_heap));
17309             memcpy (&bgc_data_per_heap, &gc_data_per_heap, sizeof(gc_data_per_heap));
17310
17311             if (do_ephemeral_gc_p)
17312             {
17313                 dprintf (2, ("GC threads running, doing gen%d GC", settings.condemned_generation));
17314
17315                 gen_to_condemn_reasons.init();
17316                 gen_to_condemn_reasons.set_condition (gen_before_bgc);
17317                 gc_data_per_heap.gen_to_condemn_reasons.init (&gen_to_condemn_reasons);
17318                 gc1();
17319 #ifdef MULTIPLE_HEAPS
17320                 gc_t_join.join(this, gc_join_bgc_after_ephemeral);
17321                 if (gc_t_join.joined())
17322 #endif //MULTIPLE_HEAPS
17323                 {
17324 #ifdef MULTIPLE_HEAPS
17325                     do_post_gc();
17326 #endif //MULTIPLE_HEAPS
17327                     settings = saved_bgc_settings;
17328                     assert (settings.concurrent);
17329
17330                     do_background_gc();
17331
17332 #ifdef MULTIPLE_HEAPS
17333                     gc_t_join.restart();
17334 #endif //MULTIPLE_HEAPS
17335                 }
17336             }
17337         }
17338         else
17339         {
17340             dprintf (2, ("couldn't create BGC threads, reverting to doing a blocking GC"));
17341             gc1();
17342         }
17343     }
17344     else
17345 #endif //BACKGROUND_GC
17346     {
17347         gc1();
17348     }
17349 #ifndef MULTIPLE_HEAPS
17350     allocation_running_time = (size_t)GCToOSInterface::GetLowPrecisionTimeStamp();
17351     allocation_running_amount = dd_new_allocation (dynamic_data_of (0));
17352     fgn_last_alloc = dd_new_allocation (dynamic_data_of (0));
17353 #endif //MULTIPLE_HEAPS
17354
17355 done:
17356     if (settings.pause_mode == pause_no_gc)
17357         allocate_for_no_gc_after_gc();
17358
17359 }
17360
17361 #define mark_stack_empty_p() (mark_stack_base == mark_stack_tos)
17362
17363 inline
17364 size_t& gc_heap::promoted_bytes(int thread)
17365 {
17366 #ifdef MULTIPLE_HEAPS
17367     return g_promoted [thread*16];
17368 #else //MULTIPLE_HEAPS
17369     UNREFERENCED_PARAMETER(thread);
17370     return g_promoted;
17371 #endif //MULTIPLE_HEAPS
17372 }
17373
17374 #ifdef INTERIOR_POINTERS
17375 heap_segment* gc_heap::find_segment (uint8_t* interior, BOOL small_segment_only_p)
17376 {
17377 #ifdef SEG_MAPPING_TABLE
17378     heap_segment* seg = seg_mapping_table_segment_of (interior);
17379     if (seg)
17380     {
17381         if (small_segment_only_p && heap_segment_loh_p (seg))
17382             return 0;
17383     }
17384     return seg;
17385 #else //SEG_MAPPING_TABLE
17386 #ifdef MULTIPLE_HEAPS
17387     for (int i = 0; i < gc_heap::n_heaps; i++)
17388     {
17389         gc_heap* h = gc_heap::g_heaps [i];
17390         hs = h->find_segment_per_heap (o, small_segment_only_p);
17391         if (hs)
17392         {
17393             break;
17394         }        
17395     }
17396 #else
17397     {
17398         gc_heap* h = pGenGCHeap;
17399         hs = h->find_segment_per_heap (o, small_segment_only_p);
17400     }
17401 #endif //MULTIPLE_HEAPS
17402 #endif //SEG_MAPPING_TABLE
17403 }
17404
17405 heap_segment* gc_heap::find_segment_per_heap (uint8_t* interior, BOOL small_segment_only_p)
17406 {
17407 #ifdef SEG_MAPPING_TABLE
17408     return find_segment (interior, small_segment_only_p);
17409 #else //SEG_MAPPING_TABLE
17410     if (in_range_for_segment (interior, ephemeral_heap_segment))
17411     {
17412         return ephemeral_heap_segment;
17413     }
17414     else
17415     {
17416         heap_segment* found_seg = 0;
17417
17418         {
17419             heap_segment* seg = generation_start_segment (generation_of (max_generation));
17420             do
17421             {
17422                 if (in_range_for_segment (interior, seg))
17423                 {
17424                     found_seg = seg;
17425                     goto end_find_segment;
17426                 }
17427
17428             } while ((seg = heap_segment_next (seg)) != 0);
17429         }
17430         if (!small_segment_only_p)
17431         {
17432 #ifdef BACKGROUND_GC
17433             {
17434                 ptrdiff_t delta = 0;
17435                 heap_segment* seg = segment_of (interior, delta);
17436                 if (seg && in_range_for_segment (interior, seg))
17437                 {
17438                     found_seg = seg;
17439                 }
17440                 goto end_find_segment;
17441             }
17442 #else //BACKGROUND_GC
17443             heap_segment* seg = generation_start_segment (generation_of (max_generation+1));
17444             do
17445             {
17446                 if (in_range_for_segment(interior, seg))
17447                 {
17448                     found_seg = seg;
17449                     goto end_find_segment;
17450                 }
17451
17452             } while ((seg = heap_segment_next (seg)) != 0);
17453 #endif //BACKGROUND_GC
17454         }
17455 end_find_segment:
17456
17457         return found_seg;
17458     }
17459 #endif //SEG_MAPPING_TABLE
17460 }
17461 #endif //INTERIOR_POINTERS
17462
17463 #if !defined(_DEBUG) && !defined(__GNUC__)
17464 inline // This causes link errors if global optimization is off
17465 #endif //!_DEBUG && !__GNUC__
17466 gc_heap* gc_heap::heap_of (uint8_t* o)
17467 {
17468 #ifdef MULTIPLE_HEAPS
17469     if (o == 0)
17470         return g_heaps [0];
17471 #ifdef SEG_MAPPING_TABLE
17472     gc_heap* hp = seg_mapping_table_heap_of (o);
17473     return (hp ? hp : g_heaps[0]);
17474 #else //SEG_MAPPING_TABLE
17475     ptrdiff_t delta = 0;
17476     heap_segment* seg = segment_of (o, delta);
17477     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17478 #endif //SEG_MAPPING_TABLE
17479 #else //MULTIPLE_HEAPS
17480     UNREFERENCED_PARAMETER(o);
17481     return __this;
17482 #endif //MULTIPLE_HEAPS
17483 }
17484
17485 inline
17486 gc_heap* gc_heap::heap_of_gc (uint8_t* o)
17487 {
17488 #ifdef MULTIPLE_HEAPS
17489     if (o == 0)
17490         return g_heaps [0];
17491 #ifdef SEG_MAPPING_TABLE
17492     gc_heap* hp = seg_mapping_table_heap_of_gc (o);
17493     return (hp ? hp : g_heaps[0]);
17494 #else //SEG_MAPPING_TABLE
17495     ptrdiff_t delta = 0;
17496     heap_segment* seg = segment_of (o, delta);
17497     return (seg ? heap_segment_heap (seg) : g_heaps [0]);
17498 #endif //SEG_MAPPING_TABLE
17499 #else //MULTIPLE_HEAPS
17500     UNREFERENCED_PARAMETER(o);
17501     return __this;
17502 #endif //MULTIPLE_HEAPS
17503 }
17504
17505 #ifdef INTERIOR_POINTERS
17506 // will find all heap objects (large and small)
17507 uint8_t* gc_heap::find_object (uint8_t* interior, uint8_t* low)
17508 {
17509     if (!gen0_bricks_cleared)
17510     {
17511 #ifdef MULTIPLE_HEAPS
17512         assert (!"Should have already been done in server GC");
17513 #endif //MULTIPLE_HEAPS
17514         gen0_bricks_cleared = TRUE;
17515         //initialize brick table for gen 0
17516         for (size_t b = brick_of (generation_allocation_start (generation_of (0)));
17517              b < brick_of (align_on_brick
17518                            (heap_segment_allocated (ephemeral_heap_segment)));
17519              b++)
17520         {
17521             set_brick (b, -1);
17522         }
17523     }
17524 #ifdef FFIND_OBJECT
17525     //indicate that in the future this needs to be done during allocation
17526 #ifdef MULTIPLE_HEAPS
17527     gen0_must_clear_bricks = FFIND_DECAY*gc_heap::n_heaps;
17528 #else
17529     gen0_must_clear_bricks = FFIND_DECAY;
17530 #endif //MULTIPLE_HEAPS
17531 #endif //FFIND_OBJECT
17532
17533     int brick_entry = get_brick_entry(brick_of (interior));
17534     if (brick_entry == 0)
17535     {
17536         // this is a pointer to a large object
17537         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17538         if (seg
17539 #ifdef FEATURE_CONSERVATIVE_GC
17540             && (GCConfig::GetConservativeGC() || interior <= heap_segment_allocated(seg))
17541 #endif
17542             )
17543         {
17544             // If interior falls within the first free object at the beginning of a generation,
17545             // we don't have brick entry for it, and we may incorrectly treat it as on large object heap.
17546             int align_const = get_alignment_constant (heap_segment_read_only_p (seg)
17547 #ifdef FEATURE_CONSERVATIVE_GC
17548                                                        || (GCConfig::GetConservativeGC() && !heap_segment_loh_p (seg))
17549 #endif
17550                                                       );
17551             //int align_const = get_alignment_constant (heap_segment_read_only_p (seg));
17552             assert (interior < heap_segment_allocated (seg));
17553
17554             uint8_t* o = heap_segment_mem (seg);
17555             while (o < heap_segment_allocated (seg))
17556             {
17557                 uint8_t* next_o = o + Align (size (o), align_const);
17558                 assert (next_o > o);
17559                 if ((o <= interior) && (interior < next_o))
17560                 return o;
17561                 o = next_o;
17562             }
17563             return 0;
17564         }
17565         else
17566         {
17567             return 0;
17568         }
17569     }
17570     else if (interior >= low)
17571     {
17572         heap_segment* seg = find_segment_per_heap (interior, TRUE);
17573         if (seg)
17574         {
17575 #ifdef FEATURE_CONSERVATIVE_GC
17576             if (interior >= heap_segment_allocated (seg))
17577                 return 0;
17578 #else
17579             assert (interior < heap_segment_allocated (seg));
17580 #endif
17581             uint8_t* o = find_first_object (interior, heap_segment_mem (seg));
17582             return o;
17583         }
17584         else
17585             return 0;
17586     }
17587     else
17588         return 0;
17589 }
17590
17591 uint8_t*
17592 gc_heap::find_object_for_relocation (uint8_t* interior, uint8_t* low, uint8_t* high)
17593 {
17594     uint8_t* old_address = interior;
17595     if (!((old_address >= low) && (old_address < high)))
17596         return 0;
17597     uint8_t* plug = 0;
17598     size_t  brick = brick_of (old_address);
17599     int    brick_entry =  brick_table [ brick ];
17600     if (brick_entry != 0)
17601     {
17602     retry:
17603         {
17604             while (brick_entry < 0)
17605             {
17606                 brick = (brick + brick_entry);
17607                 brick_entry =  brick_table [ brick ];
17608             }
17609             uint8_t* old_loc = old_address;
17610             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
17611                                       old_loc);
17612             if (node <= old_loc)
17613                 plug = node;
17614             else
17615             {
17616                 brick = brick - 1;
17617                 brick_entry =  brick_table [ brick ];
17618                 goto retry;
17619             }
17620
17621         }
17622         assert (plug);
17623         //find the object by going along the plug
17624         uint8_t* o = plug;
17625         while (o <= interior)
17626         {
17627             uint8_t* next_o = o + Align (size (o));
17628             assert (next_o > o);
17629             if (next_o > interior)
17630             {
17631                 break;
17632             }
17633             o = next_o;
17634         }
17635         assert ((o <= interior) && ((o + Align (size (o))) > interior));
17636         return o;
17637     }
17638     else
17639     {
17640         // this is a pointer to a large object
17641         heap_segment* seg = find_segment_per_heap (interior, FALSE);
17642         if (seg)
17643         {
17644             assert (interior < heap_segment_allocated (seg));
17645
17646             uint8_t* o = heap_segment_mem (seg);
17647             while (o < heap_segment_allocated (seg))
17648             {
17649                 uint8_t* next_o = o + Align (size (o));
17650                 assert (next_o > o);
17651                 if ((o < interior) && (interior < next_o))
17652                 return o;
17653                 o = next_o;
17654             }
17655             return 0;
17656         }
17657         else
17658             {
17659             return 0;
17660         }
17661     }
17662 }
17663 #else //INTERIOR_POINTERS
17664 inline
17665 uint8_t* gc_heap::find_object (uint8_t* o, uint8_t* low)
17666 {
17667     return o;
17668 }
17669 #endif //INTERIOR_POINTERS
17670
17671 #ifdef MULTIPLE_HEAPS
17672
17673 #ifdef MARK_LIST
17674 #ifdef GC_CONFIG_DRIVEN
17675 #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;} else {mark_list_index++;}}
17676 #else //GC_CONFIG_DRIVEN
17677 #define m_boundary(o) {if (mark_list_index <= mark_list_end) {*mark_list_index = o;mark_list_index++;}}
17678 #endif //GC_CONFIG_DRIVEN
17679 #else //MARK_LIST
17680 #define m_boundary(o) {}
17681 #endif //MARK_LIST
17682
17683 #define m_boundary_fullgc(o) {}
17684
17685 #else //MULTIPLE_HEAPS
17686
17687 #ifdef MARK_LIST
17688 #ifdef GC_CONFIG_DRIVEN
17689 #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;}
17690 #else
17691 #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;}
17692 #endif //GC_CONFIG_DRIVEN
17693 #else //MARK_LIST
17694 #define m_boundary(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17695 #endif //MARK_LIST
17696
17697 #define m_boundary_fullgc(o) {if (slow > o) slow = o; if (shigh < o) shigh = o;}
17698
17699 #endif //MULTIPLE_HEAPS
17700
17701 #define method_table(o) ((CObjectHeader*)(o))->GetMethodTable()
17702
17703 inline
17704 BOOL gc_heap::gc_mark1 (uint8_t* o)
17705 {
17706     BOOL marked = !marked (o);
17707     set_marked (o);
17708     dprintf (3, ("*%Ix*, newly marked: %d", (size_t)o, marked));
17709     return marked;
17710 }
17711
17712 inline
17713 BOOL gc_heap::gc_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17714 {
17715     BOOL marked = FALSE;
17716     if ((o >= low) && (o < high))
17717         marked = gc_mark1 (o);
17718 #ifdef MULTIPLE_HEAPS
17719     else if (o)
17720     {
17721         //find the heap
17722         gc_heap* hp = heap_of_gc (o);
17723         assert (hp);
17724         if ((o >= hp->gc_low) && (o < hp->gc_high))
17725             marked = gc_mark1 (o);
17726     }
17727 #ifdef SNOOP_STATS
17728     snoop_stat.objects_checked_count++;
17729
17730     if (marked)
17731     {
17732         snoop_stat.objects_marked_count++;
17733     }
17734     if (!o)
17735     {
17736         snoop_stat.zero_ref_count++;
17737     }
17738
17739 #endif //SNOOP_STATS
17740 #endif //MULTIPLE_HEAPS
17741     return marked;
17742 }
17743
17744 #ifdef BACKGROUND_GC
17745
17746 inline
17747 BOOL gc_heap::background_marked (uint8_t* o)
17748 {
17749     return mark_array_marked (o);
17750 }
17751 inline
17752 BOOL gc_heap::background_mark1 (uint8_t* o)
17753 {
17754     BOOL to_mark = !mark_array_marked (o);
17755
17756     dprintf (3, ("b*%Ix*b(%d)", (size_t)o, (to_mark ? 1 : 0)));
17757     if (to_mark)
17758     {
17759         mark_array_set_marked (o);
17760         dprintf (4, ("n*%Ix*n", (size_t)o));
17761         return TRUE;
17762     }
17763     else
17764         return FALSE;
17765 }
17766
17767 // TODO: we could consider filtering out NULL's here instead of going to 
17768 // look for it on other heaps
17769 inline
17770 BOOL gc_heap::background_mark (uint8_t* o, uint8_t* low, uint8_t* high)
17771 {
17772     BOOL marked = FALSE;
17773     if ((o >= low) && (o < high))
17774         marked = background_mark1 (o);
17775 #ifdef MULTIPLE_HEAPS
17776     else if (o)
17777     {
17778         //find the heap
17779         gc_heap* hp = heap_of (o);
17780         assert (hp);
17781         if ((o >= hp->background_saved_lowest_address) && (o < hp->background_saved_highest_address))
17782             marked = background_mark1 (o);
17783     }
17784 #endif //MULTIPLE_HEAPS
17785     return marked;
17786 }
17787
17788 #endif //BACKGROUND_GC
17789
17790 inline
17791 uint8_t* gc_heap::next_end (heap_segment* seg, uint8_t* f)
17792 {
17793     if (seg == ephemeral_heap_segment)
17794         return  f;
17795     else
17796         return  heap_segment_allocated (seg);
17797 }
17798
17799 #define new_start() {if (ppstop <= start) {break;} else {parm = start}}
17800 #define ignore_start 0
17801 #define use_start 1
17802
17803 #define go_through_object(mt,o,size,parm,start,start_useful,limit,exp)      \
17804 {                                                                           \
17805     CGCDesc* map = CGCDesc::GetCGCDescFromMT((MethodTable*)(mt));           \
17806     CGCDescSeries* cur = map->GetHighestSeries();                           \
17807     ptrdiff_t cnt = (ptrdiff_t) map->GetNumSeries();                        \
17808                                                                             \
17809     if (cnt >= 0)                                                           \
17810     {                                                                       \
17811         CGCDescSeries* last = map->GetLowestSeries();                       \
17812         uint8_t** parm = 0;                                                 \
17813         do                                                                  \
17814         {                                                                   \
17815             assert (parm <= (uint8_t**)((o) + cur->GetSeriesOffset()));     \
17816             parm = (uint8_t**)((o) + cur->GetSeriesOffset());               \
17817             uint8_t** ppstop =                                              \
17818                 (uint8_t**)((uint8_t*)parm + cur->GetSeriesSize() + (size));\
17819             if (!start_useful || (uint8_t*)ppstop > (start))                \
17820             {                                                               \
17821                 if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);\
17822                 while (parm < ppstop)                                       \
17823                 {                                                           \
17824                    {exp}                                                    \
17825                    parm++;                                                  \
17826                 }                                                           \
17827             }                                                               \
17828             cur--;                                                          \
17829                                                                             \
17830         } while (cur >= last);                                              \
17831     }                                                                       \
17832     else                                                                    \
17833     {                                                                       \
17834         /* Handle the repeating case - array of valuetypes */               \
17835         uint8_t** parm = (uint8_t**)((o) + cur->startoffset);               \
17836         if (start_useful && start > (uint8_t*)parm)                         \
17837         {                                                                   \
17838             ptrdiff_t cs = mt->RawGetComponentSize();                         \
17839             parm = (uint8_t**)((uint8_t*)parm + (((start) - (uint8_t*)parm)/cs)*cs); \
17840         }                                                                   \
17841         while ((uint8_t*)parm < ((o)+(size)-plug_skew))                     \
17842         {                                                                   \
17843             for (ptrdiff_t __i = 0; __i > cnt; __i--)                         \
17844             {                                                               \
17845                 HALF_SIZE_T skip =  cur->val_serie[__i].skip;               \
17846                 HALF_SIZE_T nptrs = cur->val_serie[__i].nptrs;              \
17847                 uint8_t** ppstop = parm + nptrs;                            \
17848                 if (!start_useful || (uint8_t*)ppstop > (start))            \
17849                 {                                                           \
17850                     if (start_useful && (uint8_t*)parm < (start)) parm = (uint8_t**)(start);      \
17851                     do                                                      \
17852                     {                                                       \
17853                        {exp}                                                \
17854                        parm++;                                              \
17855                     } while (parm < ppstop);                                \
17856                 }                                                           \
17857                 parm = (uint8_t**)((uint8_t*)ppstop + skip);                \
17858             }                                                               \
17859         }                                                                   \
17860     }                                                                       \
17861 }
17862
17863 #define go_through_object_nostart(mt,o,size,parm,exp) {go_through_object(mt,o,size,parm,o,ignore_start,(o + size),exp); }
17864
17865 // 1 thing to note about this macro:
17866 // 1) you can use *parm safely but in general you don't want to use parm 
17867 // because for the collectible types it's not an address on the managed heap.
17868 #ifndef COLLECTIBLE_CLASS
17869 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17870 {                                                                           \
17871     if (header(o)->ContainsPointers())                                      \
17872     {                                                                       \
17873         go_through_object_nostart(mt,o,size,parm,exp);                      \
17874     }                                                                       \
17875 }
17876 #else //COLLECTIBLE_CLASS
17877 #define go_through_object_cl(mt,o,size,parm,exp)                            \
17878 {                                                                           \
17879     if (header(o)->Collectible())                                           \
17880     {                                                                       \
17881         uint8_t* class_obj = get_class_object (o);                             \
17882         uint8_t** parm = &class_obj;                                           \
17883         do {exp} while (false);                                             \
17884     }                                                                       \
17885     if (header(o)->ContainsPointers())                                      \
17886     {                                                                       \
17887         go_through_object_nostart(mt,o,size,parm,exp);                      \
17888     }                                                                       \
17889 }
17890 #endif //COLLECTIBLE_CLASS
17891
17892 // This starts a plug. But mark_stack_tos isn't increased until set_pinned_info is called.
17893 void gc_heap::enque_pinned_plug (uint8_t* plug,
17894                                  BOOL save_pre_plug_info_p, 
17895                                  uint8_t* last_object_in_last_plug)
17896 {
17897     if (mark_stack_array_length <= mark_stack_tos)
17898     {
17899         if (!grow_mark_stack (mark_stack_array, mark_stack_array_length, MARK_STACK_INITIAL_LENGTH))
17900         {
17901             // we don't want to continue here due to security
17902             // risks. This happens very rarely and fixing it in the
17903             // way so that we can continue is a bit involved and will
17904             // not be done in Dev10.
17905             GCToEEInterface::HandleFatalError(CORINFO_EXCEPTION_GC);
17906         }
17907     }
17908
17909     dprintf (3, ("enqueuing P #%Id(%Ix): %Ix. oldest: %Id, LO: %Ix, pre: %d", 
17910         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)));
17911     mark& m = mark_stack_array[mark_stack_tos];
17912     m.first = plug;
17913     // Must be set now because if we have a short object we'll need the value of saved_pre_p.
17914     m.saved_pre_p = save_pre_plug_info_p;
17915
17916     if (save_pre_plug_info_p)
17917     {
17918 #ifdef SHORT_PLUGS
17919         BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17920         if (is_padded)
17921             clear_plug_padded (last_object_in_last_plug);
17922 #endif //SHORT_PLUGS
17923         memcpy (&(m.saved_pre_plug), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17924 #ifdef SHORT_PLUGS
17925         if (is_padded)
17926             set_plug_padded (last_object_in_last_plug);
17927 #endif //SHORT_PLUGS
17928
17929         memcpy (&(m.saved_pre_plug_reloc), &(((plug_and_gap*)plug)[-1]), sizeof (gap_reloc_pair));
17930
17931         // If the last object in the last plug is too short, it requires special handling.
17932         size_t last_obj_size = plug - last_object_in_last_plug;
17933         if (last_obj_size < min_pre_pin_obj_size)
17934         {
17935             record_interesting_data_point (idp_pre_short);
17936 #ifdef SHORT_PLUGS
17937             if (is_padded)
17938                 record_interesting_data_point (idp_pre_short_padded);
17939 #endif //SHORT_PLUGS
17940             dprintf (3, ("encountered a short object %Ix right before pinned plug %Ix!", 
17941                          last_object_in_last_plug, plug));
17942             // Need to set the short bit regardless of having refs or not because we need to 
17943             // indicate that this object is not walkable.
17944             m.set_pre_short();
17945
17946 #ifdef COLLECTIBLE_CLASS
17947             if (is_collectible (last_object_in_last_plug))
17948             {
17949                 m.set_pre_short_collectible();
17950             }
17951 #endif //COLLECTIBLE_CLASS
17952
17953             if (contain_pointers (last_object_in_last_plug))
17954             {
17955                 dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
17956
17957                 go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
17958                     {
17959                         size_t gap_offset = (((size_t)pval - (size_t)(plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
17960                         dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
17961                         m.set_pre_short_bit (gap_offset);
17962                     }
17963                 );
17964             }
17965         }
17966     }
17967
17968     m.saved_post_p = FALSE;
17969 }
17970
17971 void gc_heap::save_post_plug_info (uint8_t* last_pinned_plug, uint8_t* last_object_in_last_plug, uint8_t* post_plug)
17972 {
17973     UNREFERENCED_PARAMETER(last_pinned_plug);
17974
17975     mark& m = mark_stack_array[mark_stack_tos - 1];
17976     assert (last_pinned_plug == m.first);
17977     m.saved_post_plug_info_start = (uint8_t*)&(((plug_and_gap*)post_plug)[-1]);
17978
17979 #ifdef SHORT_PLUGS
17980     BOOL is_padded = is_plug_padded (last_object_in_last_plug);
17981     if (is_padded)
17982         clear_plug_padded (last_object_in_last_plug);
17983 #endif //SHORT_PLUGS
17984     memcpy (&(m.saved_post_plug), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17985 #ifdef SHORT_PLUGS
17986     if (is_padded)
17987         set_plug_padded (last_object_in_last_plug);
17988 #endif //SHORT_PLUGS
17989
17990     memcpy (&(m.saved_post_plug_reloc), m.saved_post_plug_info_start, sizeof (gap_reloc_pair));
17991
17992     // This is important - we need to clear all bits here except the last one.
17993     m.saved_post_p = TRUE;
17994
17995 #ifdef _DEBUG
17996     m.saved_post_plug_debug.gap = 1;
17997 #endif //_DEBUG
17998
17999     dprintf (3, ("PP %Ix has NP %Ix right after", last_pinned_plug, post_plug));
18000
18001     size_t last_obj_size = post_plug - last_object_in_last_plug;
18002     if (last_obj_size < min_pre_pin_obj_size)
18003     {
18004         dprintf (3, ("PP %Ix last obj %Ix is too short", last_pinned_plug, last_object_in_last_plug));
18005         record_interesting_data_point (idp_post_short);
18006 #ifdef SHORT_PLUGS
18007         if (is_padded)
18008             record_interesting_data_point (idp_post_short_padded);
18009 #endif //SHORT_PLUGS
18010         m.set_post_short();
18011 #if defined (_DEBUG) && defined (VERIFY_HEAP)
18012         verify_pinned_queue_p = TRUE;
18013 #endif // _DEBUG && VERIFY_HEAP
18014
18015 #ifdef COLLECTIBLE_CLASS
18016         if (is_collectible (last_object_in_last_plug))
18017         {
18018             m.set_post_short_collectible();
18019         }
18020 #endif //COLLECTIBLE_CLASS
18021
18022         if (contain_pointers (last_object_in_last_plug))
18023         {
18024             dprintf (3, ("short object: %Ix(%Ix)", last_object_in_last_plug, last_obj_size));
18025
18026             // TODO: since we won't be able to walk this object in relocation, we still need to
18027             // take care of collectible assemblies here.
18028             go_through_object_nostart (method_table(last_object_in_last_plug), last_object_in_last_plug, last_obj_size, pval,
18029                 {
18030                     size_t gap_offset = (((size_t)pval - (size_t)(post_plug - sizeof (gap_reloc_pair) - plug_skew))) / sizeof (uint8_t*);
18031                     dprintf (3, ("member: %Ix->%Ix, %Id ptrs from beginning of gap", (uint8_t*)pval, *pval, gap_offset));
18032                     m.set_post_short_bit (gap_offset);
18033                 }
18034             );
18035         }
18036     }
18037 }
18038
18039 //#define PREFETCH
18040 #ifdef PREFETCH
18041 __declspec(naked) void __fastcall Prefetch(void* addr)
18042 {
18043    __asm {
18044        PREFETCHT0 [ECX]
18045         ret
18046     };
18047 }
18048 #else //PREFETCH
18049 inline void Prefetch (void* addr)
18050 {
18051     UNREFERENCED_PARAMETER(addr);
18052 }
18053 #endif //PREFETCH
18054 #ifdef MH_SC_MARK
18055 inline
18056 VOLATILE(uint8_t*)& gc_heap::ref_mark_stack (gc_heap* hp, int index)
18057 {
18058     return ((VOLATILE(uint8_t*)*)(hp->mark_stack_array))[index];
18059 }
18060
18061 #endif //MH_SC_MARK
18062
18063 #define stolen 2
18064 #define partial 1
18065 #define partial_object 3
18066 inline 
18067 uint8_t* ref_from_slot (uint8_t* r)
18068 {
18069     return (uint8_t*)((size_t)r & ~(stolen | partial));
18070 }
18071 inline
18072 BOOL stolen_p (uint8_t* r)
18073 {
18074     return (((size_t)r&2) && !((size_t)r&1));
18075 }
18076 inline 
18077 BOOL ready_p (uint8_t* r)
18078 {
18079     return ((size_t)r != 1);
18080 }
18081 inline
18082 BOOL partial_p (uint8_t* r)
18083 {
18084     return (((size_t)r&1) && !((size_t)r&2));
18085 }
18086 inline 
18087 BOOL straight_ref_p (uint8_t* r)
18088 {
18089     return (!stolen_p (r) && !partial_p (r));
18090 }
18091 inline 
18092 BOOL partial_object_p (uint8_t* r)
18093 {
18094     return (((size_t)r & partial_object) == partial_object);
18095 }
18096 inline
18097 BOOL ref_p (uint8_t* r)
18098 {
18099     return (straight_ref_p (r) || partial_object_p (r));
18100 }
18101
18102 void gc_heap::mark_object_simple1 (uint8_t* oo, uint8_t* start THREAD_NUMBER_DCL)
18103 {
18104     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_tos = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)mark_stack_array;
18105     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_limit = (SERVER_SC_MARK_VOLATILE(uint8_t*)*)&mark_stack_array[mark_stack_array_length];
18106     SERVER_SC_MARK_VOLATILE(uint8_t*)* mark_stack_base = mark_stack_tos;
18107 #ifdef SORT_MARK_STACK
18108     SERVER_SC_MARK_VOLATILE(uint8_t*)* sorted_tos = mark_stack_base;
18109 #endif //SORT_MARK_STACK
18110
18111     // If we are doing a full GC we don't use mark list anyway so use m_boundary_fullgc that doesn't 
18112     // update mark list.
18113     BOOL  full_p = (settings.condemned_generation == max_generation);
18114
18115     assert ((start >= oo) && (start < oo+size(oo)));
18116
18117 #ifndef MH_SC_MARK
18118     *mark_stack_tos = oo;
18119 #endif //!MH_SC_MARK
18120
18121     while (1)
18122     {
18123 #ifdef MULTIPLE_HEAPS
18124 #else  //MULTIPLE_HEAPS
18125         const int thread = 0;
18126 #endif //MULTIPLE_HEAPS
18127
18128         if (oo && ((size_t)oo != 4))
18129         {
18130             size_t s = 0; 
18131             if (stolen_p (oo))
18132             {
18133                 --mark_stack_tos;
18134                 goto next_level;
18135             }
18136             else if (!partial_p (oo) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
18137             {
18138                 BOOL overflow_p = FALSE;
18139
18140                 if (mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit  - 1))
18141                 {
18142                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18143                     if (mark_stack_tos + CGCDesc::GetNumPointers(method_table(oo), s, num_components) >= (mark_stack_limit - 1))
18144                     {
18145                         overflow_p = TRUE;
18146                     }
18147                 }
18148                 
18149                 if (overflow_p == FALSE)
18150                 {
18151                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18152
18153                     go_through_object_cl (method_table(oo), oo, s, ppslot,
18154                                           {
18155                                               uint8_t* o = *ppslot;
18156                                               Prefetch(o);
18157                                               if (gc_mark (o, gc_low, gc_high))
18158                                               {
18159                                                   if (full_p)
18160                                                   {
18161                                                       m_boundary_fullgc (o);
18162                                                   }
18163                                                   else
18164                                                   {
18165                                                       m_boundary (o);
18166                                                   }
18167                                                   size_t obj_size = size (o);
18168                                                   promoted_bytes (thread) += obj_size;
18169                                                   if (contain_pointers_or_collectible (o))
18170                                                   {
18171                                                       *(mark_stack_tos++) = o;
18172                                                   }
18173                                               }
18174                                           }
18175                         );
18176                 }
18177                 else
18178                 {
18179                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
18180                     min_overflow_address = min (min_overflow_address, oo);
18181                     max_overflow_address = max (max_overflow_address, oo);
18182                 }
18183             }
18184             else
18185             {
18186                 if (partial_p (oo))
18187                 {
18188                     start = ref_from_slot (oo);
18189                     oo = ref_from_slot (*(--mark_stack_tos));
18190                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
18191                     assert ((oo < start) && (start < (oo + size (oo))));
18192                 }
18193 #ifdef COLLECTIBLE_CLASS
18194                 else
18195                 {
18196                     // If there's a class object, push it now. We are guaranteed to have the slot since
18197                     // we just popped one object off.
18198                     if (is_collectible (oo))
18199                     {
18200                         uint8_t* class_obj = get_class_object (oo);
18201                         if (gc_mark (class_obj, gc_low, gc_high))
18202                         {
18203                             if (full_p)
18204                             {
18205                                 m_boundary_fullgc (class_obj);
18206                             }
18207                             else
18208                             {
18209                                 m_boundary (class_obj);
18210                             }
18211
18212                             size_t obj_size = size (class_obj);
18213                             promoted_bytes (thread) += obj_size;
18214                             *(mark_stack_tos++) = class_obj;
18215                             // The code below expects that the oo is still stored in the stack slot that was
18216                             // just popped and it "pushes" it back just by incrementing the mark_stack_tos. 
18217                             // But the class_obj has just overwritten that stack slot and so the oo needs to
18218                             // be stored to the new slot that's pointed to by the mark_stack_tos.
18219                             *mark_stack_tos = oo;
18220                         }
18221                     }
18222
18223                     if (!contain_pointers (oo))
18224                     {
18225                         goto next_level;
18226                     }
18227                 }
18228 #endif //COLLECTIBLE_CLASS
18229
18230                 s = size (oo);
18231                 
18232                 BOOL overflow_p = FALSE;
18233             
18234                 if (mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
18235                 {
18236                     overflow_p = TRUE;
18237                 }
18238                 if (overflow_p == FALSE)
18239                 {
18240                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18241
18242                     //push the object and its current 
18243                     SERVER_SC_MARK_VOLATILE(uint8_t*)* place = ++mark_stack_tos;
18244                     mark_stack_tos++;
18245 #ifdef MH_SC_MARK
18246                     *(place-1) = 0;
18247                     *(place) = (uint8_t*)partial;
18248 #endif //MH_SC_MARK
18249                     int i = num_partial_refs; 
18250                     uint8_t* ref_to_continue = 0;
18251
18252                     go_through_object (method_table(oo), oo, s, ppslot,
18253                                        start, use_start, (oo + s),
18254                                        {
18255                                            uint8_t* o = *ppslot;
18256                                            Prefetch(o);
18257                                            if (gc_mark (o, gc_low, gc_high))
18258                                            {
18259                                                 if (full_p)
18260                                                 {
18261                                                     m_boundary_fullgc (o);
18262                                                 }
18263                                                 else
18264                                                 {
18265                                                     m_boundary (o);
18266                                                 }
18267                                                 size_t obj_size = size (o);
18268                                                 promoted_bytes (thread) += obj_size;
18269                                                 if (contain_pointers_or_collectible (o))
18270                                                 {
18271                                                     *(mark_stack_tos++) = o;
18272                                                     if (--i == 0)
18273                                                     {
18274                                                         ref_to_continue = (uint8_t*)((size_t)(ppslot+1) | partial);
18275                                                         goto more_to_do;
18276                                                     }
18277
18278                                                 }
18279                                            }
18280
18281                                        }
18282                         );
18283                     //we are finished with this object
18284                     assert (ref_to_continue == 0);
18285 #ifdef MH_SC_MARK
18286                     assert ((*(place-1)) == (uint8_t*)0);
18287 #else //MH_SC_MARK
18288                     *(place-1) = 0;
18289 #endif //MH_SC_MARK
18290                     *place = 0; 
18291                     // shouldn't we decrease tos by 2 here??
18292
18293 more_to_do:
18294                     if (ref_to_continue)
18295                     {
18296                         //update the start
18297 #ifdef MH_SC_MARK
18298                         assert ((*(place-1)) == (uint8_t*)0);
18299                         *(place-1) = (uint8_t*)((size_t)oo | partial_object);
18300                         assert (((*place) == (uint8_t*)1) || ((*place) == (uint8_t*)2));
18301 #endif //MH_SC_MARK
18302                         *place = ref_to_continue;
18303                     }
18304                 }
18305                 else
18306                 {
18307                     dprintf(3,("mark stack overflow for object %Ix ", (size_t)oo));
18308                     min_overflow_address = min (min_overflow_address, oo);
18309                     max_overflow_address = max (max_overflow_address, oo);
18310                 }
18311             }
18312 #ifdef SORT_MARK_STACK
18313             if (mark_stack_tos > sorted_tos + mark_stack_array_length/8)
18314             {
18315                 rqsort1 (sorted_tos, mark_stack_tos-1);
18316                 sorted_tos = mark_stack_tos-1;
18317             }
18318 #endif //SORT_MARK_STACK
18319         }
18320     next_level:
18321         if (!(mark_stack_empty_p()))
18322         {
18323             oo = *(--mark_stack_tos);
18324             start = oo;
18325
18326 #ifdef SORT_MARK_STACK
18327             sorted_tos = min ((size_t)sorted_tos, (size_t)mark_stack_tos);
18328 #endif //SORT_MARK_STACK
18329         }
18330         else
18331             break;
18332     }
18333 }
18334
18335 #ifdef MH_SC_MARK
18336 BOOL same_numa_node_p (int hn1, int hn2)
18337 {
18338     return (heap_select::find_numa_node_from_heap_no (hn1) == heap_select::find_numa_node_from_heap_no (hn2));
18339 }
18340
18341 int find_next_buddy_heap (int this_heap_number, int current_buddy, int n_heaps)
18342 {
18343     int hn = (current_buddy+1)%n_heaps;
18344     while (hn != current_buddy)
18345     {
18346         if ((this_heap_number != hn) && (same_numa_node_p (this_heap_number, hn)))
18347             return hn;
18348         hn = (hn+1)%n_heaps;
18349     }
18350     return current_buddy;
18351 }
18352
18353 void 
18354 gc_heap::mark_steal()
18355 {
18356     mark_stack_busy() = 0;
18357     //clear the mark stack in the snooping range
18358     for (int i = 0; i < max_snoop_level; i++)
18359     {
18360         ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
18361     }
18362
18363     //pick the next heap as our buddy
18364     int thpn = find_next_buddy_heap (heap_number, heap_number, n_heaps);
18365
18366 #ifdef SNOOP_STATS
18367         dprintf (SNOOP_LOG, ("(GC%d)heap%d: start snooping %d", settings.gc_index, heap_number, (heap_number+1)%n_heaps));
18368         uint32_t begin_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
18369 #endif //SNOOP_STATS
18370
18371     int idle_loop_count = 0; 
18372     int first_not_ready_level = 0;
18373
18374     while (1)
18375     {
18376         gc_heap* hp = g_heaps [thpn];
18377         int level = first_not_ready_level;
18378         first_not_ready_level = 0; 
18379
18380         while (check_next_mark_stack (hp) && (level < (max_snoop_level-1)))
18381         {
18382             idle_loop_count = 0; 
18383 #ifdef SNOOP_STATS
18384             snoop_stat.busy_count++;
18385             dprintf (SNOOP_LOG, ("heap%d: looking at next heap level %d stack contents: %Ix", 
18386                                  heap_number, level, (int)((uint8_t**)(hp->mark_stack_array))[level]));
18387 #endif //SNOOP_STATS
18388
18389             uint8_t* o = ref_mark_stack (hp, level);
18390
18391             uint8_t* start = o;
18392             if (ref_p (o))
18393             {
18394                 mark_stack_busy() = 1;
18395
18396                 BOOL success = TRUE;
18397                 uint8_t* next = (ref_mark_stack (hp, level+1));
18398                 if (ref_p (next))
18399                 {
18400                     if (((size_t)o > 4) && !partial_object_p (o))
18401                     {
18402                         //this is a normal object, not a partial mark tuple
18403                         //success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), 0, o)==o);
18404                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level), (uint8_t*)4, o)==o);
18405 #ifdef SNOOP_STATS
18406                         snoop_stat.interlocked_count++;
18407                         if (success)
18408                             snoop_stat.normal_count++;
18409 #endif //SNOOP_STATS
18410                     }
18411                     else
18412                     {
18413                         //it is a stolen entry, or beginning/ending of a partial mark
18414                         level++;
18415 #ifdef SNOOP_STATS
18416                         snoop_stat.stolen_or_pm_count++;
18417 #endif //SNOOP_STATS
18418                         success = FALSE;
18419                     }
18420                 }
18421                 else if (stolen_p (next))
18422                 {
18423                     //ignore the stolen guy and go to the next level
18424                     success = FALSE;
18425                     level+=2;
18426 #ifdef SNOOP_STATS
18427                     snoop_stat.stolen_entry_count++;
18428 #endif //SNOOP_STATS
18429                 }
18430                 else
18431                 {
18432                     assert (partial_p (next));
18433                     start = ref_from_slot (next);
18434                     //re-read the object
18435                     o = ref_from_slot (ref_mark_stack (hp, level));
18436                     if (o && start)
18437                     {
18438                         //steal the object
18439                         success = (Interlocked::CompareExchangePointer (&ref_mark_stack (hp, level+1), (uint8_t*)stolen, next)==next);
18440 #ifdef SNOOP_STATS
18441                         snoop_stat.interlocked_count++;
18442                         if (success)
18443                         {
18444                             snoop_stat.partial_mark_parent_count++;                    
18445                         }
18446 #endif //SNOOP_STATS
18447                     }
18448                     else
18449                     {
18450                         // stack is not ready, or o is completely different from the last time we read from this stack level.
18451                         // go up 2 levels to steal children or totally unrelated objects.
18452                         success = FALSE;
18453                         if (first_not_ready_level == 0)
18454                         {
18455                             first_not_ready_level = level;
18456                         }
18457                         level+=2;
18458 #ifdef SNOOP_STATS
18459                         snoop_stat.pm_not_ready_count++;
18460 #endif //SNOOP_STATS                        
18461                     }
18462                 }
18463                 if (success)
18464                 {
18465
18466 #ifdef SNOOP_STATS
18467                     dprintf (SNOOP_LOG, ("heap%d: marking %Ix from %d [%d] tl:%dms",
18468                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18469                             (GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18470                     uint32_t start_tick = GCToOSInterface::GetLowPrecisionTimeStamp();
18471 #endif //SNOOP_STATS
18472
18473                     mark_object_simple1 (o, start, heap_number);
18474
18475 #ifdef SNOOP_STATS
18476                     dprintf (SNOOP_LOG, ("heap%d: done marking %Ix from %d [%d] %dms tl:%dms",
18477                             heap_number, (size_t)o, (heap_number+1)%n_heaps, level,
18478                             (GCToOSInterface::GetLowPrecisionTimeStamp()-start_tick),(GCToOSInterface::GetLowPrecisionTimeStamp()-begin_tick)));
18479 #endif //SNOOP_STATS
18480
18481                     mark_stack_busy() = 0;
18482
18483                     //clear the mark stack in snooping range
18484                     for (int i = 0; i < max_snoop_level; i++)
18485                     {
18486                         if (((uint8_t**)mark_stack_array)[i] != 0)
18487                         {
18488                             ((VOLATILE(uint8_t*)*)(mark_stack_array))[i] = 0;
18489 #ifdef SNOOP_STATS
18490                             snoop_stat.stack_bottom_clear_count++;
18491 #endif //SNOOP_STATS
18492                         }
18493                     }
18494
18495                     level = 0; 
18496                 }
18497                 mark_stack_busy() = 0;
18498             }
18499             else
18500             {
18501                 //slot is either partial or stolen
18502                 level++;
18503             }
18504         }
18505         if ((first_not_ready_level != 0) && hp->mark_stack_busy())
18506         {
18507             continue;
18508         } 
18509         if (!hp->mark_stack_busy())
18510         {
18511             first_not_ready_level = 0; 
18512             idle_loop_count++;
18513
18514             if ((idle_loop_count % (6) )==1)
18515             {
18516 #ifdef SNOOP_STATS
18517                 snoop_stat.switch_to_thread_count++;
18518 #endif //SNOOP_STATS
18519                 GCToOSInterface::Sleep(1);
18520             }
18521             int free_count = 1;
18522 #ifdef SNOOP_STATS
18523             snoop_stat.stack_idle_count++;
18524             //dprintf (SNOOP_LOG, ("heap%d: counting idle threads", heap_number));
18525 #endif //SNOOP_STATS
18526             for (int hpn = (heap_number+1)%n_heaps; hpn != heap_number;)
18527             {
18528                 if (!((g_heaps [hpn])->mark_stack_busy()))
18529                 {
18530                     free_count++;
18531 #ifdef SNOOP_STATS
18532                 dprintf (SNOOP_LOG, ("heap%d: %d idle", heap_number, free_count));
18533 #endif //SNOOP_STATS
18534                 }
18535                 else if (same_numa_node_p (hpn, heap_number) || ((idle_loop_count%1000))==999)
18536                 {
18537                     thpn = hpn;
18538                     break;
18539                 }
18540                 hpn = (hpn+1)%n_heaps;
18541                 YieldProcessor();
18542             }
18543             if (free_count == n_heaps)
18544             {
18545                 break;
18546             }
18547         }
18548     }
18549 }
18550
18551 inline
18552 BOOL gc_heap::check_next_mark_stack (gc_heap* next_heap)
18553 {
18554 #ifdef SNOOP_STATS
18555     snoop_stat.check_level_count++;
18556 #endif //SNOOP_STATS
18557     return (next_heap->mark_stack_busy()>=1);
18558 }
18559 #endif //MH_SC_MARK
18560
18561 #ifdef SNOOP_STATS
18562 void gc_heap::print_snoop_stat()
18563 {
18564     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
18565         "heap", "check", "zero", "mark", "stole", "pstack", "nstack", "nonsk"));
18566     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d",
18567         snoop_stat.heap_index,
18568         snoop_stat.objects_checked_count,
18569         snoop_stat.zero_ref_count,
18570         snoop_stat.objects_marked_count,
18571         snoop_stat.stolen_stack_count,
18572         snoop_stat.partial_stack_count,
18573         snoop_stat.normal_stack_count,
18574         snoop_stat.non_stack_count));
18575     dprintf (1234, ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s", 
18576         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "clear"));
18577     dprintf (1234, ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18578         snoop_stat.heap_index,
18579         snoop_stat.check_level_count,
18580         snoop_stat.busy_count,
18581         snoop_stat.interlocked_count,
18582         snoop_stat.partial_mark_parent_count,
18583         snoop_stat.stolen_or_pm_count,
18584         snoop_stat.stolen_entry_count,
18585         snoop_stat.pm_not_ready_count,
18586         snoop_stat.normal_count,
18587         snoop_stat.stack_bottom_clear_count));
18588
18589     printf ("\n%4s | %8s | %8s | %8s | %8s | %8s\n", 
18590         "heap", "check", "zero", "mark", "idle", "switch");
18591     printf ("%4d | %8d | %8d | %8d | %8d | %8d\n",
18592         snoop_stat.heap_index,
18593         snoop_stat.objects_checked_count,
18594         snoop_stat.zero_ref_count,
18595         snoop_stat.objects_marked_count,
18596         snoop_stat.stack_idle_count,
18597         snoop_stat.switch_to_thread_count);
18598     printf ("%4s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
18599         "heap", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
18600     printf ("%4d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
18601         snoop_stat.heap_index,
18602         snoop_stat.check_level_count,
18603         snoop_stat.busy_count,
18604         snoop_stat.interlocked_count,
18605         snoop_stat.partial_mark_parent_count,
18606         snoop_stat.stolen_or_pm_count,
18607         snoop_stat.stolen_entry_count,
18608         snoop_stat.pm_not_ready_count,
18609         snoop_stat.normal_count,
18610         snoop_stat.stack_bottom_clear_count);
18611 }
18612 #endif //SNOOP_STATS
18613
18614 #ifdef HEAP_ANALYZE
18615 void
18616 gc_heap::ha_mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18617 {
18618     if (!internal_root_array)
18619     {
18620         internal_root_array = new (nothrow) uint8_t* [internal_root_array_length];
18621         if (!internal_root_array)
18622         {
18623             heap_analyze_success = FALSE;
18624         }
18625     }
18626
18627     if (heap_analyze_success && (internal_root_array_length <= internal_root_array_index))
18628     {
18629         size_t new_size = 2*internal_root_array_length;
18630
18631         uint64_t available_physical = 0;
18632         get_memory_info (NULL, &available_physical);
18633         if (new_size > (size_t)(available_physical / 10))
18634         {
18635             heap_analyze_success = FALSE;
18636         }
18637         else
18638         {
18639             uint8_t** tmp = new (nothrow) uint8_t* [new_size];
18640             if (tmp)
18641             {
18642                 memcpy (tmp, internal_root_array,
18643                         internal_root_array_length*sizeof (uint8_t*));
18644                 delete[] internal_root_array;
18645                 internal_root_array = tmp;
18646                 internal_root_array_length = new_size;
18647             }
18648             else
18649             {
18650                 heap_analyze_success = FALSE;
18651             }
18652         }
18653     }
18654
18655     if (heap_analyze_success)
18656     {
18657         PREFIX_ASSUME(internal_root_array_index < internal_root_array_length);
18658
18659         uint8_t* ref = (uint8_t*)po;
18660         if (!current_obj || 
18661             !((ref >= current_obj) && (ref < (current_obj + current_obj_size))))
18662         {
18663             gc_heap* hp = gc_heap::heap_of (ref);
18664             current_obj = hp->find_object (ref, hp->lowest_address);
18665             current_obj_size = size (current_obj);
18666
18667             internal_root_array[internal_root_array_index] = current_obj;
18668             internal_root_array_index++;
18669         }
18670     }
18671
18672     mark_object_simple (po THREAD_NUMBER_ARG);
18673 }
18674 #endif //HEAP_ANALYZE
18675
18676 //this method assumes that *po is in the [low. high[ range
18677 void
18678 gc_heap::mark_object_simple (uint8_t** po THREAD_NUMBER_DCL)
18679 {
18680     uint8_t* o = *po;
18681 #ifdef MULTIPLE_HEAPS
18682 #else  //MULTIPLE_HEAPS
18683     const int thread = 0;
18684 #endif //MULTIPLE_HEAPS
18685     {
18686 #ifdef SNOOP_STATS
18687         snoop_stat.objects_checked_count++;
18688 #endif //SNOOP_STATS
18689
18690         if (gc_mark1 (o))
18691         {
18692             m_boundary (o);
18693             size_t s = size (o);
18694             promoted_bytes (thread) += s;
18695             {
18696                 go_through_object_cl (method_table(o), o, s, poo,
18697                                         {
18698                                             uint8_t* oo = *poo;
18699                                             if (gc_mark (oo, gc_low, gc_high))
18700                                             {
18701                                                 m_boundary (oo);
18702                                                 size_t obj_size = size (oo);
18703                                                 promoted_bytes (thread) += obj_size;
18704
18705                                                 if (contain_pointers_or_collectible (oo))
18706                                                     mark_object_simple1 (oo, oo THREAD_NUMBER_ARG);
18707                                             }
18708                                         }
18709                     );
18710             }
18711         }
18712     }
18713 }
18714
18715 inline
18716 uint8_t* gc_heap::mark_object (uint8_t* o THREAD_NUMBER_DCL)
18717 {
18718     if ((o >= gc_low) && (o < gc_high))
18719         mark_object_simple (&o THREAD_NUMBER_ARG);
18720 #ifdef MULTIPLE_HEAPS
18721     else if (o)
18722     {
18723         //find the heap
18724         gc_heap* hp = heap_of (o);
18725         assert (hp);
18726         if ((o >= hp->gc_low) && (o < hp->gc_high))
18727             mark_object_simple (&o THREAD_NUMBER_ARG);
18728     }
18729 #endif //MULTIPLE_HEAPS
18730
18731     return o;
18732 }
18733
18734 #ifdef BACKGROUND_GC
18735
18736 void gc_heap::background_mark_simple1 (uint8_t* oo THREAD_NUMBER_DCL)
18737 {
18738     uint8_t** mark_stack_limit = &background_mark_stack_array[background_mark_stack_array_length];
18739
18740 #ifdef SORT_MARK_STACK
18741     uint8_t** sorted_tos = background_mark_stack_array;
18742 #endif //SORT_MARK_STACK
18743
18744     background_mark_stack_tos = background_mark_stack_array;
18745
18746     while (1)
18747     {
18748 #ifdef MULTIPLE_HEAPS
18749 #else  //MULTIPLE_HEAPS
18750         const int thread = 0;
18751 #endif //MULTIPLE_HEAPS
18752         if (oo)
18753         {
18754             size_t s = 0; 
18755             if ((((size_t)oo & 1) == 0) && ((s = size (oo)) < (partial_size_th*sizeof (uint8_t*))))
18756             {
18757                 BOOL overflow_p = FALSE;
18758             
18759                 if (background_mark_stack_tos + (s) /sizeof (uint8_t*) >= (mark_stack_limit - 1))
18760                 {
18761                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18762                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18763                     if (background_mark_stack_tos + num_pointers >= (mark_stack_limit - 1))
18764                     {
18765                         dprintf (2, ("h%d: %Id left, obj (mt: %Ix) %Id ptrs", 
18766                             heap_number,
18767                             (size_t)(mark_stack_limit - 1 - background_mark_stack_tos),
18768                             method_table(oo), 
18769                             num_pointers));
18770
18771                         bgc_overflow_count++;
18772                         overflow_p = TRUE;
18773                     }
18774                 }
18775             
18776                 if (overflow_p == FALSE)
18777                 {
18778                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18779
18780                     go_through_object_cl (method_table(oo), oo, s, ppslot,
18781                     {
18782                         uint8_t* o = *ppslot;
18783                         Prefetch(o);
18784                         if (background_mark (o, 
18785                                              background_saved_lowest_address, 
18786                                              background_saved_highest_address))
18787                         {
18788                             //m_boundary (o);
18789                             size_t obj_size = size (o);
18790                             bpromoted_bytes (thread) += obj_size;
18791                             if (contain_pointers_or_collectible (o))
18792                             {
18793                                 *(background_mark_stack_tos++) = o;
18794
18795                             }
18796                         }
18797                     }
18798                         );
18799                 }
18800                 else
18801                 {
18802                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18803                     background_min_overflow_address = min (background_min_overflow_address, oo);
18804                     background_max_overflow_address = max (background_max_overflow_address, oo);
18805                 }
18806             }
18807             else 
18808             {
18809                 uint8_t* start = oo;
18810                 if ((size_t)oo & 1)
18811                 {
18812                     oo = (uint8_t*)((size_t)oo & ~1);
18813                     start = *(--background_mark_stack_tos);
18814                     dprintf (4, ("oo: %Ix, start: %Ix\n", (size_t)oo, (size_t)start));
18815                 }
18816 #ifdef COLLECTIBLE_CLASS
18817                 else
18818                 {
18819                     // If there's a class object, push it now. We are guaranteed to have the slot since
18820                     // we just popped one object off.
18821                     if (is_collectible (oo))
18822                     {
18823                         uint8_t* class_obj = get_class_object (oo);
18824                         if (background_mark (class_obj, 
18825                                             background_saved_lowest_address, 
18826                                             background_saved_highest_address))
18827                         {
18828                             size_t obj_size = size (class_obj);
18829                             bpromoted_bytes (thread) += obj_size;
18830
18831                             *(background_mark_stack_tos++) = class_obj;
18832                         }
18833                     }
18834
18835                     if (!contain_pointers (oo))
18836                     {
18837                         goto next_level;
18838                     }                    
18839                 }
18840 #endif //COLLECTIBLE_CLASS
18841
18842                 s = size (oo);
18843                 
18844                 BOOL overflow_p = FALSE;
18845             
18846                 if (background_mark_stack_tos + (num_partial_refs + 2)  >= mark_stack_limit)
18847                 {
18848                     size_t num_components = ((method_table(oo))->HasComponentSize() ? ((CObjectHeader*)oo)->GetNumComponents() : 0);
18849                     size_t num_pointers = CGCDesc::GetNumPointers(method_table(oo), s, num_components);
18850
18851                     dprintf (2, ("h%d: PM: %Id left, obj %Ix (mt: %Ix) start: %Ix, total: %Id", 
18852                         heap_number,
18853                         (size_t)(mark_stack_limit - background_mark_stack_tos),
18854                         oo,
18855                         method_table(oo), 
18856                         start,
18857                         num_pointers));
18858
18859                     bgc_overflow_count++;
18860                     overflow_p = TRUE;
18861                 }
18862                 if (overflow_p == FALSE)
18863                 {
18864                     dprintf(3,("pushing mark for %Ix ", (size_t)oo));
18865
18866                     //push the object and its current 
18867                     uint8_t** place = background_mark_stack_tos++;
18868                     *(place) = start;
18869                     *(background_mark_stack_tos++) = (uint8_t*)((size_t)oo | 1);
18870
18871                     int i = num_partial_refs; 
18872
18873                     go_through_object (method_table(oo), oo, s, ppslot,
18874                                        start, use_start, (oo + s),
18875                     {
18876                         uint8_t* o = *ppslot;
18877                         Prefetch(o);
18878
18879                         if (background_mark (o, 
18880                                             background_saved_lowest_address, 
18881                                             background_saved_highest_address))
18882                         {
18883                             //m_boundary (o);
18884                             size_t obj_size = size (o);
18885                             bpromoted_bytes (thread) += obj_size;
18886                             if (contain_pointers_or_collectible (o))
18887                             {
18888                                 *(background_mark_stack_tos++) = o;
18889                                 if (--i == 0)
18890                                 {
18891                                     //update the start
18892                                     *place = (uint8_t*)(ppslot+1);
18893                                     goto more_to_do;
18894                                 }
18895
18896                             }
18897                         }
18898
18899                     }
18900                         );
18901                     //we are finished with this object
18902                     *place = 0; 
18903                     *(place+1) = 0;
18904
18905                 more_to_do:;
18906                 }
18907                 else
18908                 {
18909                     dprintf (3,("mark stack overflow for object %Ix ", (size_t)oo));
18910                     background_min_overflow_address = min (background_min_overflow_address, oo);
18911                     background_max_overflow_address = max (background_max_overflow_address, oo);
18912                 }
18913             }
18914         }
18915 #ifdef SORT_MARK_STACK
18916         if (background_mark_stack_tos > sorted_tos + mark_stack_array_length/8)
18917         {
18918             rqsort1 (sorted_tos, background_mark_stack_tos-1);
18919             sorted_tos = background_mark_stack_tos-1;
18920         }
18921 #endif //SORT_MARK_STACK
18922
18923 #ifdef COLLECTIBLE_CLASS
18924 next_level:
18925 #endif // COLLECTIBLE_CLASS
18926         allow_fgc();
18927
18928         if (!(background_mark_stack_tos == background_mark_stack_array))
18929         {
18930             oo = *(--background_mark_stack_tos);
18931
18932 #ifdef SORT_MARK_STACK
18933             sorted_tos = (uint8_t**)min ((size_t)sorted_tos, (size_t)background_mark_stack_tos);
18934 #endif //SORT_MARK_STACK
18935         }
18936         else
18937             break;
18938     }
18939
18940     assert (background_mark_stack_tos == background_mark_stack_array);
18941
18942
18943 }
18944
18945 //this version is different than the foreground GC because
18946 //it can't keep pointers to the inside of an object
18947 //while calling background_mark_simple1. The object could be moved
18948 //by an intervening foreground gc.
18949 //this method assumes that *po is in the [low. high[ range
18950 void
18951 gc_heap::background_mark_simple (uint8_t* o THREAD_NUMBER_DCL)
18952 {
18953 #ifdef MULTIPLE_HEAPS
18954 #else  //MULTIPLE_HEAPS
18955     const int thread = 0;
18956 #endif //MULTIPLE_HEAPS
18957     {
18958         dprintf (3, ("bmarking %Ix", o));
18959         
18960         if (background_mark1 (o))
18961         {
18962             //m_boundary (o);
18963             size_t s = size (o);
18964             bpromoted_bytes (thread) += s;
18965
18966             if (contain_pointers_or_collectible (o))
18967             {
18968                 background_mark_simple1 (o THREAD_NUMBER_ARG);
18969             }
18970         }
18971     }
18972 }
18973
18974 inline
18975 uint8_t* gc_heap::background_mark_object (uint8_t* o THREAD_NUMBER_DCL)
18976 {
18977     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
18978     {
18979         background_mark_simple (o THREAD_NUMBER_ARG);
18980     }
18981     else
18982     {
18983         if (o)
18984         {
18985             dprintf (3, ("or-%Ix", o));
18986         }
18987     }
18988     return o;
18989 }
18990
18991 void gc_heap::background_verify_mark (Object*& object, ScanContext* sc, uint32_t flags)
18992 {
18993     UNREFERENCED_PARAMETER(sc);
18994
18995     assert (settings.concurrent);
18996     uint8_t* o = (uint8_t*)object;
18997
18998     gc_heap* hp = gc_heap::heap_of (o);
18999 #ifdef INTERIOR_POINTERS
19000     if (flags & GC_CALL_INTERIOR)
19001     {
19002         o = hp->find_object (o, background_saved_lowest_address);
19003     }
19004 #endif //INTERIOR_POINTERS
19005
19006     if (!background_object_marked (o, FALSE))
19007     {
19008         FATAL_GC_ERROR();
19009     }
19010 }
19011
19012 void gc_heap::background_promote (Object** ppObject, ScanContext* sc, uint32_t flags)
19013 {
19014     UNREFERENCED_PARAMETER(sc);
19015     //in order to save space on the array, mark the object,
19016     //knowing that it will be visited later
19017     assert (settings.concurrent);
19018
19019     THREAD_NUMBER_FROM_CONTEXT;
19020 #ifndef MULTIPLE_HEAPS
19021     const int thread = 0;
19022 #endif //!MULTIPLE_HEAPS
19023
19024     uint8_t* o = (uint8_t*)*ppObject;
19025
19026     if (o == 0)
19027         return;
19028
19029 #ifdef DEBUG_DestroyedHandleValue
19030     // we can race with destroy handle during concurrent scan
19031     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
19032         return;
19033 #endif //DEBUG_DestroyedHandleValue
19034
19035     HEAP_FROM_THREAD;
19036
19037     gc_heap* hp = gc_heap::heap_of (o);
19038
19039     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
19040     {
19041         return;
19042     }
19043
19044 #ifdef INTERIOR_POINTERS
19045     if (flags & GC_CALL_INTERIOR)
19046     {
19047         o = hp->find_object (o, hp->background_saved_lowest_address);
19048         if (o == 0)
19049             return;
19050     }
19051 #endif //INTERIOR_POINTERS
19052
19053 #ifdef FEATURE_CONSERVATIVE_GC
19054     // For conservative GC, a value on stack may point to middle of a free object.
19055     // In this case, we don't need to promote the pointer.
19056     if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
19057     {
19058         return;
19059     }
19060 #endif //FEATURE_CONSERVATIVE_GC
19061
19062 #ifdef _DEBUG
19063     ((CObjectHeader*)o)->Validate();
19064 #endif //_DEBUG
19065
19066     dprintf (BGC_LOG, ("Background Promote %Ix", (size_t)o));
19067
19068     //needs to be called before the marking because it is possible for a foreground
19069     //gc to take place during the mark and move the object
19070     STRESS_LOG3(LF_GC|LF_GCROOTS, LL_INFO1000000, "    GCHeap::Promote: Promote GC Root *%p = %p MT = %pT", ppObject, o, o ? ((Object*) o)->GetGCSafeMethodTable() : NULL);
19071
19072     hpt->background_mark_simple (o THREAD_NUMBER_ARG);
19073 }
19074
19075 //used by the ephemeral collection to scan the local background structures
19076 //containing references.
19077 void
19078 gc_heap::scan_background_roots (promote_func* fn, int hn, ScanContext *pSC)
19079 {
19080     ScanContext sc;
19081     if (pSC == 0)
19082         pSC = &sc;
19083
19084     pSC->thread_number = hn;
19085
19086 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
19087     pSC->pCurrentDomain = 0;
19088 #endif
19089
19090     BOOL relocate_p = (fn == &GCHeap::Relocate);
19091
19092     dprintf (3, ("Scanning background mark list"));
19093
19094     //scan mark_list
19095     size_t mark_list_finger = 0;
19096     while (mark_list_finger < c_mark_list_index)
19097     {
19098         uint8_t** o = &c_mark_list [mark_list_finger];
19099         if (!relocate_p)
19100         {
19101             // We may not be able to calculate the size during relocate as POPO
19102             // may have written over the object.
19103             size_t s = size (*o);
19104             assert (Align (s) >= Align (min_obj_size));
19105             dprintf(3,("background root %Ix", (size_t)*o));
19106         }
19107         (*fn) ((Object**)o, pSC, 0);
19108         mark_list_finger++;
19109     }
19110
19111     //scan the mark stack
19112     dprintf (3, ("Scanning background mark stack"));
19113
19114     uint8_t** finger = background_mark_stack_array;
19115     while (finger < background_mark_stack_tos)
19116     {
19117         if ((finger + 1) < background_mark_stack_tos)
19118         {
19119             // We need to check for the partial mark case here.
19120             uint8_t* parent_obj = *(finger + 1);
19121             if ((size_t)parent_obj & 1)
19122             {
19123                 uint8_t* place = *finger;
19124                 size_t place_offset = 0;
19125                 uint8_t* real_parent_obj = (uint8_t*)((size_t)parent_obj & ~1);
19126
19127                 if (relocate_p)
19128                 {
19129                     *(finger + 1) = real_parent_obj;
19130                     place_offset = place - real_parent_obj;
19131                     dprintf(3,("relocating background root %Ix", (size_t)real_parent_obj));
19132                     (*fn) ((Object**)(finger + 1), pSC, 0);
19133                     real_parent_obj = *(finger + 1);
19134                     *finger = real_parent_obj + place_offset;
19135                     *(finger + 1) = (uint8_t*)((size_t)real_parent_obj | 1);
19136                     dprintf(3,("roots changed to %Ix, %Ix", *finger, *(finger + 1)));
19137                 }
19138                 else
19139                 {
19140                     uint8_t** temp = &real_parent_obj;
19141                     dprintf(3,("marking background root %Ix", (size_t)real_parent_obj));
19142                     (*fn) ((Object**)temp, pSC, 0);
19143                 }
19144
19145                 finger += 2;
19146                 continue;
19147             }
19148         }
19149         dprintf(3,("background root %Ix", (size_t)*finger));
19150         (*fn) ((Object**)finger, pSC, 0);
19151         finger++;
19152     }
19153 }
19154
19155 inline
19156 void gc_heap::background_mark_through_object (uint8_t* oo THREAD_NUMBER_DCL)
19157 {
19158     if (contain_pointers (oo))
19159     {
19160         size_t total_refs = 0;
19161         size_t s = size (oo);
19162         go_through_object_nostart (method_table(oo), oo, s, po,
19163                           {
19164                             uint8_t* o = *po;
19165                             total_refs++;
19166                             background_mark_object (o THREAD_NUMBER_ARG);
19167                           }
19168             );
19169
19170         dprintf (3,("Background marking through %Ix went through %Id refs", 
19171                           (size_t)oo,
19172                            total_refs));
19173     }
19174 }
19175
19176 uint8_t* gc_heap::background_seg_end (heap_segment* seg, BOOL concurrent_p)
19177 {
19178     if (concurrent_p && (seg == saved_overflow_ephemeral_seg))
19179     {
19180         // for now we stop at where gen1 started when we started processing 
19181         return background_min_soh_overflow_address;
19182     }
19183     else
19184     {
19185         return heap_segment_allocated (seg);
19186     }
19187 }
19188
19189 uint8_t* gc_heap::background_first_overflow (uint8_t* min_add,
19190                                           heap_segment* seg,
19191                                           BOOL concurrent_p, 
19192                                           BOOL small_object_p)
19193 {
19194     uint8_t* o = 0;
19195
19196     if (small_object_p)
19197     {
19198         if (in_range_for_segment (min_add, seg))
19199         {
19200             // min_add was the beginning of gen1 when we did the concurrent
19201             // overflow. Now we could be in a situation where min_add is
19202             // actually the same as allocated for that segment (because
19203             // we expanded heap), in which case we can not call 
19204             // find first on this address or we will AV.
19205             if (min_add >= heap_segment_allocated (seg))
19206             {
19207                 return min_add;
19208             }
19209             else
19210             {
19211                 if (concurrent_p && 
19212                     ((seg == saved_overflow_ephemeral_seg) && (min_add >= background_min_soh_overflow_address)))
19213                 {
19214                     return background_min_soh_overflow_address;
19215                 }
19216                 else
19217                 {
19218                     o = find_first_object (min_add, heap_segment_mem (seg));
19219                     return o;
19220                 }
19221             }
19222         }
19223     }
19224
19225     o = max (heap_segment_mem (seg), min_add);
19226     return o;
19227 }
19228
19229 void gc_heap::background_process_mark_overflow_internal (int condemned_gen_number,
19230                                                          uint8_t* min_add, uint8_t* max_add,
19231                                                          BOOL concurrent_p)
19232 {
19233     if (concurrent_p)
19234     {
19235         current_bgc_state = bgc_overflow_soh;
19236     }
19237
19238     size_t total_marked_objects = 0;
19239
19240 #ifdef MULTIPLE_HEAPS
19241     int thread = heap_number;
19242 #endif //MULTIPLE_HEAPS
19243
19244     exclusive_sync* loh_alloc_lock = 0;
19245
19246     dprintf (2,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
19247 #ifdef MULTIPLE_HEAPS
19248     // We don't have each heap scan all heaps concurrently because we are worried about
19249     // multiple threads calling things like find_first_object.
19250     int h_start = (concurrent_p ? heap_number : 0);
19251     int h_end = (concurrent_p ? (heap_number + 1) : n_heaps);
19252     for (int hi = h_start; hi < h_end; hi++)
19253     {
19254         gc_heap*  hp = (concurrent_p ? this : g_heaps [(heap_number + hi) % n_heaps]);
19255
19256 #else
19257     {
19258         gc_heap*  hp = 0;
19259
19260 #endif //MULTIPLE_HEAPS
19261         BOOL small_object_segments = TRUE;
19262         int align_const = get_alignment_constant (small_object_segments);
19263         generation* gen = hp->generation_of (condemned_gen_number);
19264         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
19265         PREFIX_ASSUME(seg != NULL);
19266         loh_alloc_lock = hp->bgc_alloc_lock;
19267
19268         uint8_t* o = hp->background_first_overflow (min_add,
19269                                                     seg, 
19270                                                     concurrent_p, 
19271                                                     small_object_segments);
19272
19273         while (1)
19274         {
19275             while ((o < hp->background_seg_end (seg, concurrent_p)) && (o <= max_add))
19276             {
19277                 dprintf (3, ("considering %Ix", (size_t)o));
19278
19279                 size_t s;
19280
19281                 if (concurrent_p && !small_object_segments)
19282                 {
19283                     loh_alloc_lock->bgc_mark_set (o);
19284
19285                     if (((CObjectHeader*)o)->IsFree())
19286                     {
19287                         s = unused_array_size (o);
19288                     }
19289                     else
19290                     {
19291                         s = size (o);
19292                     }
19293                 }
19294                 else
19295                 {
19296                     s = size (o);
19297                 }
19298
19299                 if (background_object_marked (o, FALSE) && contain_pointers_or_collectible (o))
19300                 {
19301                     total_marked_objects++;
19302                     go_through_object_cl (method_table(o), o, s, poo,
19303                                           uint8_t* oo = *poo;
19304                                           background_mark_object (oo THREAD_NUMBER_ARG);
19305                                          );
19306                 }
19307
19308                 if (concurrent_p && !small_object_segments)
19309                 {
19310                     loh_alloc_lock->bgc_mark_done ();
19311                 }
19312
19313                 o = o + Align (s, align_const);
19314
19315                 if (concurrent_p)
19316                 {
19317                     allow_fgc();
19318                 }
19319             }
19320
19321             dprintf (2, ("went through overflow objects in segment %Ix (%d) (so far %Id marked)", 
19322                 heap_segment_mem (seg), (small_object_segments ? 0 : 1), total_marked_objects));
19323
19324             if ((concurrent_p && (seg == hp->saved_overflow_ephemeral_seg)) ||
19325                 (seg = heap_segment_next_in_range (seg)) == 0)
19326             {
19327                 if (small_object_segments)
19328                 {
19329                     if (concurrent_p)
19330                     {
19331                         current_bgc_state = bgc_overflow_loh;
19332                     }
19333
19334                     dprintf (2, ("h%d: SOH: ov-mo: %Id", heap_number, total_marked_objects));
19335                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
19336                     concurrent_print_time_delta (concurrent_p ? "Cov SOH" : "Nov SOH");
19337                     total_marked_objects = 0;
19338                     small_object_segments = FALSE;
19339                     align_const = get_alignment_constant (small_object_segments);
19340                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
19341
19342                     PREFIX_ASSUME(seg != NULL);
19343
19344                     o = max (heap_segment_mem (seg), min_add);
19345                     continue;
19346                 }
19347                 else
19348                 {
19349                     dprintf (GTC_LOG, ("h%d: LOH: ov-mo: %Id", heap_number, total_marked_objects));
19350                     fire_overflow_event (min_add, max_add, total_marked_objects, !small_object_segments);
19351                     break;
19352                 }
19353             } 
19354             else
19355             {
19356                 o = hp->background_first_overflow (min_add, 
19357                                                    seg, 
19358                                                    concurrent_p, 
19359                                                    small_object_segments);
19360                 continue;
19361             }
19362         }
19363     }
19364 }
19365
19366 BOOL gc_heap::background_process_mark_overflow (BOOL concurrent_p)
19367 {
19368     BOOL grow_mark_array_p = TRUE;
19369
19370     if (concurrent_p)
19371     {
19372         assert (!processed_soh_overflow_p);
19373
19374         if ((background_max_overflow_address != 0) &&
19375             (background_min_overflow_address != MAX_PTR))
19376         {
19377             // We have overflow to process but we know we can't process the ephemeral generations
19378             // now (we actually could process till the current gen1 start but since we are going to 
19379             // make overflow per segment, for now I'll just stop at the saved gen1 start.
19380             saved_overflow_ephemeral_seg = ephemeral_heap_segment;
19381             background_max_soh_overflow_address = heap_segment_reserved (saved_overflow_ephemeral_seg);
19382             background_min_soh_overflow_address = generation_allocation_start (generation_of (max_generation-1));
19383         }
19384     }
19385     else
19386     {
19387         assert ((saved_overflow_ephemeral_seg == 0) || 
19388                 ((background_max_soh_overflow_address != 0) &&
19389                  (background_min_soh_overflow_address != MAX_PTR)));
19390         
19391         if (!processed_soh_overflow_p)
19392         {
19393             // if there was no more overflow we just need to process what we didn't process 
19394             // on the saved ephemeral segment.
19395             if ((background_max_overflow_address == 0) && (background_min_overflow_address == MAX_PTR))
19396             {
19397                 dprintf (2, ("final processing mark overflow - no more overflow since last time"));
19398                 grow_mark_array_p = FALSE;
19399             }
19400
19401             background_min_overflow_address = min (background_min_overflow_address, 
19402                                                 background_min_soh_overflow_address);
19403             background_max_overflow_address = max (background_max_overflow_address,
19404                                                 background_max_soh_overflow_address);
19405             processed_soh_overflow_p = TRUE;
19406         }
19407     }
19408
19409     BOOL  overflow_p = FALSE;
19410 recheck:
19411     if ((! ((background_max_overflow_address == 0)) ||
19412          ! ((background_min_overflow_address == MAX_PTR))))
19413     {
19414         overflow_p = TRUE;
19415
19416         if (grow_mark_array_p)
19417         {
19418             // Try to grow the array.
19419             size_t new_size = max (MARK_STACK_INITIAL_LENGTH, 2*background_mark_stack_array_length);
19420
19421             if ((new_size * sizeof(mark)) > 100*1024)
19422             {
19423                 size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19424
19425                 new_size = min(new_max_size, new_size);
19426             }
19427
19428             if ((background_mark_stack_array_length < new_size) && 
19429                 ((new_size - background_mark_stack_array_length) > (background_mark_stack_array_length / 2)))
19430             {
19431                 dprintf (2, ("h%d: ov grow to %Id", heap_number, new_size));
19432
19433                 uint8_t** tmp = new (nothrow) uint8_t* [new_size];
19434                 if (tmp)
19435                 {
19436                     delete background_mark_stack_array;
19437                     background_mark_stack_array = tmp;
19438                     background_mark_stack_array_length = new_size;
19439                     background_mark_stack_tos = background_mark_stack_array;
19440                 }
19441             }
19442         }
19443         else
19444         {
19445             grow_mark_array_p = TRUE;
19446         }
19447
19448         uint8_t*  min_add = background_min_overflow_address;
19449         uint8_t*  max_add = background_max_overflow_address;
19450
19451         background_max_overflow_address = 0;
19452         background_min_overflow_address = MAX_PTR;
19453
19454         background_process_mark_overflow_internal (max_generation, min_add, max_add, concurrent_p);
19455         if (!concurrent_p)
19456         {        
19457             goto recheck;
19458         }
19459     }
19460
19461     return overflow_p;
19462 }
19463
19464 #endif //BACKGROUND_GC
19465
19466 inline
19467 void gc_heap::mark_through_object (uint8_t* oo, BOOL mark_class_object_p THREAD_NUMBER_DCL)
19468 {
19469 #ifndef COLLECTIBLE_CLASS
19470     UNREFERENCED_PARAMETER(mark_class_object_p);
19471     BOOL to_mark_class_object = FALSE;
19472 #else //COLLECTIBLE_CLASS
19473     BOOL to_mark_class_object = (mark_class_object_p && (is_collectible(oo)));
19474 #endif //COLLECTIBLE_CLASS
19475     if (contain_pointers (oo) || to_mark_class_object)
19476     {
19477         dprintf(3,( "Marking through %Ix", (size_t)oo));
19478         size_t s = size (oo);
19479
19480 #ifdef COLLECTIBLE_CLASS
19481         if (to_mark_class_object)
19482         {
19483             uint8_t* class_obj = get_class_object (oo);
19484             mark_object (class_obj THREAD_NUMBER_ARG);
19485         }
19486 #endif //COLLECTIBLE_CLASS
19487
19488         if (contain_pointers (oo))
19489         {
19490             go_through_object_nostart (method_table(oo), oo, s, po,
19491                                 uint8_t* o = *po;
19492                                 mark_object (o THREAD_NUMBER_ARG);
19493                                 );
19494         }
19495     }
19496 }
19497
19498 size_t gc_heap::get_total_heap_size()
19499 {
19500     size_t total_heap_size = 0;
19501
19502 #ifdef MULTIPLE_HEAPS
19503     int hn = 0;
19504
19505     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19506     {
19507         gc_heap* hp2 = gc_heap::g_heaps [hn];
19508         total_heap_size += hp2->generation_size (max_generation + 1) + hp2->generation_sizes (hp2->generation_of (max_generation));
19509     }
19510 #else
19511     total_heap_size = generation_size (max_generation + 1) + generation_sizes (generation_of (max_generation));
19512 #endif //MULTIPLE_HEAPS
19513
19514     return total_heap_size;
19515 }
19516
19517 size_t gc_heap::get_total_fragmentation()
19518 {
19519     size_t total_fragmentation = 0;
19520
19521 #ifdef MULTIPLE_HEAPS
19522     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
19523     {
19524         gc_heap* hp = gc_heap::g_heaps[hn];
19525 #else //MULTIPLE_HEAPS
19526     {
19527         gc_heap* hp = pGenGCHeap;
19528 #endif //MULTIPLE_HEAPS
19529         for (int i = 0; i <= (max_generation + 1); i++)
19530         {
19531             generation* gen = hp->generation_of (i);
19532             total_fragmentation += (generation_free_list_space (gen) + generation_free_obj_space (gen));
19533         }
19534     }
19535
19536     return total_fragmentation;
19537 }
19538
19539 size_t gc_heap::get_total_gen_fragmentation (int gen_number)
19540 {
19541     size_t total_fragmentation = 0;
19542
19543 #ifdef MULTIPLE_HEAPS
19544     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
19545     {
19546         gc_heap* hp = gc_heap::g_heaps[hn];
19547 #else //MULTIPLE_HEAPS
19548     {
19549         gc_heap* hp = pGenGCHeap;
19550 #endif //MULTIPLE_HEAPS
19551         generation* gen = hp->generation_of (gen_number);
19552         total_fragmentation += (generation_free_list_space (gen) + generation_free_obj_space (gen));
19553     }
19554
19555     return total_fragmentation;
19556 }
19557
19558 size_t gc_heap::get_total_gen_estimated_reclaim (int gen_number)
19559 {
19560     size_t total_estimated_reclaim = 0;
19561
19562 #ifdef MULTIPLE_HEAPS
19563     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
19564     {
19565         gc_heap* hp = gc_heap::g_heaps[hn];
19566 #else //MULTIPLE_HEAPS
19567     {
19568         gc_heap* hp = pGenGCHeap;
19569 #endif //MULTIPLE_HEAPS
19570         total_estimated_reclaim += hp->estimated_reclaim (gen_number);
19571     }
19572
19573     return total_estimated_reclaim;
19574 }
19575
19576 size_t gc_heap::committed_size()
19577 {
19578     generation* gen = generation_of (max_generation);
19579     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19580     size_t total_committed = 0;
19581
19582     while (1)
19583     {
19584         total_committed += heap_segment_committed (seg) - (uint8_t*)seg;
19585
19586         seg = heap_segment_next (seg);
19587         if (!seg)
19588         {
19589             if (gen != large_object_generation)
19590             {
19591                 gen = generation_of (max_generation + 1);
19592                 seg = generation_start_segment (gen);
19593             }
19594             else
19595                 break;
19596         }
19597     }
19598
19599     return total_committed;
19600 }
19601
19602 size_t gc_heap::get_total_committed_size()
19603 {
19604     size_t total_committed = 0;
19605
19606 #ifdef MULTIPLE_HEAPS
19607     int hn = 0;
19608
19609     for (hn = 0; hn < gc_heap::n_heaps; hn++)
19610     {
19611         gc_heap* hp = gc_heap::g_heaps [hn];
19612         total_committed += hp->committed_size();
19613     }
19614 #else
19615     total_committed = committed_size();
19616 #endif //MULTIPLE_HEAPS
19617
19618     return total_committed;
19619 }
19620
19621 size_t gc_heap::committed_size (bool loh_p, size_t* allocated)
19622 {
19623     int gen_number = (loh_p ? (max_generation + 1) : max_generation);
19624     generation* gen = generation_of (gen_number);
19625     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
19626     size_t total_committed = 0;
19627     size_t total_allocated = 0;
19628
19629     while (seg)
19630     {
19631         total_committed += heap_segment_committed (seg) - (uint8_t*)seg;
19632         total_allocated += heap_segment_allocated (seg) - (uint8_t*)seg;
19633         seg = heap_segment_next (seg);
19634     }
19635
19636     *allocated = total_allocated;
19637     return total_committed;
19638 }
19639
19640 void gc_heap::get_memory_info (uint32_t* memory_load, 
19641                                uint64_t* available_physical,
19642                                uint64_t* available_page_file)
19643 {
19644     GCToOSInterface::GetMemoryStatus(memory_load, available_physical, available_page_file);
19645 }
19646
19647 void fire_mark_event (int heap_num, int root_type, size_t bytes_marked)
19648 {
19649     dprintf (DT_LOG_0, ("-----------[%d]mark %d: %Id", heap_num, root_type, bytes_marked));
19650     FIRE_EVENT(GCMarkWithType, heap_num, root_type, bytes_marked);
19651 }
19652
19653 //returns TRUE is an overflow happened.
19654 BOOL gc_heap::process_mark_overflow(int condemned_gen_number)
19655 {
19656     size_t last_promoted_bytes = promoted_bytes (heap_number);
19657     BOOL  overflow_p = FALSE;
19658 recheck:
19659     if ((! (max_overflow_address == 0) ||
19660          ! (min_overflow_address == MAX_PTR)))
19661     {
19662         overflow_p = TRUE;
19663         // Try to grow the array.
19664         size_t new_size =
19665             max (MARK_STACK_INITIAL_LENGTH, 2*mark_stack_array_length);
19666
19667         if ((new_size * sizeof(mark)) > 100*1024)
19668         {
19669             size_t new_max_size = (get_total_heap_size() / 10) / sizeof(mark);
19670
19671             new_size = min(new_max_size, new_size);
19672         }
19673
19674         if ((mark_stack_array_length < new_size) && 
19675             ((new_size - mark_stack_array_length) > (mark_stack_array_length / 2)))
19676         {
19677             mark* tmp = new (nothrow) mark [new_size];
19678             if (tmp)
19679             {
19680                 delete mark_stack_array;
19681                 mark_stack_array = tmp;
19682                 mark_stack_array_length = new_size;
19683             }
19684         }
19685
19686         uint8_t*  min_add = min_overflow_address;
19687         uint8_t*  max_add = max_overflow_address;
19688         max_overflow_address = 0;
19689         min_overflow_address = MAX_PTR;
19690         process_mark_overflow_internal (condemned_gen_number, min_add, max_add);
19691         goto recheck;
19692     }
19693
19694     size_t current_promoted_bytes = promoted_bytes (heap_number);
19695
19696     if (current_promoted_bytes != last_promoted_bytes)
19697         fire_mark_event (heap_number, ETW::GC_ROOT_OVERFLOW, (current_promoted_bytes - last_promoted_bytes));
19698     return overflow_p;
19699 }
19700
19701 void gc_heap::process_mark_overflow_internal (int condemned_gen_number,
19702                                               uint8_t* min_add, uint8_t* max_add)
19703 {
19704 #ifdef MULTIPLE_HEAPS
19705     int thread = heap_number;
19706 #endif //MULTIPLE_HEAPS
19707     BOOL  full_p = (condemned_gen_number == max_generation);
19708
19709         dprintf(3,("Processing Mark overflow [%Ix %Ix]", (size_t)min_add, (size_t)max_add));
19710 #ifdef MULTIPLE_HEAPS
19711             for (int hi = 0; hi < n_heaps; hi++)
19712             {
19713                 gc_heap*  hp = g_heaps [(heap_number + hi) % n_heaps];
19714
19715 #else
19716             {
19717                 gc_heap*  hp = 0;
19718
19719 #endif //MULTIPLE_HEAPS
19720         BOOL small_object_segments = TRUE;
19721         int align_const = get_alignment_constant (small_object_segments);
19722         generation* gen = hp->generation_of (condemned_gen_number);
19723         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
19724         
19725         PREFIX_ASSUME(seg != NULL);
19726         uint8_t*  o = max (heap_segment_mem (seg), min_add);
19727         while (1)
19728         {
19729             uint8_t*  end = heap_segment_allocated (seg);
19730
19731             while ((o < end) && (o <= max_add))
19732             {
19733                 assert ((min_add <= o) && (max_add >= o));
19734                 dprintf (3, ("considering %Ix", (size_t)o));
19735                 if (marked (o))
19736                 {
19737                     mark_through_object (o, TRUE THREAD_NUMBER_ARG);
19738                 }
19739
19740                 o = o + Align (size (o), align_const);
19741             }
19742
19743             if (( seg = heap_segment_next_in_range (seg)) == 0)
19744             {
19745                 if (small_object_segments && full_p)
19746                 {
19747                     small_object_segments = FALSE;
19748                     align_const = get_alignment_constant (small_object_segments);
19749                     seg = heap_segment_in_range (generation_start_segment (hp->generation_of (max_generation+1)));
19750
19751                     PREFIX_ASSUME(seg != NULL);
19752
19753                     o = max (heap_segment_mem (seg), min_add);
19754                     continue;
19755                 }
19756                 else
19757                 {
19758                     break;
19759                 } 
19760             } 
19761             else
19762             {
19763                 o = max (heap_segment_mem (seg), min_add);
19764                 continue;
19765             }
19766         }
19767     }
19768 }
19769
19770 // Scanning for promotion for dependent handles need special handling. Because the primary holds a strong
19771 // reference to the secondary (when the primary itself is reachable) and this can cause a cascading series of
19772 // promotions (the secondary of one handle is or promotes the primary of another) we might need to perform the
19773 // promotion scan multiple times.
19774 // This helper encapsulates the logic to complete all dependent handle promotions when running a server GC. It
19775 // also has the effect of processing any mark stack overflow.
19776
19777 #ifdef MULTIPLE_HEAPS
19778 // When multiple heaps are enabled we have must utilize a more complex algorithm in order to keep all the GC
19779 // worker threads synchronized. The algorithms are sufficiently divergent that we have different
19780 // implementations based on whether MULTIPLE_HEAPS is defined or not.
19781 //
19782 // Define some static variables used for synchronization in the method below. These should really be defined
19783 // locally but MSVC complains when the VOLATILE macro is expanded into an instantiation of the Volatile class.
19784 //
19785 // A note about the synchronization used within this method. Communication between the worker threads is
19786 // achieved via two shared booleans (defined below). These both act as latches that are transitioned only from
19787 // false -> true by unsynchronized code. They are only read or reset to false by a single thread under the
19788 // protection of a join.
19789 static VOLATILE(BOOL) s_fUnpromotedHandles = FALSE;
19790 static VOLATILE(BOOL) s_fUnscannedPromotions = FALSE;
19791 static VOLATILE(BOOL) s_fScanRequired;
19792 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19793 {
19794     // Whenever we call this method there may have been preceding object promotions. So set
19795     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19796     // based on the how the scanning proceeded).
19797     s_fUnscannedPromotions = TRUE;
19798
19799     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
19800     // the state of this thread's portion of the dependent handle table. That's because promotions on other
19801     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
19802     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
19803     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
19804     // as all the others or they'll get out of step).
19805     while (true)
19806     {
19807         // The various worker threads are all currently racing in this code. We need to work out if at least
19808         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
19809         // dependent handle table when both of the following conditions apply:
19810         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
19811         //     object happens to correspond to a primary in one of our handles we might potentially have to
19812         //     promote the associated secondary).
19813         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
19814         //
19815         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
19816         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
19817         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
19818         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
19819         // follows below. Note that we can't read this outside of the join since on any iteration apart from
19820         // the first threads will be racing between reading this value and completing their previous
19821         // iteration's table scan.
19822         //
19823         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
19824         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
19825         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
19826         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
19827         // we're safely joined.
19828         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19829             s_fUnpromotedHandles = TRUE;
19830
19831         // Synchronize all the threads so we can read our state variables safely. The shared variable
19832         // s_fScanRequired, indicating whether we should scan the tables or terminate the loop, will be set by
19833         // a single thread inside the join.
19834         gc_t_join.join(this, gc_join_scan_dependent_handles);
19835         if (gc_t_join.joined())
19836         {
19837             // We're synchronized so it's safe to read our shared state variables. We update another shared
19838             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
19839             // the loop. We scan if there has been at least one object promotion since last time and at least
19840             // one thread has a dependent handle table with a potential handle promotion possible.
19841             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
19842
19843             // Reset our shared state variables (ready to be set again on this scan or with a good initial
19844             // value for the next call if we're terminating the loop).
19845             s_fUnscannedPromotions = FALSE;
19846             s_fUnpromotedHandles = FALSE;
19847
19848             if (!s_fScanRequired)
19849             {
19850                 // We're terminating the loop. Perform any last operations that require single threaded access.
19851                 if (!initial_scan_p)
19852                 {
19853                     // On the second invocation we reconcile all mark overflow ranges across the heaps. This can help
19854                     // load balance if some of the heaps have an abnormally large workload.
19855                     uint8_t* all_heaps_max = 0;
19856                     uint8_t* all_heaps_min = MAX_PTR;
19857                     int i;
19858                     for (i = 0; i < n_heaps; i++)
19859                     {
19860                         if (all_heaps_max < g_heaps[i]->max_overflow_address)
19861                             all_heaps_max = g_heaps[i]->max_overflow_address;
19862                         if (all_heaps_min > g_heaps[i]->min_overflow_address)
19863                             all_heaps_min = g_heaps[i]->min_overflow_address;
19864                     }
19865                     for (i = 0; i < n_heaps; i++)
19866                     {
19867                         g_heaps[i]->max_overflow_address = all_heaps_max;
19868                         g_heaps[i]->min_overflow_address = all_heaps_min;
19869                     }
19870                 }
19871             }
19872
19873             // Restart all the workers.
19874             dprintf(3, ("Starting all gc thread mark stack overflow processing"));
19875             gc_t_join.restart();
19876         }
19877
19878         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19879         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
19880         // global flag indicating that at least one object promotion may have occurred (the usual comment
19881         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
19882         // exit the method since we unconditionally set this variable on method entry anyway).
19883         if (process_mark_overflow(condemned_gen_number))
19884             s_fUnscannedPromotions = TRUE;
19885
19886         // If we decided that no scan was required we can terminate the loop now.
19887         if (!s_fScanRequired)
19888             break;
19889
19890         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
19891         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
19892         // could miss noting the promotion of some primary objects).
19893         gc_t_join.join(this, gc_join_rescan_dependent_handles);
19894         if (gc_t_join.joined())
19895         {
19896             // Restart all the workers.
19897             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
19898             gc_t_join.restart();
19899         }
19900
19901         // If the portion of the dependent handle table managed by this worker has handles that could still be
19902         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
19903         // could require a rescan of handles on this or other workers.
19904         if (GCScan::GcDhUnpromotedHandlesExist(sc))
19905             if (GCScan::GcDhReScan(sc))
19906                 s_fUnscannedPromotions = TRUE;
19907     }
19908 }
19909 #else //MULTIPLE_HEAPS
19910 // Non-multiple heap version of scan_dependent_handles: much simpler without the need to keep multiple worker
19911 // threads synchronized.
19912 void gc_heap::scan_dependent_handles (int condemned_gen_number, ScanContext *sc, BOOL initial_scan_p)
19913 {
19914     UNREFERENCED_PARAMETER(initial_scan_p);
19915
19916     // Whenever we call this method there may have been preceding object promotions. So set
19917     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
19918     // based on the how the scanning proceeded).
19919     bool fUnscannedPromotions = true;
19920
19921     // Loop until there are either no more dependent handles that can have their secondary promoted or we've
19922     // managed to perform a scan without promoting anything new.
19923     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
19924     {
19925         // On each iteration of the loop start with the assumption that no further objects have been promoted.
19926         fUnscannedPromotions = false;
19927
19928         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
19929         // being visible. If there was an overflow (process_mark_overflow returned true) then additional
19930         // objects now appear to be promoted and we should set the flag.
19931         if (process_mark_overflow(condemned_gen_number))
19932             fUnscannedPromotions = true;
19933
19934         // Perform the scan and set the flag if any promotions resulted.
19935         if (GCScan::GcDhReScan(sc))
19936             fUnscannedPromotions = true;
19937     }
19938
19939     // Process any mark stack overflow that may have resulted from scanning handles (or if we didn't need to
19940     // scan any handles at all this is the processing of overflows that may have occurred prior to this method
19941     // invocation).
19942     process_mark_overflow(condemned_gen_number);
19943 }
19944 #endif //MULTIPLE_HEAPS
19945
19946 void gc_heap::mark_phase (int condemned_gen_number, BOOL mark_only_p)
19947 {
19948     assert (settings.concurrent == FALSE);
19949
19950     ScanContext sc;
19951     sc.thread_number = heap_number;
19952     sc.promotion = TRUE;
19953     sc.concurrent = FALSE;
19954
19955     dprintf(2,("---- Mark Phase condemning %d ----", condemned_gen_number));
19956     BOOL  full_p = (condemned_gen_number == max_generation);
19957
19958 #ifdef TIME_GC
19959     unsigned start;
19960     unsigned finish;
19961     start = GetCycleCount32();
19962 #endif //TIME_GC
19963
19964     int gen_to_init = condemned_gen_number;
19965     if (condemned_gen_number == max_generation)
19966     {
19967         gen_to_init = max_generation + 1;
19968     }
19969     for (int gen_idx = 0; gen_idx <= gen_to_init; gen_idx++)
19970     {
19971         dynamic_data* dd = dynamic_data_of (gen_idx);
19972         dd_begin_data_size (dd) = generation_size (gen_idx) - 
19973                                    dd_fragmentation (dd) -
19974                                    Align (size (generation_allocation_start (generation_of (gen_idx))));
19975         dprintf (2, ("begin data size for gen%d is %Id", gen_idx, dd_begin_data_size (dd)));
19976         dd_survived_size (dd) = 0;
19977         dd_pinned_survived_size (dd) = 0;
19978         dd_artificial_pinned_survived_size (dd) = 0;
19979         dd_added_pinned_size (dd) = 0;
19980 #ifdef SHORT_PLUGS
19981         dd_padding_size (dd) = 0;
19982 #endif //SHORT_PLUGS
19983 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
19984         dd_num_npinned_plugs (dd) = 0;
19985 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
19986     }
19987
19988 #ifdef FFIND_OBJECT
19989     if (gen0_must_clear_bricks > 0)
19990         gen0_must_clear_bricks--;
19991 #endif //FFIND_OBJECT
19992
19993     size_t last_promoted_bytes = 0;
19994
19995     promoted_bytes (heap_number) = 0;
19996     reset_mark_stack();
19997
19998 #ifdef SNOOP_STATS
19999     memset (&snoop_stat, 0, sizeof(snoop_stat));
20000     snoop_stat.heap_index = heap_number;
20001 #endif //SNOOP_STATS
20002
20003 #ifdef MH_SC_MARK
20004     if (full_p)
20005     {
20006         //initialize the mark stack
20007         for (int i = 0; i < max_snoop_level; i++)
20008         {
20009             ((uint8_t**)(mark_stack_array))[i] = 0;
20010         }
20011
20012         mark_stack_busy() = 1;
20013     }
20014 #endif //MH_SC_MARK
20015
20016     static uint32_t num_sizedrefs = 0;
20017
20018 #ifdef MH_SC_MARK
20019     static BOOL do_mark_steal_p = FALSE;
20020 #endif //MH_SC_MARK
20021
20022 #ifdef MULTIPLE_HEAPS
20023     gc_t_join.join(this, gc_join_begin_mark_phase);
20024     if (gc_t_join.joined())
20025     {
20026 #endif //MULTIPLE_HEAPS
20027
20028         maxgen_size_inc_p = false;
20029
20030         num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
20031
20032 #ifdef MULTIPLE_HEAPS
20033
20034 #ifdef MH_SC_MARK
20035         if (full_p)
20036         {
20037             size_t total_heap_size = get_total_heap_size();
20038
20039             if (total_heap_size > (100 * 1024 * 1024))
20040             {
20041                 do_mark_steal_p = TRUE;
20042             }
20043             else
20044             {
20045                 do_mark_steal_p = FALSE;
20046             }
20047         }
20048         else
20049         {
20050             do_mark_steal_p = FALSE;
20051         }
20052 #endif //MH_SC_MARK
20053
20054         gc_t_join.restart();
20055     }
20056 #endif //MULTIPLE_HEAPS
20057
20058     {
20059
20060 #ifdef MARK_LIST
20061         //set up the mark lists from g_mark_list
20062         assert (g_mark_list);
20063 #ifdef MULTIPLE_HEAPS
20064         mark_list = &g_mark_list [heap_number*mark_list_size];
20065 #else
20066         mark_list = g_mark_list;
20067 #endif //MULTIPLE_HEAPS
20068         //dont use the mark list for full gc
20069         //because multiple segments are more complex to handle and the list
20070         //is likely to overflow
20071         if (condemned_gen_number != max_generation)
20072             mark_list_end = &mark_list [mark_list_size-1];
20073         else
20074             mark_list_end = &mark_list [0];
20075         mark_list_index = &mark_list [0];
20076 #endif //MARK_LIST
20077
20078 #ifndef MULTIPLE_HEAPS
20079         shigh = (uint8_t*) 0;
20080         slow  = MAX_PTR;
20081 #endif //MULTIPLE_HEAPS
20082
20083         //%type%  category = quote (mark);
20084
20085         if ((condemned_gen_number == max_generation) && (num_sizedrefs > 0))
20086         {
20087             GCScan::GcScanSizedRefs(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
20088             fire_mark_event (heap_number, ETW::GC_ROOT_SIZEDREF, (promoted_bytes (heap_number) - last_promoted_bytes));
20089             last_promoted_bytes = promoted_bytes (heap_number);
20090
20091 #ifdef MULTIPLE_HEAPS
20092             gc_t_join.join(this, gc_join_scan_sizedref_done);
20093             if (gc_t_join.joined())
20094             {
20095                 dprintf(3, ("Done with marking all sized refs. Starting all gc thread for marking other strong roots"));
20096                 gc_t_join.restart();
20097             }
20098 #endif //MULTIPLE_HEAPS
20099         }
20100     
20101         dprintf(3,("Marking Roots"));
20102
20103         GCScan::GcScanRoots(GCHeap::Promote,
20104                                 condemned_gen_number, max_generation,
20105                                 &sc);
20106
20107         fire_mark_event (heap_number, ETW::GC_ROOT_STACK, (promoted_bytes (heap_number) - last_promoted_bytes));
20108         last_promoted_bytes = promoted_bytes (heap_number);
20109
20110 #ifdef BACKGROUND_GC
20111         if (recursive_gc_sync::background_running_p())
20112         {
20113             scan_background_roots (GCHeap::Promote, heap_number, &sc);
20114         }
20115 #endif //BACKGROUND_GC
20116
20117 #ifdef FEATURE_PREMORTEM_FINALIZATION
20118         dprintf(3, ("Marking finalization data"));
20119         finalize_queue->GcScanRoots(GCHeap::Promote, heap_number, 0);
20120 #endif // FEATURE_PREMORTEM_FINALIZATION
20121
20122         fire_mark_event (heap_number, ETW::GC_ROOT_FQ, (promoted_bytes (heap_number) - last_promoted_bytes));
20123         last_promoted_bytes = promoted_bytes (heap_number);
20124
20125 // MTHTS
20126         {
20127
20128             dprintf(3,("Marking handle table"));
20129             GCScan::GcScanHandles(GCHeap::Promote,
20130                                       condemned_gen_number, max_generation,
20131                                       &sc);
20132             fire_mark_event (heap_number, ETW::GC_ROOT_HANDLES, (promoted_bytes (heap_number) - last_promoted_bytes));
20133             last_promoted_bytes = promoted_bytes (heap_number);
20134         }
20135
20136 #ifdef TRACE_GC
20137         size_t promoted_before_cards = promoted_bytes (heap_number);
20138 #endif //TRACE_GC
20139
20140         dprintf (3, ("before cards: %Id", promoted_before_cards));
20141         if (!full_p)
20142         {
20143 #ifdef CARD_BUNDLE
20144 #ifdef MULTIPLE_HEAPS
20145             if (gc_t_join.r_join(this, gc_r_join_update_card_bundle))
20146             {
20147 #endif //MULTIPLE_HEAPS
20148
20149 #ifndef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
20150                 // If we are manually managing card bundles, every write to the card table should already be
20151                 // accounted for in the card bundle table so there's nothing to update here.
20152                 update_card_table_bundle();
20153 #endif
20154                 if (card_bundles_enabled())
20155                 {
20156                     verify_card_bundles();
20157                 }
20158
20159 #ifdef MULTIPLE_HEAPS
20160                 gc_t_join.r_restart();
20161             }
20162 #endif //MULTIPLE_HEAPS
20163 #endif //CARD_BUNDLE
20164
20165             card_fn mark_object_fn = &gc_heap::mark_object_simple;
20166 #ifdef HEAP_ANALYZE
20167             heap_analyze_success = TRUE;
20168             if (heap_analyze_enabled)
20169             {
20170                 internal_root_array_index = 0;
20171                 current_obj = 0;
20172                 current_obj_size = 0;
20173                 mark_object_fn = &gc_heap::ha_mark_object_simple;
20174             }
20175 #endif //HEAP_ANALYZE
20176
20177             dprintf(3,("Marking cross generation pointers"));
20178             mark_through_cards_for_segments (mark_object_fn, FALSE);
20179
20180             dprintf(3,("Marking cross generation pointers for large objects"));
20181             mark_through_cards_for_large_objects (mark_object_fn, FALSE);
20182
20183             dprintf (3, ("marked by cards: %Id", 
20184                 (promoted_bytes (heap_number) - promoted_before_cards)));
20185             fire_mark_event (heap_number, ETW::GC_ROOT_OLDER, (promoted_bytes (heap_number) - last_promoted_bytes));
20186             last_promoted_bytes = promoted_bytes (heap_number);
20187         }
20188     }
20189
20190 #ifdef MH_SC_MARK
20191     if (do_mark_steal_p)
20192     {
20193         mark_steal();
20194     }
20195 #endif //MH_SC_MARK
20196
20197     // Dependent handles need to be scanned with a special algorithm (see the header comment on
20198     // scan_dependent_handles for more detail). We perform an initial scan without synchronizing with other
20199     // worker threads or processing any mark stack overflow. This is not guaranteed to complete the operation
20200     // but in a common case (where there are no dependent handles that are due to be collected) it allows us
20201     // to optimize away further scans. The call to scan_dependent_handles is what will cycle through more
20202     // iterations if required and will also perform processing of any mark stack overflow once the dependent
20203     // handle table has been fully promoted.
20204     GCScan::GcDhInitialScan(GCHeap::Promote, condemned_gen_number, max_generation, &sc);
20205     scan_dependent_handles(condemned_gen_number, &sc, true);
20206
20207 #ifdef MULTIPLE_HEAPS
20208     dprintf(3, ("Joining for short weak handle scan"));
20209     gc_t_join.join(this, gc_join_null_dead_short_weak);
20210     if (gc_t_join.joined())
20211 #endif //MULTIPLE_HEAPS
20212     {
20213 #ifdef HEAP_ANALYZE
20214         heap_analyze_enabled = FALSE;
20215         GCToEEInterface::AnalyzeSurvivorsFinished(condemned_gen_number);
20216 #endif // HEAP_ANALYZE
20217         GCToEEInterface::AfterGcScanRoots (condemned_gen_number, max_generation, &sc);
20218
20219 #ifdef MULTIPLE_HEAPS
20220         if (!full_p)
20221         {
20222             // we used r_join and need to reinitialize states for it here.
20223             gc_t_join.r_init();
20224         }
20225
20226         //start all threads on the roots.
20227         dprintf(3, ("Starting all gc thread for short weak handle scan"));
20228         gc_t_join.restart();
20229 #endif //MULTIPLE_HEAPS
20230
20231     }
20232
20233     // null out the target of short weakref that were not promoted.
20234     GCScan::GcShortWeakPtrScan(GCHeap::Promote, condemned_gen_number, max_generation,&sc);
20235
20236 // MTHTS: keep by single thread
20237 #ifdef MULTIPLE_HEAPS
20238     dprintf(3, ("Joining for finalization"));
20239     gc_t_join.join(this, gc_join_scan_finalization);
20240     if (gc_t_join.joined())
20241 #endif //MULTIPLE_HEAPS
20242
20243     {
20244 #ifdef MULTIPLE_HEAPS
20245         //start all threads on the roots.
20246         dprintf(3, ("Starting all gc thread for Finalization"));
20247         gc_t_join.restart();
20248 #endif //MULTIPLE_HEAPS
20249     }
20250
20251     //Handle finalization.
20252     size_t promoted_bytes_live = promoted_bytes (heap_number);
20253
20254 #ifdef FEATURE_PREMORTEM_FINALIZATION
20255     dprintf (3, ("Finalize marking"));
20256     finalize_queue->ScanForFinalization (GCHeap::Promote, condemned_gen_number, mark_only_p, __this);
20257
20258     GCToEEInterface::DiagWalkFReachableObjects(__this);
20259 #endif // FEATURE_PREMORTEM_FINALIZATION
20260
20261     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
20262     // for finalization. As before scan_dependent_handles will also process any mark stack overflow.
20263     scan_dependent_handles(condemned_gen_number, &sc, false);
20264
20265 #ifdef MULTIPLE_HEAPS
20266     dprintf(3, ("Joining for weak pointer deletion"));
20267     gc_t_join.join(this, gc_join_null_dead_long_weak);
20268     if (gc_t_join.joined())
20269     {
20270         //start all threads on the roots.
20271         dprintf(3, ("Starting all gc thread for weak pointer deletion"));
20272         gc_t_join.restart();
20273     }
20274 #endif //MULTIPLE_HEAPS
20275
20276     // null out the target of long weakref that were not promoted.
20277     GCScan::GcWeakPtrScan (GCHeap::Promote, condemned_gen_number, max_generation, &sc);
20278
20279 // MTHTS: keep by single thread
20280 #ifdef MULTIPLE_HEAPS
20281 #ifdef MARK_LIST
20282 #ifdef PARALLEL_MARK_LIST_SORT
20283 //    unsigned long start = GetCycleCount32();
20284     sort_mark_list();
20285 //    printf("sort_mark_list took %u cycles\n", GetCycleCount32() - start);
20286 #endif //PARALLEL_MARK_LIST_SORT
20287 #endif //MARK_LIST
20288
20289     dprintf (3, ("Joining for sync block cache entry scanning"));
20290     gc_t_join.join(this, gc_join_null_dead_syncblk);
20291     if (gc_t_join.joined())
20292 #endif //MULTIPLE_HEAPS
20293     {
20294         // scan for deleted entries in the syncblk cache
20295         GCScan::GcWeakPtrScanBySingleThread (condemned_gen_number, max_generation, &sc);
20296
20297 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
20298         if (g_fEnableAppDomainMonitoring)
20299         {
20300             size_t promoted_all_heaps = 0;
20301 #ifdef MULTIPLE_HEAPS
20302             for (int i = 0; i < n_heaps; i++)
20303             {
20304                 promoted_all_heaps += promoted_bytes (i);
20305             }
20306 #else
20307             promoted_all_heaps = promoted_bytes (heap_number);
20308 #endif //MULTIPLE_HEAPS
20309             GCToEEInterface::RecordTotalSurvivedBytes(promoted_all_heaps);
20310         }
20311 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
20312
20313 #ifdef MULTIPLE_HEAPS
20314
20315 #ifdef MARK_LIST
20316 #ifndef PARALLEL_MARK_LIST_SORT
20317         //compact g_mark_list and sort it.
20318         combine_mark_lists();
20319 #endif //PARALLEL_MARK_LIST_SORT
20320 #endif //MARK_LIST
20321
20322         //decide on promotion
20323         if (!settings.promotion)
20324         {
20325             size_t m = 0;
20326             for (int n = 0; n <= condemned_gen_number;n++)
20327             {
20328                 m +=  (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.1);
20329             }
20330
20331             for (int i = 0; i < n_heaps; i++)
20332             {
20333                 dynamic_data* dd = g_heaps[i]->dynamic_data_of (min (condemned_gen_number +1,
20334                                                                      max_generation));
20335                 size_t older_gen_size = (dd_current_size (dd) +
20336                                          (dd_desired_allocation (dd) -
20337                                          dd_new_allocation (dd)));
20338
20339                 if ((m > (older_gen_size)) ||
20340                     (promoted_bytes (i) > m))
20341                 {
20342                     settings.promotion = TRUE;
20343                 }
20344             }
20345         }
20346
20347 #ifdef SNOOP_STATS
20348         if (do_mark_steal_p)
20349         {
20350             size_t objects_checked_count = 0;
20351             size_t zero_ref_count = 0;
20352             size_t objects_marked_count = 0;
20353             size_t check_level_count = 0;
20354             size_t busy_count = 0;
20355             size_t interlocked_count = 0;
20356             size_t partial_mark_parent_count = 0;
20357             size_t stolen_or_pm_count = 0; 
20358             size_t stolen_entry_count = 0; 
20359             size_t pm_not_ready_count = 0; 
20360             size_t normal_count = 0;
20361             size_t stack_bottom_clear_count = 0;
20362
20363             for (int i = 0; i < n_heaps; i++)
20364             {
20365                 gc_heap* hp = g_heaps[i];
20366                 hp->print_snoop_stat();
20367                 objects_checked_count += hp->snoop_stat.objects_checked_count;
20368                 zero_ref_count += hp->snoop_stat.zero_ref_count;
20369                 objects_marked_count += hp->snoop_stat.objects_marked_count;
20370                 check_level_count += hp->snoop_stat.check_level_count;
20371                 busy_count += hp->snoop_stat.busy_count;
20372                 interlocked_count += hp->snoop_stat.interlocked_count;
20373                 partial_mark_parent_count += hp->snoop_stat.partial_mark_parent_count;
20374                 stolen_or_pm_count += hp->snoop_stat.stolen_or_pm_count;
20375                 stolen_entry_count += hp->snoop_stat.stolen_entry_count;
20376                 pm_not_ready_count += hp->snoop_stat.pm_not_ready_count;
20377                 normal_count += hp->snoop_stat.normal_count;
20378                 stack_bottom_clear_count += hp->snoop_stat.stack_bottom_clear_count;
20379             }
20380
20381             fflush (stdout);
20382
20383             printf ("-------total stats-------\n");
20384             printf ("%8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s | %8s\n", 
20385                 "checked", "zero", "marked", "level", "busy", "xchg", "pmparent", "s_pm", "stolen", "nready", "normal", "clear");
20386             printf ("%8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d | %8d\n",
20387                 objects_checked_count,
20388                 zero_ref_count,
20389                 objects_marked_count,
20390                 check_level_count,
20391                 busy_count,
20392                 interlocked_count,
20393                 partial_mark_parent_count,
20394                 stolen_or_pm_count,
20395                 stolen_entry_count,
20396                 pm_not_ready_count,
20397                 normal_count,
20398                 stack_bottom_clear_count);
20399         }
20400 #endif //SNOOP_STATS
20401
20402         //start all threads.
20403         dprintf(3, ("Starting all threads for end of mark phase"));
20404         gc_t_join.restart();
20405 #else //MULTIPLE_HEAPS
20406
20407         //decide on promotion
20408         if (!settings.promotion)
20409         {
20410             size_t m = 0;
20411             for (int n = 0; n <= condemned_gen_number;n++)
20412             {
20413                 m +=  (size_t)(dd_min_size (dynamic_data_of (n))*(n+1)*0.06);
20414             }
20415             dynamic_data* dd = dynamic_data_of (min (condemned_gen_number +1,
20416                                                      max_generation));
20417             size_t older_gen_size = (dd_current_size (dd) +
20418                                      (dd_desired_allocation (dd) -
20419                                      dd_new_allocation (dd)));
20420
20421             dprintf (2, ("promotion threshold: %Id, promoted bytes: %Id size n+1: %Id",
20422                          m, promoted_bytes (heap_number), older_gen_size));
20423
20424             if ((m > older_gen_size) ||
20425                     (promoted_bytes (heap_number) > m))
20426             {
20427                 settings.promotion = TRUE;
20428             }
20429         }
20430
20431 #endif //MULTIPLE_HEAPS
20432     }
20433
20434 #ifdef MULTIPLE_HEAPS
20435 #ifdef MARK_LIST
20436 #ifdef PARALLEL_MARK_LIST_SORT
20437 //    start = GetCycleCount32();
20438     merge_mark_lists();
20439 //    printf("merge_mark_lists took %u cycles\n", GetCycleCount32() - start);
20440 #endif //PARALLEL_MARK_LIST_SORT
20441 #endif //MARK_LIST
20442 #endif //MULTIPLE_HEAPS
20443
20444 #ifdef BACKGROUND_GC
20445     total_promoted_bytes = promoted_bytes (heap_number);
20446 #endif //BACKGROUND_GC
20447
20448     promoted_bytes (heap_number) -= promoted_bytes_live;
20449
20450 #ifdef TIME_GC
20451         finish = GetCycleCount32();
20452         mark_time = finish - start;
20453 #endif //TIME_GC
20454
20455     dprintf(2,("---- End of mark phase ----"));
20456 }
20457
20458 inline
20459 void gc_heap::pin_object (uint8_t* o, uint8_t** ppObject, uint8_t* low, uint8_t* high)
20460 {
20461     dprintf (3, ("Pinning %Ix", (size_t)o));
20462     if ((o >= low) && (o < high))
20463     {
20464         dprintf(3,("^%Ix^", (size_t)o));
20465         set_pinned (o);
20466
20467 #ifdef FEATURE_EVENT_TRACE        
20468         if(EVENT_ENABLED(PinObjectAtGCTime))
20469         {
20470             fire_etw_pin_object_event(o, ppObject);
20471         }
20472 #endif // FEATURE_EVENT_TRACE
20473
20474 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
20475         num_pinned_objects++;
20476 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
20477     }
20478 }
20479
20480 #if defined(ENABLE_PERF_COUNTERS) || defined(FEATURE_EVENT_TRACE)
20481 size_t gc_heap::get_total_pinned_objects()
20482 {
20483 #ifdef MULTIPLE_HEAPS
20484     size_t total_num_pinned_objects = 0;
20485     for (int i = 0; i < gc_heap::n_heaps; i++)
20486     {
20487         gc_heap* hp = gc_heap::g_heaps[i];
20488         total_num_pinned_objects += hp->num_pinned_objects;
20489     }
20490     return total_num_pinned_objects;
20491 #else //MULTIPLE_HEAPS
20492     return num_pinned_objects;
20493 #endif //MULTIPLE_HEAPS
20494 }
20495 #endif //ENABLE_PERF_COUNTERS || FEATURE_EVENT_TRACE
20496
20497 void gc_heap::reset_mark_stack ()
20498 {
20499     reset_pinned_queue();
20500     max_overflow_address = 0;
20501     min_overflow_address = MAX_PTR;
20502 }
20503
20504 #ifdef FEATURE_STRUCTALIGN
20505 //
20506 // The word with left child, right child, and align info is laid out as follows:
20507 //
20508 //      |   upper short word   |   lower short word   |
20509 //      |<------------> <----->|<------------> <----->|
20510 //      |  left child   info hi| right child   info lo|
20511 // x86: |    10 bits     6 bits|   10 bits      6 bits|
20512 //
20513 // where left/right child are signed values and concat(info hi, info lo) is unsigned.
20514 //
20515 // The "align info" encodes two numbers: the required alignment (a power of two)
20516 // and the misalignment (the number of machine words the destination address needs
20517 // to be adjusted by to provide alignment - so this number is always smaller than
20518 // the required alignment).  Thus, the two can be represented as the "logical or"
20519 // of the two numbers.  Note that the actual pad is computed from the misalignment
20520 // by adding the alignment iff the misalignment is non-zero and less than min_obj_size.
20521 //
20522
20523 // The number of bits in a brick.
20524 #if defined (_TARGET_AMD64_)
20525 #define brick_bits (12)
20526 #else
20527 #define brick_bits (11)
20528 #endif //_TARGET_AMD64_
20529 C_ASSERT(brick_size == (1 << brick_bits));
20530
20531 // The number of bits needed to represent the offset to a child node.
20532 // "brick_bits + 1" allows us to represent a signed offset within a brick.
20533 #define child_bits (brick_bits + 1 - LOG2_PTRSIZE)
20534
20535 // The number of bits in each of the pad hi, pad lo fields.
20536 #define pad_bits (sizeof(short) * 8 - child_bits)
20537
20538 #define child_from_short(w) (((signed short)(w) / (1 << (pad_bits - LOG2_PTRSIZE))) & ~((1 << LOG2_PTRSIZE) - 1))
20539 #define pad_mask ((1 << pad_bits) - 1)
20540 #define pad_from_short(w) ((size_t)(w) & pad_mask)
20541 #else // FEATURE_STRUCTALIGN
20542 #define child_from_short(w) (w)
20543 #endif // FEATURE_STRUCTALIGN
20544
20545 inline
20546 short node_left_child(uint8_t* node)
20547 {
20548     return child_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20549 }
20550
20551 inline
20552 void set_node_left_child(uint8_t* node, ptrdiff_t val)
20553 {
20554     assert (val > -(ptrdiff_t)brick_size);
20555     assert (val < (ptrdiff_t)brick_size);
20556     assert (Aligned (val));
20557 #ifdef FEATURE_STRUCTALIGN
20558     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.left);
20559     ((plug_and_pair*)node)[-1].m_pair.left = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20560 #else // FEATURE_STRUCTALIGN
20561     ((plug_and_pair*)node)[-1].m_pair.left = (short)val;
20562 #endif // FEATURE_STRUCTALIGN
20563     assert (node_left_child (node) == val);
20564 }
20565
20566 inline
20567 short node_right_child(uint8_t* node)
20568 {
20569     return child_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20570 }
20571
20572 inline
20573 void set_node_right_child(uint8_t* node, ptrdiff_t val)
20574 {
20575     assert (val > -(ptrdiff_t)brick_size);
20576     assert (val < (ptrdiff_t)brick_size);
20577     assert (Aligned (val));
20578 #ifdef FEATURE_STRUCTALIGN
20579     size_t pad = pad_from_short(((plug_and_pair*)node)[-1].m_pair.right);
20580     ((plug_and_pair*)node)[-1].m_pair.right = ((short)val << (pad_bits - LOG2_PTRSIZE)) | (short)pad;
20581 #else // FEATURE_STRUCTALIGN
20582     ((plug_and_pair*)node)[-1].m_pair.right = (short)val;
20583 #endif // FEATURE_STRUCTALIGN
20584     assert (node_right_child (node) == val);
20585 }
20586
20587 #ifdef FEATURE_STRUCTALIGN
20588 void node_aligninfo (uint8_t* node, int& requiredAlignment, ptrdiff_t& pad)
20589 {
20590     // Extract the single-number aligninfo from the fields.
20591     short left = ((plug_and_pair*)node)[-1].m_pair.left;
20592     short right = ((plug_and_pair*)node)[-1].m_pair.right;
20593     ptrdiff_t pad_shifted = (pad_from_short(left) << pad_bits) | pad_from_short(right);
20594     ptrdiff_t aligninfo = pad_shifted * DATA_ALIGNMENT;
20595
20596     // Replicate the topmost bit into all lower bits.
20597     ptrdiff_t x = aligninfo;
20598     x |= x >> 8;
20599     x |= x >> 4;
20600     x |= x >> 2;
20601     x |= x >> 1;
20602
20603     // Clear all bits but the highest.
20604     requiredAlignment = (int)(x ^ (x >> 1));
20605     pad = aligninfo - requiredAlignment;
20606     pad += AdjustmentForMinPadSize(pad, requiredAlignment);
20607 }
20608
20609 inline
20610 ptrdiff_t node_alignpad (uint8_t* node)
20611 {
20612     int requiredAlignment;
20613     ptrdiff_t alignpad;
20614     node_aligninfo (node, requiredAlignment, alignpad);
20615     return alignpad;
20616 }
20617
20618 void clear_node_aligninfo (uint8_t* node)
20619 {
20620     ((plug_and_pair*)node)[-1].m_pair.left &= ~0 << pad_bits;
20621     ((plug_and_pair*)node)[-1].m_pair.right &= ~0 << pad_bits;
20622 }
20623
20624 void set_node_aligninfo (uint8_t* node, int requiredAlignment, ptrdiff_t pad)
20625 {
20626     // Encode the alignment requirement and alignment offset as a single number
20627     // as described above.
20628     ptrdiff_t aligninfo = (size_t)requiredAlignment + (pad & (requiredAlignment-1));
20629     assert (Aligned (aligninfo));
20630     ptrdiff_t aligninfo_shifted = aligninfo / DATA_ALIGNMENT;
20631     assert (aligninfo_shifted < (1 << (pad_bits + pad_bits)));
20632
20633     ptrdiff_t hi = aligninfo_shifted >> pad_bits;
20634     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.left) == 0);
20635     ((plug_and_pair*)node)[-1].m_pair.left |= hi;
20636
20637     ptrdiff_t lo = aligninfo_shifted & pad_mask;
20638     assert (pad_from_short(((plug_and_gap*)node)[-1].m_pair.right) == 0);
20639     ((plug_and_pair*)node)[-1].m_pair.right |= lo;
20640
20641 #ifdef _DEBUG
20642     int requiredAlignment2;
20643     ptrdiff_t pad2;
20644     node_aligninfo (node, requiredAlignment2, pad2);
20645     assert (requiredAlignment == requiredAlignment2);
20646     assert (pad == pad2);
20647 #endif // _DEBUG
20648 }
20649 #endif // FEATURE_STRUCTALIGN
20650
20651 inline
20652 void loh_set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20653 {
20654     ptrdiff_t* place = &(((loh_obj_and_pad*)node)[-1].reloc);
20655     *place = val;
20656 }
20657
20658 inline
20659 ptrdiff_t loh_node_relocation_distance(uint8_t* node)
20660 {
20661     return (((loh_obj_and_pad*)node)[-1].reloc);
20662 }
20663
20664 inline
20665 ptrdiff_t node_relocation_distance (uint8_t* node)
20666 {
20667     return (((plug_and_reloc*)(node))[-1].reloc & ~3);
20668 }
20669
20670 inline
20671 void set_node_relocation_distance(uint8_t* node, ptrdiff_t val)
20672 {
20673     assert (val == (val & ~3));
20674     ptrdiff_t* place = &(((plug_and_reloc*)node)[-1].reloc);
20675     //clear the left bit and the relocation field
20676     *place &= 1;
20677     // store the value
20678     *place |= val;
20679 }
20680
20681 #define node_left_p(node) (((plug_and_reloc*)(node))[-1].reloc & 2)
20682
20683 #define set_node_left(node) ((plug_and_reloc*)(node))[-1].reloc |= 2;
20684
20685 #ifndef FEATURE_STRUCTALIGN
20686 void set_node_realigned(uint8_t* node)
20687 {
20688     ((plug_and_reloc*)(node))[-1].reloc |= 1;
20689 }
20690
20691 void clear_node_realigned(uint8_t* node)
20692 {
20693 #ifdef RESPECT_LARGE_ALIGNMENT
20694     ((plug_and_reloc*)(node))[-1].reloc &= ~1;
20695 #else
20696     UNREFERENCED_PARAMETER(node);
20697 #endif //RESPECT_LARGE_ALIGNMENT
20698 }
20699 #endif // FEATURE_STRUCTALIGN
20700
20701 inline
20702 size_t  node_gap_size (uint8_t* node)
20703 {
20704     return ((plug_and_gap *)node)[-1].gap;
20705 }
20706
20707 void set_gap_size (uint8_t* node, size_t size)
20708 {
20709     assert (Aligned (size));
20710
20711     // clear the 2 uint32_t used by the node.
20712     ((plug_and_gap *)node)[-1].reloc = 0;
20713     ((plug_and_gap *)node)[-1].lr =0;
20714     ((plug_and_gap *)node)[-1].gap = size;
20715
20716     assert ((size == 0 )||(size >= sizeof(plug_and_reloc)));
20717
20718 }
20719
20720 uint8_t* gc_heap::insert_node (uint8_t* new_node, size_t sequence_number,
20721                    uint8_t* tree, uint8_t* last_node)
20722 {
20723     dprintf (3, ("IN: %Ix(%Ix), T: %Ix(%Ix), L: %Ix(%Ix) [%Ix]",
20724                  (size_t)new_node, brick_of(new_node), 
20725                  (size_t)tree, brick_of(tree), 
20726                  (size_t)last_node, brick_of(last_node),
20727                  sequence_number));
20728     if (power_of_two_p (sequence_number))
20729     {
20730         set_node_left_child (new_node, (tree - new_node));
20731         dprintf (3, ("NT: %Ix, LC->%Ix", (size_t)new_node, (tree - new_node)));
20732         tree = new_node;
20733     }
20734     else
20735     {
20736         if (oddp (sequence_number))
20737         {
20738             set_node_right_child (last_node, (new_node - last_node));
20739             dprintf (3, ("%Ix RC->%Ix", last_node, (new_node - last_node)));
20740         }
20741         else
20742         {
20743             uint8_t*  earlier_node = tree;
20744             size_t imax = logcount(sequence_number) - 2;
20745             for (size_t i = 0; i != imax; i++)
20746             {
20747                 earlier_node = earlier_node + node_right_child (earlier_node);
20748             }
20749             int tmp_offset = node_right_child (earlier_node);
20750             assert (tmp_offset); // should never be empty
20751             set_node_left_child (new_node, ((earlier_node + tmp_offset ) - new_node));
20752             set_node_right_child (earlier_node, (new_node - earlier_node));
20753
20754             dprintf (3, ("%Ix LC->%Ix, %Ix RC->%Ix", 
20755                 new_node, ((earlier_node + tmp_offset ) - new_node),
20756                 earlier_node, (new_node - earlier_node)));
20757         }
20758     }
20759     return tree;
20760 }
20761
20762 size_t gc_heap::update_brick_table (uint8_t* tree, size_t current_brick,
20763                                     uint8_t* x, uint8_t* plug_end)
20764 {
20765     dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix",
20766         tree, current_brick, x, plug_end));
20767
20768     if (tree != NULL)
20769     {
20770         dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix", 
20771             current_brick, (size_t)(tree - brick_address (current_brick)), tree));
20772         set_brick (current_brick, (tree - brick_address (current_brick)));
20773     }
20774     else
20775     {
20776         dprintf (3, ("b- %Ix->-1", current_brick));
20777         set_brick (current_brick, -1);
20778     }
20779     size_t  b = 1 + current_brick;
20780     ptrdiff_t  offset = 0;
20781     size_t last_br = brick_of (plug_end-1);
20782     current_brick = brick_of (x-1);
20783     dprintf (3, ("ubt: %Ix->%Ix]->%Ix]", b, last_br, current_brick));
20784     while (b <= current_brick)
20785     {
20786         if (b <= last_br)
20787         {
20788             set_brick (b, --offset);
20789         }
20790         else
20791         {
20792             set_brick (b,-1);
20793         }
20794         b++;
20795     }
20796     return brick_of (x);
20797 }
20798
20799 void gc_heap::plan_generation_start (generation* gen, generation* consing_gen, uint8_t* next_plug_to_allocate)
20800 {
20801 #ifdef BIT64
20802     // We should never demote big plugs to gen0.
20803     if (gen == youngest_generation)
20804     {
20805         heap_segment* seg = ephemeral_heap_segment;
20806         size_t mark_stack_large_bos = mark_stack_bos;
20807         size_t large_plug_pos = 0;
20808         while (mark_stack_large_bos < mark_stack_tos)
20809         {
20810             if (mark_stack_array[mark_stack_large_bos].len > demotion_plug_len_th)
20811             {
20812                 while (mark_stack_bos <= mark_stack_large_bos)
20813                 {
20814                     size_t entry = deque_pinned_plug();
20815                     size_t len = pinned_len (pinned_plug_of (entry));
20816                     uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20817                     if (len > demotion_plug_len_th)
20818                     {
20819                         dprintf (2, ("ps(%d): S %Ix (%Id)(%Ix)", gen->gen_num, plug, len, (plug+len)));
20820                     }
20821                     pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (consing_gen);
20822                     assert(mark_stack_array[entry].len == 0 ||
20823                             mark_stack_array[entry].len >= Align(min_obj_size));
20824                     generation_allocation_pointer (consing_gen) = plug + len;
20825                     generation_allocation_limit (consing_gen) = heap_segment_plan_allocated (seg);
20826                     set_allocator_next_pin (consing_gen);
20827                 }
20828             }
20829
20830             mark_stack_large_bos++;
20831         }
20832     }
20833 #endif // BIT64
20834
20835     generation_plan_allocation_start (gen) =
20836         allocate_in_condemned_generations (consing_gen, Align (min_obj_size), -1);
20837     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20838     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20839     if (next_plug_to_allocate)
20840     {
20841         size_t dist_to_next_plug = (size_t)(next_plug_to_allocate - generation_allocation_pointer (consing_gen));
20842         if (allocation_left > dist_to_next_plug)
20843         {
20844             allocation_left = dist_to_next_plug;
20845         }
20846     }
20847     if (allocation_left < Align (min_obj_size))
20848     {
20849         generation_plan_allocation_start_size (gen) += allocation_left;
20850         generation_allocation_pointer (consing_gen) += allocation_left;
20851     }
20852
20853     dprintf (2, ("plan alloc gen%d(%Ix) start at %Ix (ptr: %Ix, limit: %Ix, next: %Ix)", gen->gen_num, 
20854         generation_plan_allocation_start (gen),
20855         generation_plan_allocation_start_size (gen),
20856         generation_allocation_pointer (consing_gen), generation_allocation_limit (consing_gen),
20857         next_plug_to_allocate));
20858 }
20859
20860 void gc_heap::realloc_plan_generation_start (generation* gen, generation* consing_gen)
20861 {
20862     BOOL adjacentp = FALSE;
20863
20864     generation_plan_allocation_start (gen) =  
20865         allocate_in_expanded_heap (consing_gen, Align(min_obj_size), adjacentp, 0, 
20866 #ifdef SHORT_PLUGS
20867                                    FALSE, NULL, 
20868 #endif //SHORT_PLUGS
20869                                    FALSE, -1 REQD_ALIGN_AND_OFFSET_ARG);
20870
20871     generation_plan_allocation_start_size (gen) = Align (min_obj_size);
20872     size_t allocation_left = (size_t)(generation_allocation_limit (consing_gen) - generation_allocation_pointer (consing_gen));
20873     if ((allocation_left < Align (min_obj_size)) && 
20874          (generation_allocation_limit (consing_gen)!=heap_segment_plan_allocated (generation_allocation_segment (consing_gen))))
20875     {
20876         generation_plan_allocation_start_size (gen) += allocation_left;
20877         generation_allocation_pointer (consing_gen) += allocation_left;
20878     }
20879
20880     dprintf (1, ("plan re-alloc gen%d start at %Ix (ptr: %Ix, limit: %Ix)", gen->gen_num, 
20881         generation_plan_allocation_start (consing_gen),
20882         generation_allocation_pointer (consing_gen), 
20883         generation_allocation_limit (consing_gen))); 
20884 }
20885
20886 void gc_heap::plan_generation_starts (generation*& consing_gen)
20887 {
20888     //make sure that every generation has a planned allocation start
20889     int  gen_number = settings.condemned_generation;
20890     while (gen_number >= 0)
20891     {
20892         if (gen_number < max_generation)
20893         {
20894             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
20895         }
20896         generation* gen = generation_of (gen_number);
20897         if (0 == generation_plan_allocation_start (gen))
20898         {
20899             plan_generation_start (gen, consing_gen, 0);
20900             assert (generation_plan_allocation_start (gen));
20901         }
20902         gen_number--;
20903     }
20904     // now we know the planned allocation size
20905     heap_segment_plan_allocated (ephemeral_heap_segment) =
20906         generation_allocation_pointer (consing_gen);
20907 }
20908
20909 void gc_heap::advance_pins_for_demotion (generation* gen)
20910 {
20911     uint8_t* original_youngest_start = generation_allocation_start (youngest_generation);
20912     heap_segment* seg = ephemeral_heap_segment;
20913
20914     if ((!(pinned_plug_que_empty_p())))
20915     {
20916         size_t gen1_pinned_promoted = generation_pinned_allocation_compact_size (generation_of (max_generation));
20917         size_t gen1_pins_left = dd_pinned_survived_size (dynamic_data_of (max_generation - 1)) - gen1_pinned_promoted;
20918         size_t total_space_to_skip = last_gen1_pin_end - generation_allocation_pointer (gen);
20919         float pin_frag_ratio = (float)gen1_pins_left / (float)total_space_to_skip;
20920         float pin_surv_ratio = (float)gen1_pins_left / (float)(dd_survived_size (dynamic_data_of (max_generation - 1)));
20921         if ((pin_frag_ratio > 0.15) && (pin_surv_ratio > 0.30))
20922         {
20923             while (!pinned_plug_que_empty_p() &&
20924                     (pinned_plug (oldest_pin()) < original_youngest_start))
20925             {
20926                 size_t entry = deque_pinned_plug();
20927                 size_t len = pinned_len (pinned_plug_of (entry));
20928                 uint8_t* plug = pinned_plug (pinned_plug_of(entry));
20929                 pinned_len (pinned_plug_of (entry)) = plug - generation_allocation_pointer (gen);
20930                 assert(mark_stack_array[entry].len == 0 ||
20931                         mark_stack_array[entry].len >= Align(min_obj_size));
20932                 generation_allocation_pointer (gen) = plug + len;
20933                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
20934                 set_allocator_next_pin (gen);
20935
20936                 //Add the size of the pinned plug to the right pinned allocations
20937                 //find out which gen this pinned plug came from 
20938                 int frgn = object_gennum (plug);
20939                 if ((frgn != (int)max_generation) && settings.promotion)
20940                 {
20941                     int togn = object_gennum_plan (plug);
20942                     generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
20943                     if (frgn < togn)
20944                     {
20945                         generation_pinned_allocation_compact_size (generation_of (togn)) += len;
20946                     }
20947                 }
20948
20949                 dprintf (2, ("skipping gap %d, pin %Ix (%Id)", 
20950                     pinned_len (pinned_plug_of (entry)), plug, len));
20951             }
20952         }
20953         dprintf (2, ("ad_p_d: PL: %Id, SL: %Id, pfr: %d, psr: %d", 
20954             gen1_pins_left, total_space_to_skip, (int)(pin_frag_ratio*100), (int)(pin_surv_ratio*100)));
20955     }
20956 }
20957
20958 void gc_heap::process_ephemeral_boundaries (uint8_t* x,
20959                                             int& active_new_gen_number,
20960                                             int& active_old_gen_number,
20961                                             generation*& consing_gen,
20962                                             BOOL& allocate_in_condemned)
20963 {
20964 retry:
20965     if ((active_old_gen_number > 0) &&
20966         (x >= generation_allocation_start (generation_of (active_old_gen_number - 1))))
20967     {
20968         dprintf (2, ("crossing gen%d, x is %Ix", active_old_gen_number - 1, x));
20969
20970         if (!pinned_plug_que_empty_p())
20971         {
20972             dprintf (2, ("oldest pin: %Ix(%Id)",
20973                 pinned_plug (oldest_pin()), 
20974                 (x - pinned_plug (oldest_pin()))));
20975         }
20976
20977         if (active_old_gen_number <= (settings.promotion ? (max_generation - 1) : max_generation))
20978         {
20979             active_new_gen_number--;
20980         }
20981
20982         active_old_gen_number--;
20983         assert ((!settings.promotion) || (active_new_gen_number>0));
20984
20985         if (active_new_gen_number == (max_generation - 1))
20986         {
20987 #ifdef FREE_USAGE_STATS
20988             if (settings.condemned_generation == max_generation)
20989             {
20990                 // We need to do this before we skip the rest of the pinned plugs.
20991                 generation* gen_2 = generation_of (max_generation);
20992                 generation* gen_1 = generation_of (max_generation - 1);
20993
20994                 size_t total_num_pinned_free_spaces_left = 0;
20995
20996                 // We are about to allocate gen1, check to see how efficient fitting in gen2 pinned free spaces is.
20997                 for (int j = 0; j < NUM_GEN_POWER2; j++)
20998                 {
20999                     dprintf (1, ("[h%d][#%Id]2^%d: current: %Id, S: 2: %Id, 1: %Id(%Id)", 
21000                         heap_number, 
21001                         settings.gc_index,
21002                         (j + 10), 
21003                         gen_2->gen_current_pinned_free_spaces[j],
21004                         gen_2->gen_plugs[j], gen_1->gen_plugs[j],
21005                         (gen_2->gen_plugs[j] + gen_1->gen_plugs[j])));
21006
21007                     total_num_pinned_free_spaces_left += gen_2->gen_current_pinned_free_spaces[j];
21008                 }
21009
21010                 float pinned_free_list_efficiency = 0;
21011                 size_t total_pinned_free_space = generation_allocated_in_pinned_free (gen_2) + generation_pinned_free_obj_space (gen_2);
21012                 if (total_pinned_free_space != 0)
21013                 {
21014                     pinned_free_list_efficiency = (float)(generation_allocated_in_pinned_free (gen_2)) / (float)total_pinned_free_space;
21015                 }
21016
21017                 dprintf (1, ("[h%d] gen2 allocated %Id bytes with %Id bytes pinned free spaces (effi: %d%%), %Id (%Id) left",
21018                             heap_number,
21019                             generation_allocated_in_pinned_free (gen_2),
21020                             total_pinned_free_space, 
21021                             (int)(pinned_free_list_efficiency * 100),
21022                             generation_pinned_free_obj_space (gen_2),
21023                             total_num_pinned_free_spaces_left));
21024             }
21025 #endif //FREE_USAGE_STATS
21026
21027             //Go past all of the pinned plugs for this generation.
21028             while (!pinned_plug_que_empty_p() &&
21029                    (!in_range_for_segment ((pinned_plug (oldest_pin())), ephemeral_heap_segment)))
21030             {
21031                 size_t  entry = deque_pinned_plug();
21032                 mark*  m = pinned_plug_of (entry);
21033                 uint8_t*  plug = pinned_plug (m);
21034                 size_t  len = pinned_len (m);
21035                 // detect pinned block in different segment (later) than
21036                 // allocation segment, skip those until the oldest pin is in the ephemeral seg.
21037                 // adjust the allocation segment along the way (at the end it will
21038                 // be the ephemeral segment.
21039                 heap_segment* nseg = heap_segment_in_range (generation_allocation_segment (consing_gen));
21040
21041                 PREFIX_ASSUME(nseg != NULL);
21042
21043                 while (!((plug >= generation_allocation_pointer (consing_gen))&&
21044                         (plug < heap_segment_allocated (nseg))))
21045                 {
21046                     //adjust the end of the segment to be the end of the plug
21047                     assert (generation_allocation_pointer (consing_gen)>=
21048                             heap_segment_mem (nseg));
21049                     assert (generation_allocation_pointer (consing_gen)<=
21050                             heap_segment_committed (nseg));
21051
21052                     heap_segment_plan_allocated (nseg) =
21053                         generation_allocation_pointer (consing_gen);
21054                     //switch allocation segment
21055                     nseg = heap_segment_next_rw (nseg);
21056                     generation_allocation_segment (consing_gen) = nseg;
21057                     //reset the allocation pointer and limits
21058                     generation_allocation_pointer (consing_gen) =
21059                         heap_segment_mem (nseg);
21060                 }
21061                 set_new_pin_info (m, generation_allocation_pointer (consing_gen));
21062                 assert(pinned_len(m) == 0 || pinned_len(m) >= Align(min_obj_size));
21063                 generation_allocation_pointer (consing_gen) = plug + len;
21064                 generation_allocation_limit (consing_gen) =
21065                     generation_allocation_pointer (consing_gen);
21066             }
21067             allocate_in_condemned = TRUE;
21068             consing_gen = ensure_ephemeral_heap_segment (consing_gen);
21069         }
21070
21071         if (active_new_gen_number != max_generation)
21072         {
21073             if (active_new_gen_number == (max_generation - 1))
21074             {
21075                 maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
21076                 if (!demote_gen1_p)
21077                     advance_pins_for_demotion (consing_gen);
21078             }
21079
21080             plan_generation_start (generation_of (active_new_gen_number), consing_gen, x);
21081                 
21082             dprintf (2, ("process eph: allocated gen%d start at %Ix", 
21083                 active_new_gen_number,
21084                 generation_plan_allocation_start (generation_of (active_new_gen_number))));
21085
21086             if ((demotion_low == MAX_PTR) && !pinned_plug_que_empty_p())
21087             {
21088                 uint8_t* pplug = pinned_plug (oldest_pin());
21089                 if (object_gennum (pplug) > 0)
21090                 {
21091                     demotion_low = pplug;
21092                     dprintf (3, ("process eph: dlow->%Ix", demotion_low));
21093                 }
21094             }
21095
21096             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
21097         }
21098
21099         goto retry;
21100     }
21101 }
21102
21103 inline
21104 void gc_heap::seg_clear_mark_bits (heap_segment* seg)
21105 {
21106     uint8_t* o = heap_segment_mem (seg);
21107     while (o < heap_segment_allocated (seg))
21108     {
21109         if (marked (o))
21110         {
21111             clear_marked (o);
21112         }
21113         o = o  + Align (size (o));
21114     }
21115 }
21116
21117 #ifdef FEATURE_BASICFREEZE
21118 void gc_heap::sweep_ro_segments (heap_segment* start_seg)
21119 {
21120     //go through all of the segment in range and reset the mark bit
21121     //TODO works only on small object segments
21122
21123     heap_segment* seg = start_seg;
21124
21125     while (seg)
21126     {
21127         if (heap_segment_read_only_p (seg) &&
21128             heap_segment_in_range_p (seg))
21129         {
21130 #ifdef BACKGROUND_GC
21131             if (settings.concurrent)
21132             {
21133                 seg_clear_mark_array_bits_soh (seg);
21134             }
21135             else
21136             {
21137                 seg_clear_mark_bits (seg);
21138             }
21139 #else //BACKGROUND_GC
21140
21141 #ifdef MARK_ARRAY
21142             if(gc_can_use_concurrent)
21143             {
21144                 clear_mark_array (max (heap_segment_mem (seg), lowest_address),
21145                               min (heap_segment_allocated (seg), highest_address),
21146                               FALSE); // read_only segments need the mark clear
21147             }
21148 #else //MARK_ARRAY
21149             seg_clear_mark_bits (seg);
21150 #endif //MARK_ARRAY
21151
21152 #endif //BACKGROUND_GC
21153         }
21154         seg = heap_segment_next (seg);
21155     }
21156 }
21157 #endif // FEATURE_BASICFREEZE
21158
21159 #ifdef FEATURE_LOH_COMPACTION
21160 inline
21161 BOOL gc_heap::loh_pinned_plug_que_empty_p()
21162 {
21163     return (loh_pinned_queue_bos == loh_pinned_queue_tos);
21164 }
21165
21166 void gc_heap::loh_set_allocator_next_pin()
21167 {
21168     if (!(loh_pinned_plug_que_empty_p()))
21169     {
21170         mark*  oldest_entry = loh_oldest_pin();
21171         uint8_t* plug = pinned_plug (oldest_entry);
21172         generation* gen = large_object_generation;
21173         if ((plug >= generation_allocation_pointer (gen)) &&
21174             (plug <  generation_allocation_limit (gen)))
21175         {
21176             generation_allocation_limit (gen) = pinned_plug (oldest_entry);
21177         }
21178         else
21179             assert (!((plug < generation_allocation_pointer (gen)) &&
21180                       (plug >= heap_segment_mem (generation_allocation_segment (gen)))));
21181     }
21182 }
21183
21184 size_t gc_heap::loh_deque_pinned_plug ()
21185 {
21186     size_t m = loh_pinned_queue_bos;
21187     loh_pinned_queue_bos++;
21188     return m;
21189 }
21190
21191 inline
21192 mark* gc_heap::loh_pinned_plug_of (size_t bos)
21193 {
21194     return &loh_pinned_queue[bos];
21195 }
21196
21197 inline
21198 mark* gc_heap::loh_oldest_pin()
21199 {
21200     return loh_pinned_plug_of (loh_pinned_queue_bos);
21201 }
21202
21203 // If we can't grow the queue, then don't compact.
21204 BOOL gc_heap::loh_enque_pinned_plug (uint8_t* plug, size_t len)
21205 {
21206     assert(len >= Align(min_obj_size, get_alignment_constant (FALSE)));
21207
21208     if (loh_pinned_queue_length <= loh_pinned_queue_tos)
21209     {
21210         if (!grow_mark_stack (loh_pinned_queue, loh_pinned_queue_length, LOH_PIN_QUEUE_LENGTH))
21211         {
21212             return FALSE;
21213         }
21214     }
21215     dprintf (3, (" P: %Ix(%Id)", plug, len));
21216     mark& m = loh_pinned_queue[loh_pinned_queue_tos];
21217     m.first = plug;
21218     m.len = len;
21219     loh_pinned_queue_tos++;
21220     loh_set_allocator_next_pin();
21221     return TRUE;
21222 }
21223
21224 inline
21225 BOOL gc_heap::loh_size_fit_p (size_t size, uint8_t* alloc_pointer, uint8_t* alloc_limit)
21226 {
21227     dprintf (1235, ("trying to fit %Id(%Id) between %Ix and %Ix (%Id)", 
21228         size, 
21229         (2* AlignQword (loh_padding_obj_size) +  size),
21230         alloc_pointer,
21231         alloc_limit,
21232         (alloc_limit - alloc_pointer)));
21233
21234     return ((alloc_pointer + 2* AlignQword (loh_padding_obj_size) +  size) <= alloc_limit);
21235 }
21236
21237 uint8_t* gc_heap::loh_allocate_in_condemned (uint8_t* old_loc, size_t size)
21238 {
21239     UNREFERENCED_PARAMETER(old_loc);
21240
21241     generation* gen = large_object_generation;
21242     dprintf (1235, ("E: p:%Ix, l:%Ix, s: %Id", 
21243         generation_allocation_pointer (gen),
21244         generation_allocation_limit (gen),
21245         size));
21246
21247 retry:
21248     {
21249         heap_segment* seg = generation_allocation_segment (gen);
21250         if (!(loh_size_fit_p (size, generation_allocation_pointer (gen), generation_allocation_limit (gen))))
21251         {
21252             if ((!(loh_pinned_plug_que_empty_p()) &&
21253                  (generation_allocation_limit (gen) ==
21254                   pinned_plug (loh_oldest_pin()))))
21255             {
21256                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21257                 size_t len = pinned_len (m);
21258                 uint8_t* plug = pinned_plug (m);
21259                 dprintf (1235, ("AIC: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
21260                 pinned_len (m) = plug - generation_allocation_pointer (gen);
21261                 generation_allocation_pointer (gen) = plug + len;
21262                 
21263                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21264                 loh_set_allocator_next_pin();
21265                 dprintf (1235, ("s: p: %Ix, l: %Ix (%Id)", 
21266                     generation_allocation_pointer (gen), 
21267                     generation_allocation_limit (gen),
21268                     (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21269
21270                 goto retry;
21271             }
21272
21273             if (generation_allocation_limit (gen) != heap_segment_plan_allocated (seg))
21274             {
21275                 generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21276                 dprintf (1235, ("l->pa(%Ix)", generation_allocation_limit (gen)));
21277             }
21278             else
21279             {
21280                 if (heap_segment_plan_allocated (seg) != heap_segment_committed (seg))
21281                 {
21282                     heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
21283                     generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21284                     dprintf (1235, ("l->c(%Ix)", generation_allocation_limit (gen)));
21285                 }
21286                 else
21287                 {
21288                     if (loh_size_fit_p (size, generation_allocation_pointer (gen), heap_segment_reserved (seg)) &&
21289                         (grow_heap_segment (seg, (generation_allocation_pointer (gen) + size + 2* AlignQword (loh_padding_obj_size)))))
21290                     {
21291                         dprintf (1235, ("growing seg from %Ix to %Ix\n", heap_segment_committed (seg),
21292                                          (generation_allocation_pointer (gen) + size)));
21293
21294                         heap_segment_plan_allocated (seg) = heap_segment_committed (seg);
21295                         generation_allocation_limit (gen) = heap_segment_plan_allocated (seg);
21296
21297                         dprintf (1235, ("g: p: %Ix, l: %Ix (%Id)", 
21298                             generation_allocation_pointer (gen), 
21299                             generation_allocation_limit (gen),
21300                             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21301                     }
21302                     else
21303                     {
21304                         heap_segment* next_seg = heap_segment_next (seg);
21305                         assert (generation_allocation_pointer (gen)>=
21306                                 heap_segment_mem (seg));
21307                         // Verify that all pinned plugs for this segment are consumed
21308                         if (!loh_pinned_plug_que_empty_p() &&
21309                             ((pinned_plug (loh_oldest_pin()) <
21310                               heap_segment_allocated (seg)) &&
21311                              (pinned_plug (loh_oldest_pin()) >=
21312                               generation_allocation_pointer (gen))))
21313                         {
21314                             LOG((LF_GC, LL_INFO10, "remaining pinned plug %Ix while leaving segment on allocation",
21315                                          pinned_plug (loh_oldest_pin())));
21316                             dprintf (1236, ("queue empty: %d", loh_pinned_plug_que_empty_p()));
21317                             FATAL_GC_ERROR();
21318                         }
21319                         assert (generation_allocation_pointer (gen)>=
21320                                 heap_segment_mem (seg));
21321                         assert (generation_allocation_pointer (gen)<=
21322                                 heap_segment_committed (seg));
21323                         heap_segment_plan_allocated (seg) = generation_allocation_pointer (gen);
21324
21325                         if (next_seg)
21326                         {
21327                             // for LOH do we want to try starting from the first LOH every time though?
21328                             generation_allocation_segment (gen) = next_seg;
21329                             generation_allocation_pointer (gen) = heap_segment_mem (next_seg);
21330                             generation_allocation_limit (gen) = generation_allocation_pointer (gen);
21331
21332                             dprintf (1235, ("n: p: %Ix, l: %Ix (%Id)", 
21333                                 generation_allocation_pointer (gen), 
21334                                 generation_allocation_limit (gen),
21335                                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21336                         }
21337                         else
21338                         {
21339                             dprintf (1, ("We ran out of space compacting, shouldn't happen"));
21340                             FATAL_GC_ERROR();
21341                         }
21342                     }
21343                 }
21344             }
21345             loh_set_allocator_next_pin();
21346
21347             dprintf (1235, ("r: p: %Ix, l: %Ix (%Id)", 
21348                 generation_allocation_pointer (gen), 
21349                 generation_allocation_limit (gen),
21350                 (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21351
21352             goto retry;
21353         }
21354     }
21355
21356     {
21357         assert (generation_allocation_pointer (gen)>=
21358                 heap_segment_mem (generation_allocation_segment (gen)));
21359         uint8_t* result = generation_allocation_pointer (gen);
21360         size_t loh_pad = AlignQword (loh_padding_obj_size);
21361
21362         generation_allocation_pointer (gen) += size + loh_pad;
21363         assert (generation_allocation_pointer (gen) <= generation_allocation_limit (gen));
21364
21365         dprintf (1235, ("p: %Ix, l: %Ix (%Id)", 
21366             generation_allocation_pointer (gen), 
21367             generation_allocation_limit (gen),
21368             (generation_allocation_limit (gen) - generation_allocation_pointer (gen))));
21369
21370         assert (result + loh_pad);
21371         return result + loh_pad;
21372     }
21373 }
21374
21375 BOOL gc_heap::should_compact_loh()
21376 {
21377     // If hard limit is specified GC will automatically decide if LOH needs to be compacted.
21378     return (heap_hard_limit || loh_compaction_always_p || (loh_compaction_mode != loh_compaction_default));
21379 }
21380
21381 inline
21382 void gc_heap::check_loh_compact_mode (BOOL all_heaps_compacted_p)
21383 {
21384     if (settings.loh_compaction && (loh_compaction_mode == loh_compaction_once))
21385     {
21386         if (all_heaps_compacted_p)
21387         {
21388             // If the compaction mode says to compact once and we are going to compact LOH, 
21389             // we need to revert it back to no compaction.
21390             loh_compaction_mode = loh_compaction_default;
21391         }
21392     }
21393 }
21394
21395 BOOL gc_heap::plan_loh()
21396 {
21397     if (!loh_pinned_queue)
21398     {
21399         loh_pinned_queue = new (nothrow) (mark [LOH_PIN_QUEUE_LENGTH]);
21400         if (!loh_pinned_queue)
21401         {
21402             dprintf (1, ("Cannot allocate the LOH pinned queue (%Id bytes), no compaction", 
21403                          LOH_PIN_QUEUE_LENGTH * sizeof (mark)));
21404             return FALSE;
21405         }
21406
21407         loh_pinned_queue_length = LOH_PIN_QUEUE_LENGTH;
21408     }
21409
21410     if (heap_number == 0)
21411         loh_pinned_queue_decay = LOH_PIN_DECAY;
21412
21413     loh_pinned_queue_tos = 0;
21414     loh_pinned_queue_bos = 0;
21415     
21416     generation* gen        = large_object_generation;
21417     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21418     PREFIX_ASSUME(start_seg != NULL);
21419     heap_segment* seg      = start_seg;
21420     uint8_t* o             = generation_allocation_start (gen);
21421
21422     dprintf (1235, ("before GC LOH size: %Id, free list: %Id, free obj: %Id\n", 
21423         generation_size (max_generation + 1), 
21424         generation_free_list_space (gen),
21425         generation_free_obj_space (gen)));
21426
21427     while (seg)
21428     {
21429         heap_segment_plan_allocated (seg) = heap_segment_mem (seg);
21430         seg = heap_segment_next (seg);
21431     }
21432
21433     seg = start_seg;
21434
21435     //Skip the generation gap object
21436     o = o + AlignQword (size (o));
21437     // We don't need to ever realloc gen3 start so don't touch it.
21438     heap_segment_plan_allocated (seg) = o;
21439     generation_allocation_pointer (gen) = o;
21440     generation_allocation_limit (gen) = generation_allocation_pointer (gen);
21441     generation_allocation_segment (gen) = start_seg;
21442
21443     uint8_t* free_space_start = o;
21444     uint8_t* free_space_end = o;
21445     uint8_t* new_address = 0;
21446
21447     while (1)
21448     {
21449         if (o >= heap_segment_allocated (seg))
21450         {
21451             seg = heap_segment_next (seg);
21452             if (seg == 0)
21453             {
21454                 break;
21455             }
21456
21457             o = heap_segment_mem (seg);
21458         }
21459
21460         if (marked (o))
21461         {
21462             free_space_end = o;
21463             size_t size = AlignQword (size (o));
21464             dprintf (1235, ("%Ix(%Id) M", o, size));
21465
21466             if (pinned (o))
21467             {
21468                 // We don't clear the pinned bit yet so we can check in 
21469                 // compact phase how big a free object we should allocate
21470                 // in front of the pinned object. We use the reloc address
21471                 // field to store this.
21472                 if (!loh_enque_pinned_plug (o, size))
21473                 {
21474                     return FALSE;
21475                 }
21476                 new_address = o;
21477             }
21478             else
21479             {
21480                 new_address = loh_allocate_in_condemned (o, size);
21481             }
21482
21483             loh_set_node_relocation_distance (o, (new_address - o));
21484             dprintf (1235, ("lobj %Ix-%Ix -> %Ix-%Ix (%Id)", o, (o + size), new_address, (new_address + size), (new_address - o)));
21485
21486             o = o + size;
21487             free_space_start = o;
21488             if (o < heap_segment_allocated (seg))
21489             {
21490                 assert (!marked (o));
21491             }
21492         }
21493         else
21494         {
21495             while (o < heap_segment_allocated (seg) && !marked (o))
21496             {
21497                 dprintf (1235, ("%Ix(%Id) F (%d)", o, AlignQword (size (o)), ((method_table (o) == g_gc_pFreeObjectMethodTable) ? 1 : 0)));
21498                 o = o + AlignQword (size (o));
21499             }
21500         }
21501     }
21502
21503     while (!loh_pinned_plug_que_empty_p())
21504     {
21505         mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21506         size_t len = pinned_len (m);
21507         uint8_t* plug = pinned_plug (m);
21508
21509         // detect pinned block in different segment (later) than
21510         // allocation segment
21511         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (gen));
21512
21513         while ((plug < generation_allocation_pointer (gen)) ||
21514                (plug >= heap_segment_allocated (nseg)))
21515         {
21516             assert ((plug < heap_segment_mem (nseg)) ||
21517                     (plug > heap_segment_reserved (nseg)));
21518             //adjust the end of the segment to be the end of the plug
21519             assert (generation_allocation_pointer (gen)>=
21520                     heap_segment_mem (nseg));
21521             assert (generation_allocation_pointer (gen)<=
21522                     heap_segment_committed (nseg));
21523
21524             heap_segment_plan_allocated (nseg) =
21525                 generation_allocation_pointer (gen);
21526             //switch allocation segment
21527             nseg = heap_segment_next_rw (nseg);
21528             generation_allocation_segment (gen) = nseg;
21529             //reset the allocation pointer and limits
21530             generation_allocation_pointer (gen) =
21531                 heap_segment_mem (nseg);
21532         }
21533
21534         dprintf (1235, ("SP: %Ix->%Ix(%Id)", generation_allocation_pointer (gen), plug, plug - generation_allocation_pointer (gen)));
21535         pinned_len (m) = plug - generation_allocation_pointer (gen);
21536         generation_allocation_pointer (gen) = plug + len;
21537     }
21538
21539     heap_segment_plan_allocated (generation_allocation_segment (gen)) = generation_allocation_pointer (gen);
21540     generation_allocation_pointer (gen) = 0;
21541     generation_allocation_limit (gen) = 0;
21542
21543     return TRUE;
21544 }
21545
21546 void gc_heap::compact_loh()
21547 {
21548     assert (should_compact_loh());
21549
21550     generation* gen        = large_object_generation;
21551     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
21552     PREFIX_ASSUME(start_seg != NULL);
21553     heap_segment* seg      = start_seg;
21554     heap_segment* prev_seg = 0;
21555     uint8_t* o             = generation_allocation_start (gen);
21556
21557     //Skip the generation gap object
21558     o = o + AlignQword (size (o));
21559     // We don't need to ever realloc gen3 start so don't touch it.
21560     uint8_t* free_space_start = o;
21561     uint8_t* free_space_end = o;
21562     generation_allocator (gen)->clear();
21563     generation_free_list_space (gen) = 0;
21564     generation_free_obj_space (gen) = 0;
21565
21566     loh_pinned_queue_bos = 0;
21567
21568     while (1)
21569     {
21570         if (o >= heap_segment_allocated (seg))
21571         {
21572             heap_segment* next_seg = heap_segment_next (seg);
21573
21574             if ((heap_segment_plan_allocated (seg) == heap_segment_mem (seg)) &&
21575                 (seg != start_seg) && !heap_segment_read_only_p (seg))
21576             {
21577                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
21578                 assert (prev_seg);
21579                 heap_segment_next (prev_seg) = next_seg;
21580                 heap_segment_next (seg) = freeable_large_heap_segment;
21581                 freeable_large_heap_segment = seg;
21582             }
21583             else
21584             {
21585                 if (!heap_segment_read_only_p (seg))
21586                 {
21587                     // We grew the segment to accommodate allocations.
21588                     if (heap_segment_plan_allocated (seg) > heap_segment_allocated (seg))
21589                     {
21590                         if ((heap_segment_plan_allocated (seg) - plug_skew)  > heap_segment_used (seg))
21591                         {
21592                             heap_segment_used (seg) = heap_segment_plan_allocated (seg) - plug_skew;
21593                         }
21594                     }
21595
21596                     heap_segment_allocated (seg) = heap_segment_plan_allocated (seg);
21597                     dprintf (3, ("Trimming seg to %Ix[", heap_segment_allocated (seg)));
21598                     decommit_heap_segment_pages (seg, 0);
21599                     dprintf (1236, ("CLOH: seg: %Ix, alloc: %Ix, used: %Ix, committed: %Ix",
21600                         seg, 
21601                         heap_segment_allocated (seg),
21602                         heap_segment_used (seg),
21603                         heap_segment_committed (seg)));
21604                     //heap_segment_used (seg) = heap_segment_allocated (seg) - plug_skew;
21605                     dprintf (1236, ("CLOH: used is set to %Ix", heap_segment_used (seg)));
21606                 }
21607                 prev_seg = seg;
21608             }
21609
21610             seg = next_seg;
21611             if (seg == 0)
21612                 break;
21613             else
21614             {
21615                 o = heap_segment_mem (seg);
21616             }
21617         }
21618
21619         if (marked (o))
21620         {
21621             free_space_end = o;
21622             size_t size = AlignQword (size (o));
21623
21624             size_t loh_pad;
21625             uint8_t* reloc = o;
21626             clear_marked (o);
21627
21628             if (pinned (o))
21629             {
21630                 // We are relying on the fact the pinned objects are always looked at in the same order 
21631                 // in plan phase and in compact phase.
21632                 mark* m = loh_pinned_plug_of (loh_deque_pinned_plug());
21633                 uint8_t* plug = pinned_plug (m);
21634                 assert (plug == o);
21635
21636                 loh_pad = pinned_len (m);
21637                 clear_pinned (o);
21638             }
21639             else
21640             {
21641                 loh_pad = AlignQword (loh_padding_obj_size);
21642
21643                 reloc += loh_node_relocation_distance (o);
21644                 gcmemcopy (reloc, o, size, TRUE);
21645             }
21646
21647             thread_gap ((reloc - loh_pad), loh_pad, gen);
21648
21649             o = o + size;
21650             free_space_start = o;
21651             if (o < heap_segment_allocated (seg))
21652             {
21653                 assert (!marked (o));
21654             }
21655         }
21656         else
21657         {
21658             while (o < heap_segment_allocated (seg) && !marked (o))
21659             {
21660                 o = o + AlignQword (size (o));
21661             }
21662         }
21663     }
21664
21665     assert (loh_pinned_plug_que_empty_p());
21666
21667     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
21668         generation_size (max_generation + 1), 
21669         generation_free_list_space (gen),
21670         generation_free_obj_space (gen)));
21671 }
21672
21673 void gc_heap::relocate_in_loh_compact()
21674 {
21675     generation* gen        = large_object_generation;
21676     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21677     uint8_t* o             = generation_allocation_start (gen);
21678
21679     //Skip the generation gap object
21680     o = o + AlignQword (size (o));
21681
21682     relocate_args args;
21683     args.low = gc_low;
21684     args.high = gc_high;
21685     args.last_plug = 0;
21686
21687     while (1)
21688     {
21689         if (o >= heap_segment_allocated (seg))
21690         {
21691             seg = heap_segment_next (seg);
21692             if (seg == 0)
21693             {
21694                 break;
21695             }
21696
21697             o = heap_segment_mem (seg);
21698         }
21699
21700         if (marked (o))
21701         {
21702             size_t size = AlignQword (size (o));
21703
21704             check_class_object_demotion (o);
21705             if (contain_pointers (o))
21706             {
21707                 go_through_object_nostart (method_table (o), o, size(o), pval,
21708                 {
21709                     reloc_survivor_helper (pval);
21710                 });
21711             }
21712
21713             o = o + size;
21714             if (o < heap_segment_allocated (seg))
21715             {
21716                 assert (!marked (o));
21717             }
21718         }
21719         else
21720         {
21721             while (o < heap_segment_allocated (seg) && !marked (o))
21722             {
21723                 o = o + AlignQword (size (o));
21724             }
21725         }
21726     }
21727
21728     dprintf (1235, ("after GC LOH size: %Id, free list: %Id, free obj: %Id\n\n", 
21729         generation_size (max_generation + 1), 
21730         generation_free_list_space (gen),
21731         generation_free_obj_space (gen)));
21732 }
21733
21734 void gc_heap::walk_relocation_for_loh (void* profiling_context, record_surv_fn fn)
21735 {
21736     generation* gen        = large_object_generation;
21737     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));
21738     uint8_t* o             = generation_allocation_start (gen);
21739
21740     //Skip the generation gap object
21741     o = o + AlignQword (size (o));
21742
21743     while (1)
21744     {
21745         if (o >= heap_segment_allocated (seg))
21746         {
21747             seg = heap_segment_next (seg);
21748             if (seg == 0)
21749             {
21750                 break;
21751             }
21752
21753             o = heap_segment_mem (seg);
21754         }
21755
21756         if (marked (o))
21757         {
21758             size_t size = AlignQword (size (o));
21759
21760             ptrdiff_t reloc = loh_node_relocation_distance (o);
21761
21762             STRESS_LOG_PLUG_MOVE(o, (o + size), -reloc);
21763
21764             fn (o, (o + size), reloc, profiling_context, !!settings.compaction, false);
21765
21766             o = o + size;
21767             if (o < heap_segment_allocated (seg))
21768             {
21769                 assert (!marked (o));
21770             }
21771         }
21772         else
21773         {
21774             while (o < heap_segment_allocated (seg) && !marked (o))
21775             {
21776                 o = o + AlignQword (size (o));
21777             }
21778         }
21779     }
21780 }
21781
21782 BOOL gc_heap::loh_object_p (uint8_t* o)
21783 {
21784 #ifdef MULTIPLE_HEAPS
21785     gc_heap* hp = gc_heap::g_heaps [0];
21786     int brick_entry = hp->brick_table[hp->brick_of (o)];
21787 #else //MULTIPLE_HEAPS
21788     int brick_entry = brick_table[brick_of (o)];
21789 #endif //MULTIPLE_HEAPS
21790
21791     return (brick_entry == 0);
21792 }
21793 #endif //FEATURE_LOH_COMPACTION
21794
21795 void gc_heap::convert_to_pinned_plug (BOOL& last_npinned_plug_p, 
21796                                       BOOL& last_pinned_plug_p, 
21797                                       BOOL& pinned_plug_p,
21798                                       size_t ps,
21799                                       size_t& artificial_pinned_size)
21800 {
21801     last_npinned_plug_p = FALSE;
21802     last_pinned_plug_p = TRUE;
21803     pinned_plug_p = TRUE;
21804     artificial_pinned_size = ps;
21805 }
21806
21807 // Because we have the artificial pinning, we can't guarantee that pinned and npinned
21808 // plugs are always interleaved.
21809 void gc_heap::store_plug_gap_info (uint8_t* plug_start,
21810                                    uint8_t* plug_end,
21811                                    BOOL& last_npinned_plug_p, 
21812                                    BOOL& last_pinned_plug_p, 
21813                                    uint8_t*& last_pinned_plug,
21814                                    BOOL& pinned_plug_p,
21815                                    uint8_t* last_object_in_last_plug,
21816                                    BOOL& merge_with_last_pin_p,
21817                                    // this is only for verification purpose
21818                                    size_t last_plug_len)
21819 {
21820     UNREFERENCED_PARAMETER(last_plug_len);
21821
21822     if (!last_npinned_plug_p && !last_pinned_plug_p)
21823     {
21824         //dprintf (3, ("last full plug end: %Ix, full plug start: %Ix", plug_end, plug_start));
21825         dprintf (3, ("Free: %Ix", (plug_start - plug_end)));
21826         assert ((plug_start == plug_end) || ((size_t)(plug_start - plug_end) >= Align (min_obj_size)));
21827         set_gap_size (plug_start, plug_start - plug_end);
21828     }
21829
21830     if (pinned (plug_start))
21831     {
21832         BOOL save_pre_plug_info_p = FALSE;
21833
21834         if (last_npinned_plug_p || last_pinned_plug_p)
21835         {
21836             //if (last_plug_len == Align (min_obj_size))
21837             //{
21838             //    dprintf (3, ("debugging only - last npinned plug is min, check to see if it's correct"));
21839             //    GCToOSInterface::DebugBreak();
21840             //}
21841             save_pre_plug_info_p = TRUE;
21842         }
21843
21844         pinned_plug_p = TRUE;
21845         last_npinned_plug_p = FALSE;
21846
21847         if (last_pinned_plug_p)
21848         {
21849             dprintf (3, ("last plug %Ix was also pinned, should merge", last_pinned_plug));
21850             merge_with_last_pin_p = TRUE;
21851         }
21852         else
21853         {
21854             last_pinned_plug_p = TRUE;
21855             last_pinned_plug = plug_start;
21856                 
21857             enque_pinned_plug (last_pinned_plug, save_pre_plug_info_p, last_object_in_last_plug);
21858
21859             if (save_pre_plug_info_p)
21860             {
21861                 set_gap_size (plug_start, sizeof (gap_reloc_pair));
21862             }
21863         }
21864     }
21865     else
21866     {
21867         if (last_pinned_plug_p)
21868         {
21869             //if (Align (last_plug_len) < min_pre_pin_obj_size)
21870             //{
21871             //    dprintf (3, ("debugging only - last pinned plug is min, check to see if it's correct"));
21872             //    GCToOSInterface::DebugBreak();
21873             //}
21874
21875             save_post_plug_info (last_pinned_plug, last_object_in_last_plug, plug_start);
21876             set_gap_size (plug_start, sizeof (gap_reloc_pair));
21877
21878             verify_pins_with_post_plug_info("after saving post plug info");
21879         }
21880         last_npinned_plug_p = TRUE;
21881         last_pinned_plug_p = FALSE;
21882     }
21883 }
21884
21885 void gc_heap::record_interesting_data_point (interesting_data_point idp)
21886 {
21887 #ifdef GC_CONFIG_DRIVEN
21888     (interesting_data_per_gc[idp])++;
21889 #else
21890     UNREFERENCED_PARAMETER(idp);
21891 #endif //GC_CONFIG_DRIVEN
21892 }
21893
21894 #ifdef _PREFAST_
21895 #pragma warning(push)
21896 #pragma warning(disable:21000) // Suppress PREFast warning about overly large function
21897 #endif //_PREFAST_
21898 void gc_heap::plan_phase (int condemned_gen_number)
21899 {
21900     size_t old_gen2_allocated = 0;
21901     size_t old_gen2_size = 0;
21902
21903     if (condemned_gen_number == (max_generation - 1))
21904     {
21905         old_gen2_allocated = generation_free_list_allocated (generation_of (max_generation));
21906         old_gen2_size = generation_size (max_generation);
21907     }
21908
21909     assert (settings.concurrent == FALSE);
21910
21911     // %type%  category = quote (plan);
21912 #ifdef TIME_GC
21913     unsigned start;
21914     unsigned finish;
21915     start = GetCycleCount32();
21916 #endif //TIME_GC
21917
21918     dprintf (2,("---- Plan Phase ---- Condemned generation %d, promotion: %d",
21919                 condemned_gen_number, settings.promotion ? 1 : 0));
21920
21921     generation*  condemned_gen1 = generation_of (condemned_gen_number);
21922
21923 #ifdef MARK_LIST
21924     BOOL use_mark_list = FALSE;
21925     uint8_t** mark_list_next = &mark_list[0];
21926 #ifdef GC_CONFIG_DRIVEN
21927     dprintf (3, ("total number of marked objects: %Id (%Id)",
21928                  (mark_list_index - &mark_list[0]), ((mark_list_end - &mark_list[0]))));
21929     
21930     if (mark_list_index >= (mark_list_end + 1))
21931         mark_list_index = mark_list_end + 1;
21932 #else
21933     dprintf (3, ("mark_list length: %Id",
21934                  (mark_list_index - &mark_list[0])));
21935 #endif //GC_CONFIG_DRIVEN
21936
21937     if ((condemned_gen_number < max_generation) &&
21938         (mark_list_index <= mark_list_end) 
21939 #ifdef BACKGROUND_GC        
21940         && (!recursive_gc_sync::background_running_p())
21941 #endif //BACKGROUND_GC
21942         )
21943     {
21944 #ifndef MULTIPLE_HEAPS
21945         _sort (&mark_list[0], mark_list_index-1, 0);
21946         //printf ("using mark list at GC #%d", dd_collection_count (dynamic_data_of (0)));
21947         //verify_qsort_array (&mark_list[0], mark_list_index-1);
21948 #endif //!MULTIPLE_HEAPS
21949         use_mark_list = TRUE;
21950         get_gc_data_per_heap()->set_mechanism_bit (gc_mark_list_bit);
21951     }
21952     else
21953     {
21954         dprintf (3, ("mark_list not used"));
21955     }
21956
21957 #endif //MARK_LIST
21958
21959 #ifdef FEATURE_BASICFREEZE
21960     if ((generation_start_segment (condemned_gen1) != ephemeral_heap_segment) &&
21961         ro_segments_in_range)
21962     {
21963         sweep_ro_segments (generation_start_segment (condemned_gen1));
21964     }
21965 #endif // FEATURE_BASICFREEZE
21966
21967 #ifndef MULTIPLE_HEAPS
21968     if (shigh != (uint8_t*)0)
21969     {
21970         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
21971
21972         PREFIX_ASSUME(seg != NULL);
21973
21974         heap_segment* fseg = seg;
21975         do
21976         {
21977             if (slow > heap_segment_mem (seg) &&
21978                 slow < heap_segment_reserved (seg))
21979             {
21980                 if (seg == fseg)
21981                 {
21982                     uint8_t* o = generation_allocation_start (condemned_gen1) +
21983                         Align (size (generation_allocation_start (condemned_gen1)));
21984                     if (slow > o)
21985                     {
21986                         assert ((slow - o) >= (int)Align (min_obj_size));
21987 #ifdef BACKGROUND_GC
21988                         if (current_c_gc_state == c_gc_state_marking)
21989                         {
21990                             bgc_clear_batch_mark_array_bits (o, slow);
21991                         }
21992 #endif //BACKGROUND_GC
21993                         make_unused_array (o, slow - o);
21994                     }
21995                 } 
21996                 else
21997                 {
21998                     assert (condemned_gen_number == max_generation);
21999                     make_unused_array (heap_segment_mem (seg),
22000                                        slow - heap_segment_mem (seg));
22001                 }
22002             }
22003             if (in_range_for_segment (shigh, seg))
22004             {
22005 #ifdef BACKGROUND_GC
22006                 if (current_c_gc_state == c_gc_state_marking)
22007                 {
22008                     bgc_clear_batch_mark_array_bits ((shigh + Align (size (shigh))), heap_segment_allocated (seg));
22009                 }
22010 #endif //BACKGROUND_GC
22011                 heap_segment_allocated (seg) = shigh + Align (size (shigh));
22012             }
22013             // test if the segment is in the range of [slow, shigh]
22014             if (!((heap_segment_reserved (seg) >= slow) &&
22015                   (heap_segment_mem (seg) <= shigh)))
22016             {
22017                 // shorten it to minimum
22018                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
22019             }
22020             seg = heap_segment_next_rw (seg);
22021         } while (seg);
22022     }
22023     else
22024     {
22025         heap_segment* seg = heap_segment_rw (generation_start_segment (condemned_gen1));
22026
22027         PREFIX_ASSUME(seg != NULL);
22028
22029         heap_segment* sseg = seg;
22030         do
22031         {
22032             // shorten it to minimum
22033             if (seg == sseg)
22034             {
22035                 // no survivors make all generations look empty
22036                 uint8_t* o = generation_allocation_start (condemned_gen1) +
22037                     Align (size (generation_allocation_start (condemned_gen1)));
22038 #ifdef BACKGROUND_GC
22039                 if (current_c_gc_state == c_gc_state_marking)
22040                 {
22041                     bgc_clear_batch_mark_array_bits (o, heap_segment_allocated (seg));
22042                 }
22043 #endif //BACKGROUND_GC
22044                 heap_segment_allocated (seg) = o;
22045             }
22046             else
22047             {
22048                 assert (condemned_gen_number == max_generation);
22049 #ifdef BACKGROUND_GC
22050                 if (current_c_gc_state == c_gc_state_marking)
22051                 {
22052                     bgc_clear_batch_mark_array_bits (heap_segment_mem (seg), heap_segment_allocated (seg));
22053                 }
22054 #endif //BACKGROUND_GC
22055                 heap_segment_allocated (seg) =  heap_segment_mem (seg);
22056             }
22057             seg = heap_segment_next_rw (seg);
22058         } while (seg);
22059     }
22060
22061 #endif //MULTIPLE_HEAPS
22062
22063     heap_segment*  seg1 = heap_segment_rw (generation_start_segment (condemned_gen1));
22064
22065     PREFIX_ASSUME(seg1 != NULL);
22066
22067     uint8_t*  end = heap_segment_allocated (seg1);
22068     uint8_t*  first_condemned_address = generation_allocation_start (condemned_gen1);
22069     uint8_t*  x = first_condemned_address;
22070
22071     assert (!marked (x));
22072     uint8_t*  plug_end = x;
22073     uint8_t*  tree = 0;
22074     size_t  sequence_number = 0;
22075     uint8_t*  last_node = 0;
22076     size_t  current_brick = brick_of (x);
22077     BOOL  allocate_in_condemned = ((condemned_gen_number == max_generation)||
22078                                    (settings.promotion == FALSE));
22079     int  active_old_gen_number = condemned_gen_number;
22080     int  active_new_gen_number = (allocate_in_condemned ? condemned_gen_number:
22081                                   (1 + condemned_gen_number));
22082     generation*  older_gen = 0;
22083     generation* consing_gen = condemned_gen1;
22084     alloc_list  r_free_list [MAX_BUCKET_COUNT];
22085
22086     size_t r_free_list_space = 0;
22087     size_t r_free_obj_space = 0;
22088     size_t r_older_gen_free_list_allocated = 0;
22089     size_t r_older_gen_condemned_allocated = 0;
22090     size_t r_older_gen_end_seg_allocated = 0;
22091     uint8_t*  r_allocation_pointer = 0;
22092     uint8_t*  r_allocation_limit = 0;
22093     uint8_t* r_allocation_start_region = 0;
22094     heap_segment*  r_allocation_segment = 0;
22095 #ifdef FREE_USAGE_STATS
22096     size_t r_older_gen_free_space[NUM_GEN_POWER2];
22097 #endif //FREE_USAGE_STATS
22098
22099     if ((condemned_gen_number < max_generation))
22100     {
22101         older_gen = generation_of (min (max_generation, 1 + condemned_gen_number));
22102         generation_allocator (older_gen)->copy_to_alloc_list (r_free_list);
22103
22104         r_free_list_space = generation_free_list_space (older_gen);
22105         r_free_obj_space = generation_free_obj_space (older_gen);
22106 #ifdef FREE_USAGE_STATS
22107         memcpy (r_older_gen_free_space, older_gen->gen_free_spaces, sizeof (r_older_gen_free_space));
22108 #endif //FREE_USAGE_STATS
22109         generation_allocate_end_seg_p (older_gen) = FALSE;
22110         r_older_gen_free_list_allocated = generation_free_list_allocated (older_gen);
22111         r_older_gen_condemned_allocated = generation_condemned_allocated (older_gen);
22112         r_older_gen_end_seg_allocated = generation_end_seg_allocated (older_gen);
22113         r_allocation_limit = generation_allocation_limit (older_gen);
22114         r_allocation_pointer = generation_allocation_pointer (older_gen);
22115         r_allocation_start_region = generation_allocation_context_start_region (older_gen);
22116         r_allocation_segment = generation_allocation_segment (older_gen);
22117         heap_segment* start_seg = heap_segment_rw (generation_start_segment (older_gen));
22118
22119         PREFIX_ASSUME(start_seg != NULL);
22120
22121         if (start_seg != ephemeral_heap_segment)
22122         {
22123             assert (condemned_gen_number == (max_generation - 1));
22124             while (start_seg && (start_seg != ephemeral_heap_segment))
22125             {
22126                 assert (heap_segment_allocated (start_seg) >=
22127                         heap_segment_mem (start_seg));
22128                 assert (heap_segment_allocated (start_seg) <=
22129                         heap_segment_reserved (start_seg));
22130                 heap_segment_plan_allocated (start_seg) =
22131                     heap_segment_allocated (start_seg);
22132                 start_seg = heap_segment_next_rw (start_seg);
22133             }
22134         }
22135     }
22136
22137     //reset all of the segment allocated sizes
22138     {
22139         heap_segment*  seg2 = heap_segment_rw (generation_start_segment (condemned_gen1));
22140
22141         PREFIX_ASSUME(seg2 != NULL);
22142
22143         while (seg2)
22144         {
22145             heap_segment_plan_allocated (seg2) =
22146                 heap_segment_mem (seg2);
22147             seg2 = heap_segment_next_rw (seg2);
22148         }
22149     }
22150     int  condemned_gn = condemned_gen_number;
22151
22152     int bottom_gen = 0;
22153     init_free_and_plug();
22154
22155     while (condemned_gn >= bottom_gen)
22156     {
22157         generation*  condemned_gen2 = generation_of (condemned_gn);
22158         generation_allocator (condemned_gen2)->clear();
22159         generation_free_list_space (condemned_gen2) = 0;
22160         generation_free_obj_space (condemned_gen2) = 0;
22161         generation_allocation_size (condemned_gen2) = 0;
22162         generation_condemned_allocated (condemned_gen2) = 0; 
22163         generation_pinned_allocated (condemned_gen2) = 0; 
22164         generation_free_list_allocated(condemned_gen2) = 0; 
22165         generation_end_seg_allocated (condemned_gen2) = 0; 
22166         generation_pinned_allocation_sweep_size (condemned_gen2) = 0;
22167         generation_pinned_allocation_compact_size (condemned_gen2) = 0;
22168 #ifdef FREE_USAGE_STATS
22169         generation_pinned_free_obj_space (condemned_gen2) = 0;
22170         generation_allocated_in_pinned_free (condemned_gen2) = 0;
22171         generation_allocated_since_last_pin (condemned_gen2) = 0;
22172 #endif //FREE_USAGE_STATS
22173         generation_plan_allocation_start (condemned_gen2) = 0;
22174         generation_allocation_segment (condemned_gen2) =
22175             heap_segment_rw (generation_start_segment (condemned_gen2));
22176
22177         PREFIX_ASSUME(generation_allocation_segment(condemned_gen2) != NULL);
22178
22179         if (generation_start_segment (condemned_gen2) != ephemeral_heap_segment)
22180         {
22181             generation_allocation_pointer (condemned_gen2) =
22182                 heap_segment_mem (generation_allocation_segment (condemned_gen2));
22183         }
22184         else
22185         {
22186             generation_allocation_pointer (condemned_gen2) = generation_allocation_start (condemned_gen2);
22187         }
22188
22189         generation_allocation_limit (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
22190         generation_allocation_context_start_region (condemned_gen2) = generation_allocation_pointer (condemned_gen2);
22191
22192         condemned_gn--;
22193     }
22194
22195     BOOL allocate_first_generation_start = FALSE;
22196     
22197     if (allocate_in_condemned)
22198     {
22199         allocate_first_generation_start = TRUE;
22200     }
22201
22202     dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
22203
22204     demotion_low = MAX_PTR;
22205     demotion_high = heap_segment_allocated (ephemeral_heap_segment);
22206
22207     // If we are doing a gen1 only because of cards, it means we should not demote any pinned plugs
22208     // from gen1. They should get promoted to gen2.
22209     demote_gen1_p = !(settings.promotion && 
22210                       (settings.condemned_generation == (max_generation - 1)) && 
22211                       gen_to_condemn_reasons.is_only_condition (gen_low_card_p));
22212
22213     total_ephemeral_size = 0;
22214
22215     print_free_and_plug ("BP");
22216
22217     for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
22218     {
22219         generation* temp_gen = generation_of (gen_idx);
22220
22221         dprintf (2, ("gen%d start %Ix, plan start %Ix",
22222             gen_idx, 
22223             generation_allocation_start (temp_gen),
22224             generation_plan_allocation_start (temp_gen)));
22225     }
22226
22227     BOOL fire_pinned_plug_events_p = EVENT_ENABLED(PinPlugAtGCTime);
22228     size_t last_plug_len = 0;
22229
22230     while (1)
22231     {
22232         if (x >= end)
22233         {
22234             assert (x == end);
22235             assert (heap_segment_allocated (seg1) == end);
22236             heap_segment_allocated (seg1) = plug_end;
22237
22238             current_brick = update_brick_table (tree, current_brick, x, plug_end);
22239             dprintf (3, ("end of seg: new tree, sequence# 0"));
22240             sequence_number = 0;
22241             tree = 0;
22242
22243             if (heap_segment_next_rw (seg1))
22244             {
22245                 seg1 = heap_segment_next_rw (seg1);
22246                 end = heap_segment_allocated (seg1);
22247                 plug_end = x = heap_segment_mem (seg1);
22248                 current_brick = brick_of (x);
22249                 dprintf(3,( " From %Ix to %Ix", (size_t)x, (size_t)end));
22250                 continue;
22251             }
22252             else
22253             {
22254                 break;
22255             }
22256         }
22257
22258         BOOL last_npinned_plug_p = FALSE;
22259         BOOL last_pinned_plug_p = FALSE;
22260
22261         // last_pinned_plug is the beginning of the last pinned plug. If we merge a plug into a pinned
22262         // plug we do not change the value of last_pinned_plug. This happens with artificially pinned plugs -
22263         // it can be merged with a previous pinned plug and a pinned plug after it can be merged with it.
22264         uint8_t* last_pinned_plug = 0;
22265         size_t num_pinned_plugs_in_plug = 0;
22266
22267         uint8_t* last_object_in_plug = 0;
22268
22269         while ((x < end) && marked (x))
22270         {
22271             uint8_t*  plug_start = x;
22272             uint8_t*  saved_plug_end = plug_end;
22273             BOOL   pinned_plug_p = FALSE;
22274             BOOL   npin_before_pin_p = FALSE;
22275             BOOL   saved_last_npinned_plug_p = last_npinned_plug_p;
22276             uint8_t*  saved_last_object_in_plug = last_object_in_plug;
22277             BOOL   merge_with_last_pin_p = FALSE;
22278
22279             size_t added_pinning_size = 0;
22280             size_t artificial_pinned_size = 0;
22281
22282             store_plug_gap_info (plug_start, plug_end, last_npinned_plug_p, last_pinned_plug_p, 
22283                                  last_pinned_plug, pinned_plug_p, last_object_in_plug, 
22284                                  merge_with_last_pin_p, last_plug_len);
22285
22286 #ifdef FEATURE_STRUCTALIGN
22287             int requiredAlignment = ((CObjectHeader*)plug_start)->GetRequiredAlignment();
22288             size_t alignmentOffset = OBJECT_ALIGNMENT_OFFSET;
22289 #endif // FEATURE_STRUCTALIGN
22290
22291             {
22292                 uint8_t* xl = x;
22293                 while ((xl < end) && marked (xl) && (pinned (xl) == pinned_plug_p))
22294                 {
22295                     assert (xl < end);
22296                     if (pinned(xl))
22297                     {
22298                         clear_pinned (xl);
22299                     }
22300 #ifdef FEATURE_STRUCTALIGN
22301                     else
22302                     {
22303                         int obj_requiredAlignment = ((CObjectHeader*)xl)->GetRequiredAlignment();
22304                         if (obj_requiredAlignment > requiredAlignment)
22305                         {
22306                             requiredAlignment = obj_requiredAlignment;
22307                             alignmentOffset = xl - plug_start + OBJECT_ALIGNMENT_OFFSET;
22308                         }
22309                     }
22310 #endif // FEATURE_STRUCTALIGN
22311
22312                     clear_marked (xl);
22313
22314                     dprintf(4, ("+%Ix+", (size_t)xl));
22315                     assert ((size (xl) > 0));
22316                     assert ((size (xl) <= loh_size_threshold));
22317
22318                     last_object_in_plug = xl;
22319
22320                     xl = xl + Align (size (xl));
22321                     Prefetch (xl);
22322                 }
22323
22324                 BOOL next_object_marked_p = ((xl < end) && marked (xl));
22325
22326                 if (pinned_plug_p)
22327                 {
22328                     // If it is pinned we need to extend to the next marked object as we can't use part of
22329                     // a pinned object to make the artificial gap (unless the last 3 ptr sized words are all
22330                     // references but for now I am just using the next non pinned object for that).
22331                     if (next_object_marked_p) 
22332                     {
22333                         clear_marked (xl);
22334                         last_object_in_plug = xl;
22335                         size_t extra_size = Align (size (xl));
22336                         xl = xl + extra_size;
22337                         added_pinning_size = extra_size;
22338                     }
22339                 }
22340                 else
22341                 {
22342                     if (next_object_marked_p)
22343                         npin_before_pin_p = TRUE;
22344                 }
22345
22346                 assert (xl <= end);
22347                 x = xl;
22348             }
22349             dprintf (3, ( "%Ix[", (size_t)x));
22350             plug_end = x;
22351             size_t ps = plug_end - plug_start;
22352             last_plug_len = ps;
22353             dprintf (3, ( "%Ix[(%Ix)", (size_t)x, ps));
22354             uint8_t*  new_address = 0;
22355
22356             if (!pinned_plug_p)
22357             {
22358                 if (allocate_in_condemned &&
22359                     (settings.condemned_generation == max_generation) &&
22360                     (ps > OS_PAGE_SIZE))
22361                 {
22362                     ptrdiff_t reloc = plug_start - generation_allocation_pointer (consing_gen);
22363                     //reloc should >=0 except when we relocate
22364                     //across segments and the dest seg is higher then the src
22365
22366                     if ((ps > (8*OS_PAGE_SIZE)) &&
22367                         (reloc > 0) &&
22368                         ((size_t)reloc < (ps/16)))
22369                     {
22370                         dprintf (3, ("Pinning %Ix; reloc would have been: %Ix",
22371                                      (size_t)plug_start, reloc));
22372                         // The last plug couldn't have been a npinned plug or it would have
22373                         // included this plug.
22374                         assert (!saved_last_npinned_plug_p);
22375
22376                         if (last_pinned_plug)
22377                         {
22378                             dprintf (3, ("artificially pinned plug merged with last pinned plug"));
22379                             merge_with_last_pin_p = TRUE;
22380                         }
22381                         else
22382                         {
22383                             enque_pinned_plug (plug_start, FALSE, 0);
22384                             last_pinned_plug = plug_start;
22385                         }
22386
22387                         convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
22388                                                 ps, artificial_pinned_size);
22389                     }
22390                 }
22391             }
22392
22393             if (allocate_first_generation_start)
22394             {
22395                 allocate_first_generation_start = FALSE;
22396                 plan_generation_start (condemned_gen1, consing_gen, plug_start);
22397                 assert (generation_plan_allocation_start (condemned_gen1));
22398             }
22399
22400             if (seg1 == ephemeral_heap_segment)
22401             {
22402                 process_ephemeral_boundaries (plug_start, active_new_gen_number,
22403                                               active_old_gen_number,
22404                                               consing_gen,
22405                                               allocate_in_condemned);
22406             }
22407
22408             dprintf (3, ("adding %Id to gen%d surv", ps, active_old_gen_number));
22409
22410             dynamic_data* dd_active_old = dynamic_data_of (active_old_gen_number);
22411             dd_survived_size (dd_active_old) += ps;
22412
22413             BOOL convert_to_pinned_p = FALSE;
22414
22415             if (!pinned_plug_p)
22416             {
22417 #if defined (RESPECT_LARGE_ALIGNMENT) || defined (FEATURE_STRUCTALIGN)
22418                 dd_num_npinned_plugs (dd_active_old)++;
22419 #endif //RESPECT_LARGE_ALIGNMENT || FEATURE_STRUCTALIGN
22420
22421                 add_gen_plug (active_old_gen_number, ps);
22422
22423                 if (allocate_in_condemned)
22424                 {
22425                     verify_pins_with_post_plug_info("before aic");
22426
22427                     new_address =
22428                         allocate_in_condemned_generations (consing_gen,
22429                                                            ps,
22430                                                            active_old_gen_number,
22431 #ifdef SHORT_PLUGS
22432                                                            &convert_to_pinned_p,
22433                                                            (npin_before_pin_p ? plug_end : 0),
22434                                                            seg1,
22435 #endif //SHORT_PLUGS
22436                                                            plug_start REQD_ALIGN_AND_OFFSET_ARG);
22437                     verify_pins_with_post_plug_info("after aic");
22438                 }
22439                 else
22440                 {
22441                     new_address = allocate_in_older_generation (older_gen, ps, active_old_gen_number, plug_start REQD_ALIGN_AND_OFFSET_ARG);
22442
22443                     if (new_address != 0)
22444                     {
22445                         if (settings.condemned_generation == (max_generation - 1))
22446                         {
22447                             dprintf (3, (" NA: %Ix-%Ix -> %Ix, %Ix (%Ix)",
22448                                 plug_start, plug_end,
22449                                 (size_t)new_address, (size_t)new_address + (plug_end - plug_start),
22450                                 (size_t)(plug_end - plug_start)));
22451                         }
22452                     }
22453                     else
22454                     {
22455                         if (generation_allocator(older_gen)->discard_if_no_fit_p())
22456                         {
22457                             allocate_in_condemned = TRUE;
22458                         }
22459
22460                         new_address = allocate_in_condemned_generations (consing_gen, ps, active_old_gen_number, 
22461 #ifdef SHORT_PLUGS
22462                                                                          &convert_to_pinned_p,
22463                                                                          (npin_before_pin_p ? plug_end : 0),
22464                                                                          seg1,
22465 #endif //SHORT_PLUGS
22466                                                                          plug_start REQD_ALIGN_AND_OFFSET_ARG);
22467                     }
22468                 }
22469
22470                 if (convert_to_pinned_p)
22471                 {
22472                     assert (last_npinned_plug_p != FALSE);
22473                     assert (last_pinned_plug_p == FALSE);
22474                     convert_to_pinned_plug (last_npinned_plug_p, last_pinned_plug_p, pinned_plug_p,
22475                                             ps, artificial_pinned_size);
22476                     enque_pinned_plug (plug_start, FALSE, 0);
22477                     last_pinned_plug = plug_start;
22478                 }
22479                 else
22480                 {
22481                     if (!new_address)
22482                     {
22483                         //verify that we are at then end of the ephemeral segment
22484                         assert (generation_allocation_segment (consing_gen) ==
22485                                 ephemeral_heap_segment);
22486                         //verify that we are near the end
22487                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) <
22488                                 heap_segment_allocated (ephemeral_heap_segment));
22489                         assert ((generation_allocation_pointer (consing_gen) + Align (ps)) >
22490                                 (heap_segment_allocated (ephemeral_heap_segment) + Align (min_obj_size)));
22491                     }
22492                     else
22493                     {
22494 #ifdef SIMPLE_DPRINTF
22495                         dprintf (3, ("(%Ix)[%Ix->%Ix, NA: [%Ix(%Id), %Ix[: %Ix(%d)",
22496                             (size_t)(node_gap_size (plug_start)), 
22497                             plug_start, plug_end, (size_t)new_address, (size_t)(plug_start - new_address),
22498                                 (size_t)new_address + ps, ps, 
22499                                 (is_plug_padded (plug_start) ? 1 : 0)));
22500 #endif //SIMPLE_DPRINTF
22501
22502 #ifdef SHORT_PLUGS
22503                         if (is_plug_padded (plug_start))
22504                         {
22505                             dprintf (3, ("%Ix was padded", plug_start));
22506                             dd_padding_size (dd_active_old) += Align (min_obj_size);
22507                         }
22508 #endif //SHORT_PLUGS
22509                     }
22510                 }
22511             }
22512
22513             if (pinned_plug_p)
22514             {
22515                 if (fire_pinned_plug_events_p)
22516                 {
22517                     FIRE_EVENT(PinPlugAtGCTime, plug_start, plug_end, 
22518                                (merge_with_last_pin_p ? 0 : (uint8_t*)node_gap_size (plug_start)));
22519                 }
22520
22521                 if (merge_with_last_pin_p)
22522                 {
22523                     merge_with_last_pinned_plug (last_pinned_plug, ps);
22524                 }
22525                 else
22526                 {
22527                     assert (last_pinned_plug == plug_start);
22528                     set_pinned_info (plug_start, ps, consing_gen);
22529                 }
22530
22531                 new_address = plug_start;
22532
22533                 dprintf (3, ( "(%Ix)PP: [%Ix, %Ix[%Ix](m:%d)",
22534                             (size_t)(node_gap_size (plug_start)), (size_t)plug_start,
22535                             (size_t)plug_end, ps,
22536                             (merge_with_last_pin_p ? 1 : 0)));
22537
22538                 dprintf (3, ("adding %Id to gen%d pinned surv", plug_end - plug_start, active_old_gen_number));
22539                 dd_pinned_survived_size (dd_active_old) += plug_end - plug_start;
22540                 dd_added_pinned_size (dd_active_old) += added_pinning_size;
22541                 dd_artificial_pinned_survived_size (dd_active_old) += artificial_pinned_size;
22542
22543                 if (!demote_gen1_p && (active_old_gen_number == (max_generation - 1)))
22544                 {
22545                     last_gen1_pin_end = plug_end;
22546                 }
22547             }
22548
22549 #ifdef _DEBUG
22550             // detect forward allocation in the same segment
22551             assert (!((new_address > plug_start) &&
22552                 (new_address < heap_segment_reserved (seg1))));
22553 #endif //_DEBUG
22554
22555             if (!merge_with_last_pin_p)
22556             {
22557                 if (current_brick != brick_of (plug_start))
22558                 {
22559                     current_brick = update_brick_table (tree, current_brick, plug_start, saved_plug_end);
22560                     sequence_number = 0;
22561                     tree = 0;
22562                 }
22563
22564                 set_node_relocation_distance (plug_start, (new_address - plug_start));
22565                 if (last_node && (node_relocation_distance (last_node) ==
22566                                   (node_relocation_distance (plug_start) +
22567                                    (ptrdiff_t)node_gap_size (plug_start))))
22568                 {
22569                     //dprintf(3,( " Lb"));
22570                     dprintf (3, ("%Ix Lb", plug_start));
22571                     set_node_left (plug_start);
22572                 }
22573                 if (0 == sequence_number)
22574                 {
22575                     dprintf (2, ("sn: 0, tree is set to %Ix", plug_start));
22576                     tree = plug_start;
22577                 }
22578
22579                 verify_pins_with_post_plug_info("before insert node");
22580
22581                 tree = insert_node (plug_start, ++sequence_number, tree, last_node);
22582                 dprintf (3, ("tree is %Ix (b: %Ix) after insert_node", tree, brick_of (tree)));
22583                 last_node = plug_start;
22584
22585 #ifdef _DEBUG
22586                 // If we detect if the last plug is pinned plug right before us, we should save this gap info
22587                 if (!pinned_plug_p)
22588                 {
22589                     if (mark_stack_tos > 0)
22590                     {
22591                         mark& m = mark_stack_array[mark_stack_tos - 1];
22592                         if (m.has_post_plug_info())
22593                         {
22594                             uint8_t* post_plug_info_start = m.saved_post_plug_info_start;
22595                             size_t* current_plug_gap_start = (size_t*)(plug_start - sizeof (plug_and_gap));
22596                             if ((uint8_t*)current_plug_gap_start == post_plug_info_start)
22597                             {
22598                                 dprintf (3, ("Ginfo: %Ix, %Ix, %Ix",
22599                                     *current_plug_gap_start, *(current_plug_gap_start + 1),
22600                                     *(current_plug_gap_start + 2)));
22601                                 memcpy (&(m.saved_post_plug_debug), current_plug_gap_start, sizeof (gap_reloc_pair));
22602                             }
22603                         }
22604                     }
22605                 }
22606 #endif //_DEBUG
22607
22608                 verify_pins_with_post_plug_info("after insert node");
22609             }
22610         }
22611         
22612         if (num_pinned_plugs_in_plug > 1)
22613         {
22614             dprintf (3, ("more than %Id pinned plugs in this plug", num_pinned_plugs_in_plug));
22615         }
22616
22617         {
22618 #ifdef MARK_LIST
22619             if (use_mark_list)
22620             {
22621                while ((mark_list_next < mark_list_index) &&
22622                       (*mark_list_next <= x))
22623                {
22624                    mark_list_next++;
22625                }
22626                if ((mark_list_next < mark_list_index)
22627 #ifdef MULTIPLE_HEAPS
22628                    && (*mark_list_next < end) //for multiple segments
22629 #endif //MULTIPLE_HEAPS
22630                    )
22631                    x = *mark_list_next;
22632                else
22633                    x = end;
22634             }
22635             else
22636 #endif //MARK_LIST
22637             {
22638                 uint8_t* xl = x;
22639 #ifdef BACKGROUND_GC
22640                 if (current_c_gc_state == c_gc_state_marking)
22641                 {
22642                     assert (recursive_gc_sync::background_running_p());
22643                     while ((xl < end) && !marked (xl))
22644                     {
22645                         dprintf (4, ("-%Ix-", (size_t)xl));
22646                         assert ((size (xl) > 0));
22647                         background_object_marked (xl, TRUE);
22648                         xl = xl + Align (size (xl));
22649                         Prefetch (xl);
22650                     }
22651                 }
22652                 else
22653 #endif //BACKGROUND_GC
22654                 {
22655                     while ((xl < end) && !marked (xl))
22656                     {
22657                         dprintf (4, ("-%Ix-", (size_t)xl));
22658                         assert ((size (xl) > 0));
22659                         xl = xl + Align (size (xl));
22660                         Prefetch (xl);
22661                     }
22662                 }
22663                 assert (xl <= end);
22664                 x = xl;
22665             }
22666         }
22667     }
22668
22669     while (!pinned_plug_que_empty_p())
22670     {
22671         if (settings.promotion)
22672         {
22673             uint8_t* pplug = pinned_plug (oldest_pin());
22674             if (in_range_for_segment (pplug, ephemeral_heap_segment))
22675             {
22676                 consing_gen = ensure_ephemeral_heap_segment (consing_gen);
22677                 //allocate all of the generation gaps
22678                 while (active_new_gen_number > 0)
22679                 {
22680                     active_new_gen_number--;
22681
22682                     if (active_new_gen_number == (max_generation - 1))
22683                     {
22684                         maxgen_pinned_compact_before_advance = generation_pinned_allocation_compact_size (generation_of (max_generation));
22685                         if (!demote_gen1_p)
22686                             advance_pins_for_demotion (consing_gen);
22687                     }
22688
22689                     generation* gen = generation_of (active_new_gen_number);
22690                     plan_generation_start (gen, consing_gen, 0);
22691
22692                     if (demotion_low == MAX_PTR)
22693                     {
22694                         demotion_low = pplug;
22695                         dprintf (3, ("end plan: dlow->%Ix", demotion_low));
22696                     }
22697
22698                     dprintf (2, ("(%d)gen%d plan start: %Ix", 
22699                                   heap_number, active_new_gen_number, (size_t)generation_plan_allocation_start (gen)));
22700                     assert (generation_plan_allocation_start (gen));
22701                 }
22702             }
22703         }
22704
22705         if (pinned_plug_que_empty_p())
22706             break;
22707
22708         size_t  entry = deque_pinned_plug();
22709         mark*  m = pinned_plug_of (entry);
22710         uint8_t*  plug = pinned_plug (m);
22711         size_t  len = pinned_len (m);
22712
22713         // detect pinned block in different segment (later) than
22714         // allocation segment
22715         heap_segment* nseg = heap_segment_rw (generation_allocation_segment (consing_gen));
22716
22717         while ((plug < generation_allocation_pointer (consing_gen)) ||
22718                (plug >= heap_segment_allocated (nseg)))
22719         {
22720             assert ((plug < heap_segment_mem (nseg)) ||
22721                     (plug > heap_segment_reserved (nseg)));
22722             //adjust the end of the segment to be the end of the plug
22723             assert (generation_allocation_pointer (consing_gen)>=
22724                     heap_segment_mem (nseg));
22725             assert (generation_allocation_pointer (consing_gen)<=
22726                     heap_segment_committed (nseg));
22727
22728             heap_segment_plan_allocated (nseg) =
22729                 generation_allocation_pointer (consing_gen);
22730             //switch allocation segment
22731             nseg = heap_segment_next_rw (nseg);
22732             generation_allocation_segment (consing_gen) = nseg;
22733             //reset the allocation pointer and limits
22734             generation_allocation_pointer (consing_gen) =
22735                 heap_segment_mem (nseg);
22736         }
22737
22738         set_new_pin_info (m, generation_allocation_pointer (consing_gen));
22739         dprintf (2, ("pin %Ix b: %Ix->%Ix", plug, brick_of (plug),
22740             (size_t)(brick_table[brick_of (plug)])));
22741
22742         generation_allocation_pointer (consing_gen) = plug + len;
22743         generation_allocation_limit (consing_gen) =
22744             generation_allocation_pointer (consing_gen);
22745         //Add the size of the pinned plug to the right pinned allocations
22746         //find out which gen this pinned plug came from 
22747         int frgn = object_gennum (plug);
22748         if ((frgn != (int)max_generation) && settings.promotion)
22749         {
22750             generation_pinned_allocation_sweep_size ((generation_of (frgn +1))) += len;
22751         }
22752
22753     }
22754
22755     plan_generation_starts (consing_gen);
22756     print_free_and_plug ("AP");
22757
22758     {
22759 #ifdef SIMPLE_DPRINTF
22760         for (int gen_idx = 0; gen_idx <= max_generation; gen_idx++)
22761         {
22762             generation* temp_gen = generation_of (gen_idx);
22763             dynamic_data* temp_dd = dynamic_data_of (gen_idx);
22764
22765             int added_pinning_ratio = 0;
22766             int artificial_pinned_ratio = 0;
22767
22768             if (dd_pinned_survived_size (temp_dd) != 0)
22769             {
22770                 added_pinning_ratio = (int)((float)dd_added_pinned_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22771                 artificial_pinned_ratio = (int)((float)dd_artificial_pinned_survived_size (temp_dd) * 100 / (float)dd_pinned_survived_size (temp_dd));
22772             }
22773
22774             size_t padding_size = 
22775 #ifdef SHORT_PLUGS
22776                 dd_padding_size (temp_dd);
22777 #else
22778                 0;
22779 #endif //SHORT_PLUGS
22780             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",
22781                 gen_idx, 
22782                 generation_allocation_start (temp_gen),
22783                 generation_plan_allocation_start (temp_gen),
22784                 (size_t)(generation_plan_allocation_start (temp_gen) - generation_allocation_start (temp_gen)),
22785                 generation_allocation_size (temp_gen),
22786                 generation_pinned_allocation_compact_size (temp_gen),
22787                 generation_pinned_allocation_sweep_size (temp_gen),
22788                 dd_survived_size (temp_dd),
22789                 dd_pinned_survived_size (temp_dd),
22790                 added_pinning_ratio,
22791                 artificial_pinned_ratio,
22792                 (dd_survived_size (temp_dd) - dd_pinned_survived_size (temp_dd)),
22793                 padding_size));
22794         }
22795 #endif //SIMPLE_DPRINTF
22796     }
22797
22798     if (settings.condemned_generation == (max_generation - 1 ))
22799     {
22800         size_t plan_gen2_size = generation_plan_size (max_generation);
22801         size_t growth = plan_gen2_size - old_gen2_size;
22802
22803         generation* older_gen = generation_of (settings.condemned_generation + 1);
22804         size_t rejected_free_space = generation_free_obj_space (older_gen) - r_free_obj_space;
22805         size_t free_list_allocated = generation_free_list_allocated (older_gen) - r_older_gen_free_list_allocated;
22806         size_t end_seg_allocated = generation_end_seg_allocated (older_gen) - r_older_gen_end_seg_allocated;
22807         size_t condemned_allocated = generation_condemned_allocated (older_gen) - r_older_gen_condemned_allocated;
22808
22809         if (growth > 0)
22810         {
22811             dprintf (1, ("gen2 grew %Id (end seg alloc: %Id, condemned alloc: %Id", 
22812                          growth, end_seg_allocated, condemned_allocated));
22813
22814             maxgen_size_inc_p = true;
22815         }
22816         else
22817         {
22818             dprintf (2, ("gen2 shrank %Id (end seg alloc: %Id, , condemned alloc: %Id, gen1 c alloc: %Id", 
22819                          (old_gen2_size - plan_gen2_size), end_seg_allocated, condemned_allocated,
22820                          generation_condemned_allocated (generation_of (max_generation - 1))));
22821         }
22822
22823         dprintf (1, ("older gen's free alloc: %Id->%Id, seg alloc: %Id->%Id, condemned alloc: %Id->%Id",
22824                     r_older_gen_free_list_allocated, generation_free_list_allocated (older_gen),
22825                     r_older_gen_end_seg_allocated, generation_end_seg_allocated (older_gen), 
22826                     r_older_gen_condemned_allocated, generation_condemned_allocated (older_gen)));
22827
22828         dprintf (1, ("this GC did %Id free list alloc(%Id bytes free space rejected)",
22829             free_list_allocated, rejected_free_space));
22830
22831         maxgen_size_increase* maxgen_size_info = &(get_gc_data_per_heap()->maxgen_size_info);
22832         maxgen_size_info->free_list_allocated = free_list_allocated;
22833         maxgen_size_info->free_list_rejected = rejected_free_space;
22834         maxgen_size_info->end_seg_allocated = end_seg_allocated;
22835         maxgen_size_info->condemned_allocated = condemned_allocated;
22836         maxgen_size_info->pinned_allocated = maxgen_pinned_compact_before_advance;
22837         maxgen_size_info->pinned_allocated_advance = generation_pinned_allocation_compact_size (generation_of (max_generation)) - maxgen_pinned_compact_before_advance;
22838
22839 #ifdef FREE_USAGE_STATS
22840         int free_list_efficiency = 0;
22841         if ((free_list_allocated + rejected_free_space) != 0)
22842             free_list_efficiency = (int)(((float) (free_list_allocated) / (float)(free_list_allocated + rejected_free_space)) * (float)100);
22843
22844         int running_free_list_efficiency = (int)(generation_allocator_efficiency(older_gen)*100);
22845
22846         dprintf (1, ("gen%d free list alloc effi: %d%%, current effi: %d%%",
22847                     older_gen->gen_num,
22848                     free_list_efficiency, running_free_list_efficiency));
22849
22850         dprintf (1, ("gen2 free list change"));
22851         for (int j = 0; j < NUM_GEN_POWER2; j++)
22852         {
22853             dprintf (1, ("[h%d][#%Id]: 2^%d: F: %Id->%Id(%Id), P: %Id", 
22854                 heap_number, 
22855                 settings.gc_index,
22856                 (j + 10), r_older_gen_free_space[j], older_gen->gen_free_spaces[j], 
22857                 (ptrdiff_t)(r_older_gen_free_space[j] - older_gen->gen_free_spaces[j]),
22858                 (generation_of(max_generation - 1))->gen_plugs[j]));
22859         }
22860 #endif //FREE_USAGE_STATS
22861     }
22862
22863     size_t fragmentation =
22864         generation_fragmentation (generation_of (condemned_gen_number),
22865                                   consing_gen,
22866                                   heap_segment_allocated (ephemeral_heap_segment));
22867
22868     dprintf (2,("Fragmentation: %Id", fragmentation));
22869     dprintf (2,("---- End of Plan phase ----"));
22870
22871 #ifdef TIME_GC
22872     finish = GetCycleCount32();
22873     plan_time = finish - start;
22874 #endif //TIME_GC
22875
22876     // We may update write barrier code.  We assume here EE has been suspended if we are on a GC thread.
22877     assert(IsGCInProgress());
22878
22879     BOOL should_expand = FALSE;
22880     BOOL should_compact= FALSE;
22881     ephemeral_promotion = FALSE;
22882
22883 #ifdef BIT64
22884     if ((!settings.concurrent) &&
22885         !provisional_mode_triggered &&
22886         ((condemned_gen_number < max_generation) && 
22887          ((settings.gen0_reduction_count > 0) || (settings.entry_memory_load >= 95))))
22888     {
22889         dprintf (GTC_LOG, ("gen0 reduction count is %d, condemning %d, mem load %d",
22890                      settings.gen0_reduction_count,
22891                      condemned_gen_number,
22892                      settings.entry_memory_load));
22893         should_compact = TRUE;
22894
22895         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, 
22896             ((settings.gen0_reduction_count > 0) ? compact_fragmented_gen0 : compact_high_mem_load));
22897
22898         if ((condemned_gen_number >= (max_generation - 1)) && 
22899             dt_low_ephemeral_space_p (tuning_deciding_expansion))
22900         {
22901             dprintf (GTC_LOG, ("Not enough space for all ephemeral generations with compaction"));
22902             should_expand = TRUE;
22903         }
22904     }
22905     else
22906     {
22907 #endif // BIT64
22908         should_compact = decide_on_compacting (condemned_gen_number, fragmentation, should_expand);
22909 #ifdef BIT64
22910     }
22911 #endif // BIT64
22912
22913 #ifdef FEATURE_LOH_COMPACTION
22914     loh_compacted_p = FALSE;
22915 #endif //FEATURE_LOH_COMPACTION
22916
22917     if (condemned_gen_number == max_generation)
22918     {
22919 #ifdef FEATURE_LOH_COMPACTION
22920         if (settings.loh_compaction)
22921         {
22922             if (plan_loh())
22923             {
22924                 should_compact = TRUE;
22925                 get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_loh_forced);
22926                 loh_compacted_p = TRUE;
22927             }
22928         }
22929         else
22930         {
22931             if ((heap_number == 0) && (loh_pinned_queue))
22932             {
22933                 loh_pinned_queue_decay--;
22934
22935                 if (!loh_pinned_queue_decay)
22936                 {
22937                     delete loh_pinned_queue;
22938                     loh_pinned_queue = 0;
22939                 }
22940             }
22941         }
22942
22943         if (!loh_compacted_p)
22944 #endif //FEATURE_LOH_COMPACTION
22945         {
22946             GCToEEInterface::DiagWalkLOHSurvivors(__this);
22947             sweep_large_objects();
22948         }
22949     }
22950     else
22951     {
22952         settings.loh_compaction = FALSE;
22953     }
22954
22955 #ifdef MULTIPLE_HEAPS
22956
22957     new_heap_segment = NULL;
22958
22959     if (should_compact && should_expand)
22960         gc_policy = policy_expand;
22961     else if (should_compact)
22962         gc_policy = policy_compact;
22963     else
22964         gc_policy = policy_sweep;
22965
22966     //vote for result of should_compact
22967     dprintf (3, ("Joining for compaction decision"));
22968     gc_t_join.join(this, gc_join_decide_on_compaction);
22969     if (gc_t_join.joined())
22970     {
22971         //safe place to delete large heap segments
22972         if (condemned_gen_number == max_generation)
22973         {
22974             for (int i = 0; i < n_heaps; i++)
22975             {
22976                 g_heaps [i]->rearrange_large_heap_segments ();
22977             }
22978         }
22979
22980         if (maxgen_size_inc_p && provisional_mode_triggered)
22981         {
22982             pm_trigger_full_gc = true;
22983             dprintf (GTC_LOG, ("in PM: maxgen size inc, doing a sweeping gen1 and trigger NGC2"));
22984         }
22985         else
22986         {
22987             settings.demotion = FALSE;
22988             int pol_max = policy_sweep;
22989 #ifdef GC_CONFIG_DRIVEN
22990             BOOL is_compaction_mandatory = FALSE;
22991 #endif //GC_CONFIG_DRIVEN
22992
22993             int i;
22994             for (i = 0; i < n_heaps; i++)
22995             {
22996                 if (pol_max < g_heaps[i]->gc_policy)
22997                     pol_max = policy_compact;
22998                 // set the demotion flag is any of the heap has demotion
22999                 if (g_heaps[i]->demotion_high >= g_heaps[i]->demotion_low)
23000                 {
23001                     (g_heaps[i]->get_gc_data_per_heap())->set_mechanism_bit (gc_demotion_bit);
23002                     settings.demotion = TRUE;
23003                 }
23004
23005 #ifdef GC_CONFIG_DRIVEN
23006                 if (!is_compaction_mandatory)
23007                 {
23008                     int compact_reason = (g_heaps[i]->get_gc_data_per_heap())->get_mechanism (gc_heap_compact);
23009                     if (compact_reason >= 0)
23010                     {
23011                         if (gc_heap_compact_reason_mandatory_p[compact_reason])
23012                             is_compaction_mandatory = TRUE;
23013                     }
23014                 }
23015 #endif //GC_CONFIG_DRIVEN
23016             }
23017
23018 #ifdef GC_CONFIG_DRIVEN
23019             if (!is_compaction_mandatory)
23020             {
23021                 // If compaction is not mandatory we can feel free to change it to a sweeping GC.
23022                 // Note that we may want to change this to only checking every so often instead of every single GC.
23023                 if (should_do_sweeping_gc (pol_max >= policy_compact))
23024                 {
23025                     pol_max = policy_sweep;
23026                 }
23027                 else
23028                 {
23029                     if (pol_max == policy_sweep)
23030                         pol_max = policy_compact;
23031                 }
23032             }
23033 #endif //GC_CONFIG_DRIVEN
23034
23035             for (i = 0; i < n_heaps; i++)
23036             {
23037                 if (pol_max > g_heaps[i]->gc_policy)
23038                     g_heaps[i]->gc_policy = pol_max;
23039                 //get the segment while we are serialized
23040                 if (g_heaps[i]->gc_policy == policy_expand)
23041                 {
23042                     g_heaps[i]->new_heap_segment =
23043                         g_heaps[i]->soh_get_segment_to_expand();
23044                     if (!g_heaps[i]->new_heap_segment)
23045                     {
23046                         set_expand_in_full_gc (condemned_gen_number);
23047                         //we are out of memory, cancel the expansion
23048                         g_heaps[i]->gc_policy = policy_compact;
23049                     }
23050                 }
23051             }
23052
23053             BOOL is_full_compacting_gc = FALSE;
23054
23055             if ((gc_policy >= policy_compact) && (condemned_gen_number == max_generation))
23056             {
23057                 full_gc_counts[gc_type_compacting]++;
23058                 is_full_compacting_gc = TRUE;
23059             }
23060
23061             for (i = 0; i < n_heaps; i++)
23062             {
23063                 //copy the card and brick tables
23064                 if (g_gc_card_table!= g_heaps[i]->card_table)
23065                 {
23066                     g_heaps[i]->copy_brick_card_table();
23067                 }
23068
23069                 if (is_full_compacting_gc)
23070                 {
23071                     g_heaps[i]->loh_alloc_since_cg = 0;
23072                 }
23073             }
23074         }
23075
23076         //start all threads on the roots.
23077         dprintf(3, ("Starting all gc threads after compaction decision"));
23078         gc_t_join.restart();
23079     }
23080
23081     //reset the local variable accordingly
23082     should_compact = (gc_policy >= policy_compact);
23083     should_expand  = (gc_policy >= policy_expand);
23084
23085 #else //MULTIPLE_HEAPS
23086
23087     //safe place to delete large heap segments
23088     if (condemned_gen_number == max_generation)
23089     {
23090         rearrange_large_heap_segments ();
23091     }
23092
23093     if (maxgen_size_inc_p && provisional_mode_triggered)
23094     {
23095         pm_trigger_full_gc = true;
23096         dprintf (GTC_LOG, ("in PM: maxgen size inc, doing a sweeping gen1 and trigger NGC2"));
23097     }
23098     else
23099     {
23100         settings.demotion = ((demotion_high >= demotion_low) ? TRUE : FALSE);
23101         if (settings.demotion)
23102             get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
23103
23104 #ifdef GC_CONFIG_DRIVEN
23105         BOOL is_compaction_mandatory = FALSE;
23106         int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
23107         if (compact_reason >= 0)
23108             is_compaction_mandatory = gc_heap_compact_reason_mandatory_p[compact_reason];
23109
23110         if (!is_compaction_mandatory)
23111         {
23112             if (should_do_sweeping_gc (should_compact))
23113                 should_compact = FALSE;
23114             else
23115                 should_compact = TRUE;
23116         }
23117 #endif //GC_CONFIG_DRIVEN
23118
23119         if (should_compact && (condemned_gen_number == max_generation))
23120         {
23121             full_gc_counts[gc_type_compacting]++;
23122             loh_alloc_since_cg = 0;
23123         }
23124     }
23125 #endif //MULTIPLE_HEAPS
23126
23127     if (!pm_trigger_full_gc && pm_stress_on && provisional_mode_triggered)
23128     {
23129         if ((settings.condemned_generation == (max_generation - 1)) &&
23130             ((settings.gc_index % 5) == 0))
23131         {
23132             pm_trigger_full_gc = true;
23133         }
23134     }
23135
23136     if (settings.condemned_generation == (max_generation - 1))
23137     {
23138         if (provisional_mode_triggered)
23139         {
23140             if (should_expand)
23141             {
23142                 should_expand = FALSE;
23143                 dprintf (GTC_LOG, ("h%d in PM cannot expand", heap_number));
23144             }
23145         }
23146
23147         if (pm_trigger_full_gc)
23148         {
23149             should_compact = FALSE;
23150             dprintf (GTC_LOG, ("h%d PM doing sweeping", heap_number));
23151         }
23152     }
23153
23154     if (should_compact)
23155     {
23156         dprintf (2,( "**** Doing Compacting GC ****"));
23157
23158         if (should_expand)
23159         {
23160 #ifndef MULTIPLE_HEAPS
23161             heap_segment* new_heap_segment = soh_get_segment_to_expand();
23162 #endif //!MULTIPLE_HEAPS
23163             if (new_heap_segment)
23164             {
23165                 consing_gen = expand_heap(condemned_gen_number,
23166                                           consing_gen,
23167                                           new_heap_segment);
23168             }
23169
23170             // If we couldn't get a new segment, or we were able to 
23171             // reserve one but no space to commit, we couldn't
23172             // expand heap.
23173             if (ephemeral_heap_segment != new_heap_segment)
23174             {
23175                 set_expand_in_full_gc (condemned_gen_number);
23176                 should_expand = FALSE;
23177             }
23178         }
23179         generation_allocation_limit (condemned_gen1) =
23180             generation_allocation_pointer (condemned_gen1);
23181         if ((condemned_gen_number < max_generation))
23182         {
23183             generation_allocator (older_gen)->commit_alloc_list_changes();
23184
23185             // Fix the allocation area of the older generation
23186             fix_older_allocation_area (older_gen);
23187         }
23188         assert (generation_allocation_segment (consing_gen) ==
23189                 ephemeral_heap_segment);
23190
23191         GCToEEInterface::DiagWalkSurvivors(__this);
23192
23193         relocate_phase (condemned_gen_number, first_condemned_address);
23194         compact_phase (condemned_gen_number, first_condemned_address,
23195                        (!settings.demotion && settings.promotion));
23196         fix_generation_bounds (condemned_gen_number, consing_gen);
23197         assert (generation_allocation_limit (youngest_generation) ==
23198                 generation_allocation_pointer (youngest_generation));
23199         if (condemned_gen_number >= (max_generation -1))
23200         {
23201 #ifdef MULTIPLE_HEAPS
23202             // this needs be serialized just because we have one
23203             // segment_standby_list/seg_table for all heaps. We should make it at least
23204             // so that when hoarding is not on we don't need this join because
23205             // decommitting memory can take a long time.
23206             //must serialize on deleting segments
23207             gc_t_join.join(this, gc_join_rearrange_segs_compaction);
23208             if (gc_t_join.joined())
23209             {
23210                 for (int i = 0; i < n_heaps; i++)
23211                 {
23212                     g_heaps[i]->rearrange_heap_segments(TRUE);
23213                 }
23214                 gc_t_join.restart();
23215             }
23216 #else
23217             rearrange_heap_segments(TRUE);
23218 #endif //MULTIPLE_HEAPS
23219
23220             if (should_expand)
23221             {
23222                 //fix the start_segment for the ephemeral generations
23223                 for (int i = 0; i < max_generation; i++)
23224                 {
23225                     generation* gen = generation_of (i);
23226                     generation_start_segment (gen) = ephemeral_heap_segment;
23227                     generation_allocation_segment (gen) = ephemeral_heap_segment;
23228                 }
23229             }
23230         }
23231
23232         {
23233 #ifdef FEATURE_PREMORTEM_FINALIZATION
23234             finalize_queue->UpdatePromotedGenerations (condemned_gen_number,
23235                                                        (!settings.demotion && settings.promotion));
23236 #endif // FEATURE_PREMORTEM_FINALIZATION
23237
23238 #ifdef MULTIPLE_HEAPS
23239             dprintf(3, ("Joining after end of compaction"));
23240             gc_t_join.join(this, gc_join_adjust_handle_age_compact);
23241             if (gc_t_join.joined())
23242 #endif //MULTIPLE_HEAPS
23243             {
23244 #ifdef MULTIPLE_HEAPS
23245                 //join all threads to make sure they are synchronized
23246                 dprintf(3, ("Restarting after Promotion granted"));
23247                 gc_t_join.restart();
23248 #endif //MULTIPLE_HEAPS
23249             }
23250
23251             ScanContext sc;
23252             sc.thread_number = heap_number;
23253             sc.promotion = FALSE;
23254             sc.concurrent = FALSE;
23255             // new generations bounds are set can call this guy
23256             if (settings.promotion && !settings.demotion)
23257             {
23258                 dprintf (2, ("Promoting EE roots for gen %d",
23259                              condemned_gen_number));
23260                 GCScan::GcPromotionsGranted(condemned_gen_number,
23261                                                 max_generation, &sc);
23262             }
23263             else if (settings.demotion)
23264             {
23265                 dprintf (2, ("Demoting EE roots for gen %d",
23266                              condemned_gen_number));
23267                 GCScan::GcDemote (condemned_gen_number, max_generation, &sc);
23268             }
23269         }
23270
23271         {
23272             gen0_big_free_spaces = 0;
23273
23274             reset_pinned_queue_bos();
23275             unsigned int  gen_number = min (max_generation, 1 + condemned_gen_number);
23276             generation*  gen = generation_of (gen_number);
23277             uint8_t*  low = generation_allocation_start (generation_of (gen_number-1));
23278             uint8_t*  high =  heap_segment_allocated (ephemeral_heap_segment);
23279             
23280             while (!pinned_plug_que_empty_p())
23281             {
23282                 mark*  m = pinned_plug_of (deque_pinned_plug());
23283                 size_t len = pinned_len (m);
23284                 uint8_t*  arr = (pinned_plug (m) - len);
23285                 dprintf(3,("free [%Ix %Ix[ pin",
23286                             (size_t)arr, (size_t)arr + len));
23287                 if (len != 0)
23288                 {
23289                     assert (len >= Align (min_obj_size));
23290                     make_unused_array (arr, len);
23291                     // fix fully contained bricks + first one
23292                     // if the array goes beyond the first brick
23293                     size_t start_brick = brick_of (arr);
23294                     size_t end_brick = brick_of (arr + len);
23295                     if (end_brick != start_brick)
23296                     {
23297                         dprintf (3,
23298                                     ("Fixing bricks [%Ix, %Ix[ to point to unused array %Ix",
23299                                     start_brick, end_brick, (size_t)arr));
23300                         set_brick (start_brick,
23301                                     arr - brick_address (start_brick));
23302                         size_t brick = start_brick+1;
23303                         while (brick < end_brick)
23304                         {
23305                             set_brick (brick, start_brick - brick);
23306                             brick++;
23307                         }
23308                     }
23309
23310                     //when we take an old segment to make the new
23311                     //ephemeral segment. we can have a bunch of
23312                     //pinned plugs out of order going to the new ephemeral seg
23313                     //and then the next plugs go back to max_generation
23314                     if ((heap_segment_mem (ephemeral_heap_segment) <= arr) &&
23315                         (heap_segment_reserved (ephemeral_heap_segment) > arr))
23316                     {
23317
23318                         while ((low <= arr) && (high > arr))
23319                         {
23320                             gen_number--;
23321                             assert ((gen_number >= 1) || (demotion_low != MAX_PTR) ||
23322                                     settings.demotion || !settings.promotion);
23323                             dprintf (3, ("new free list generation %d", gen_number));
23324
23325                             gen = generation_of (gen_number);
23326                             if (gen_number >= 1)
23327                                 low = generation_allocation_start (generation_of (gen_number-1));
23328                             else
23329                                 low = high;
23330                         }
23331                     }
23332                     else
23333                     {
23334                         dprintf (3, ("new free list generation %d", max_generation));
23335                         gen_number = max_generation;
23336                         gen = generation_of (gen_number);
23337                     }
23338
23339                     dprintf(3,("threading it into generation %d", gen_number));
23340                     thread_gap (arr, len, gen);
23341                     add_gen_free (gen_number, len);
23342                 }
23343             }
23344         }
23345
23346 #ifdef _DEBUG
23347         for (int x = 0; x <= max_generation; x++)
23348         {
23349             assert (generation_allocation_start (generation_of (x)));
23350         }
23351 #endif //_DEBUG
23352
23353         if (!settings.demotion && settings.promotion)
23354         {
23355             //clear card for generation 1. generation 0 is empty
23356             clear_card_for_addresses (
23357                 generation_allocation_start (generation_of (1)),
23358                 generation_allocation_start (generation_of (0)));
23359         }
23360         if (settings.promotion && !settings.demotion)
23361         {
23362             uint8_t* start = generation_allocation_start (youngest_generation);
23363             MAYBE_UNUSED_VAR(start);
23364             assert (heap_segment_allocated (ephemeral_heap_segment) ==
23365                     (start + Align (size (start))));
23366         }
23367     }
23368     else
23369     {
23370         //force promotion for sweep
23371         settings.promotion = TRUE;
23372         settings.compaction = FALSE;
23373
23374         ScanContext sc;
23375         sc.thread_number = heap_number;
23376         sc.promotion = FALSE;
23377         sc.concurrent = FALSE;
23378
23379         dprintf (2, ("**** Doing Mark and Sweep GC****"));
23380
23381         if ((condemned_gen_number < max_generation))
23382         {
23383             generation_allocator (older_gen)->copy_from_alloc_list (r_free_list);
23384             generation_free_list_space (older_gen) = r_free_list_space;
23385             generation_free_obj_space (older_gen) = r_free_obj_space;
23386             generation_free_list_allocated (older_gen) = r_older_gen_free_list_allocated;
23387             generation_end_seg_allocated (older_gen) = r_older_gen_end_seg_allocated;
23388             generation_condemned_allocated (older_gen) = r_older_gen_condemned_allocated;
23389             generation_allocation_limit (older_gen) = r_allocation_limit;
23390             generation_allocation_pointer (older_gen) = r_allocation_pointer;
23391             generation_allocation_context_start_region (older_gen) = r_allocation_start_region;
23392             generation_allocation_segment (older_gen) = r_allocation_segment;
23393         }
23394
23395         if ((condemned_gen_number < max_generation))
23396         {
23397             // Fix the allocation area of the older generation
23398             fix_older_allocation_area (older_gen);
23399         }
23400
23401         GCToEEInterface::DiagWalkSurvivors(__this);
23402
23403         gen0_big_free_spaces = 0;
23404         make_free_lists (condemned_gen_number);
23405         recover_saved_pinned_info();
23406
23407 #ifdef FEATURE_PREMORTEM_FINALIZATION
23408         finalize_queue->UpdatePromotedGenerations (condemned_gen_number, TRUE);
23409 #endif // FEATURE_PREMORTEM_FINALIZATION
23410 // MTHTS: leave single thread for HT processing on plan_phase
23411 #ifdef MULTIPLE_HEAPS
23412         dprintf(3, ("Joining after end of sweep"));
23413         gc_t_join.join(this, gc_join_adjust_handle_age_sweep);
23414         if (gc_t_join.joined())
23415 #endif //MULTIPLE_HEAPS
23416         {
23417             GCScan::GcPromotionsGranted(condemned_gen_number,
23418                                             max_generation, &sc);
23419             if (condemned_gen_number >= (max_generation -1))
23420             {
23421 #ifdef MULTIPLE_HEAPS
23422                 for (int i = 0; i < n_heaps; i++)
23423                 {
23424                     g_heaps[i]->rearrange_heap_segments(FALSE);
23425                 }
23426 #else
23427                 rearrange_heap_segments(FALSE);
23428 #endif //MULTIPLE_HEAPS
23429             }
23430
23431 #ifdef MULTIPLE_HEAPS
23432             //join all threads to make sure they are synchronized
23433             dprintf(3, ("Restarting after Promotion granted"));
23434             gc_t_join.restart();
23435 #endif //MULTIPLE_HEAPS
23436         }
23437
23438 #ifdef _DEBUG
23439         for (int x = 0; x <= max_generation; x++)
23440         {
23441             assert (generation_allocation_start (generation_of (x)));
23442         }
23443 #endif //_DEBUG
23444
23445         //clear card for generation 1. generation 0 is empty
23446         clear_card_for_addresses (
23447             generation_allocation_start (generation_of (1)),
23448             generation_allocation_start (generation_of (0)));
23449         assert ((heap_segment_allocated (ephemeral_heap_segment) ==
23450                  (generation_allocation_start (youngest_generation) +
23451                   Align (min_obj_size))));
23452     }
23453
23454     //verify_partial();
23455 }
23456 #ifdef _PREFAST_
23457 #pragma warning(pop)
23458 #endif //_PREFAST_
23459
23460
23461 /*****************************
23462 Called after compact phase to fix all generation gaps
23463 ********************************/
23464 void gc_heap::fix_generation_bounds (int condemned_gen_number,
23465                                      generation* consing_gen)
23466 {
23467     UNREFERENCED_PARAMETER(consing_gen);
23468
23469     assert (generation_allocation_segment (consing_gen) ==
23470             ephemeral_heap_segment);
23471
23472     //assign the planned allocation start to the generation
23473     int gen_number = condemned_gen_number;
23474     int bottom_gen = 0;
23475
23476     while (gen_number >= bottom_gen)
23477     {
23478         generation*  gen = generation_of (gen_number);
23479         dprintf(3,("Fixing generation pointers for %Ix", gen_number));
23480         if ((gen_number < max_generation) && ephemeral_promotion)
23481         {
23482             make_unused_array (saved_ephemeral_plan_start[gen_number], 
23483                                saved_ephemeral_plan_start_size[gen_number]);
23484         }
23485         reset_allocation_pointers (gen, generation_plan_allocation_start (gen));
23486         make_unused_array (generation_allocation_start (gen), generation_plan_allocation_start_size (gen));
23487         dprintf(3,(" start %Ix", (size_t)generation_allocation_start (gen)));
23488         gen_number--;
23489     }
23490 #ifdef MULTIPLE_HEAPS
23491     if (ephemeral_promotion)
23492     {
23493         //we are creating a generation fault. set the cards.
23494         // and we are only doing this for multiple heaps because in the single heap scenario the 
23495         // new ephemeral generations will be empty and there'll be no need to set cards for the
23496         // old ephemeral generations that got promoted into max_generation.
23497         ptrdiff_t delta = 0;
23498 #ifdef SEG_MAPPING_TABLE
23499         heap_segment* old_ephemeral_seg = seg_mapping_table_segment_of (saved_ephemeral_plan_start[max_generation-1]);
23500 #else //SEG_MAPPING_TABLE
23501         heap_segment* old_ephemeral_seg = segment_of (saved_ephemeral_plan_start[max_generation-1], delta);
23502 #endif //SEG_MAPPING_TABLE
23503
23504         assert (in_range_for_segment (saved_ephemeral_plan_start[max_generation-1], old_ephemeral_seg));
23505         size_t end_card = card_of (align_on_card (heap_segment_plan_allocated (old_ephemeral_seg)));
23506         size_t card = card_of (saved_ephemeral_plan_start[max_generation-1]);
23507         while (card != end_card)
23508         {
23509             set_card (card);
23510             card++;
23511         }
23512     }
23513 #endif //MULTIPLE_HEAPS
23514     {
23515         alloc_allocated = heap_segment_plan_allocated(ephemeral_heap_segment);
23516         //reset the allocated size
23517         uint8_t* start = generation_allocation_start (youngest_generation);
23518         MAYBE_UNUSED_VAR(start);
23519         if (settings.promotion && !settings.demotion)
23520         {
23521             assert ((start + Align (size (start))) ==
23522                     heap_segment_plan_allocated(ephemeral_heap_segment));
23523         }
23524
23525         heap_segment_allocated(ephemeral_heap_segment)=
23526             heap_segment_plan_allocated(ephemeral_heap_segment);
23527     }
23528 }
23529
23530 uint8_t* gc_heap::generation_limit (int gen_number)
23531 {
23532     if (settings.promotion)
23533     {
23534         if (gen_number <= 1)
23535             return heap_segment_reserved (ephemeral_heap_segment);
23536         else
23537             return generation_allocation_start (generation_of ((gen_number - 2)));
23538     }
23539     else
23540     {
23541         if (gen_number <= 0)
23542             return heap_segment_reserved (ephemeral_heap_segment);
23543         else
23544             return generation_allocation_start (generation_of ((gen_number - 1)));
23545     }
23546 }
23547
23548 BOOL gc_heap::ensure_gap_allocation (int condemned_gen_number)
23549 {
23550     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
23551     size_t size = Align (min_obj_size)*(condemned_gen_number+1);
23552     assert ((start + size) <=
23553             heap_segment_reserved (ephemeral_heap_segment));
23554     if ((start + size) >
23555         heap_segment_committed (ephemeral_heap_segment))
23556     {
23557         if (!grow_heap_segment (ephemeral_heap_segment, start + size))
23558         {
23559             return FALSE;
23560         }
23561     }
23562     return TRUE;
23563 }
23564
23565 uint8_t* gc_heap::allocate_at_end (size_t size)
23566 {
23567     uint8_t* start = heap_segment_allocated (ephemeral_heap_segment);
23568     size = Align (size);
23569     uint8_t* result = start;
23570     // only called to allocate a min obj so can't overflow here.
23571     assert ((start + size) <=
23572             heap_segment_reserved (ephemeral_heap_segment));
23573     //ensure_gap_allocation took care of it
23574     assert ((start + size) <=
23575             heap_segment_committed (ephemeral_heap_segment));
23576     heap_segment_allocated (ephemeral_heap_segment) += size;
23577     return result;
23578 }
23579
23580
23581 void gc_heap::make_free_lists (int condemned_gen_number)
23582 {
23583 #ifdef TIME_GC
23584     unsigned start;
23585     unsigned finish;
23586     start = GetCycleCount32();
23587 #endif //TIME_GC
23588
23589     //Promotion has to happen in sweep case.
23590     assert (settings.promotion);
23591
23592     generation* condemned_gen = generation_of (condemned_gen_number);
23593     uint8_t* start_address = generation_allocation_start (condemned_gen);
23594
23595     size_t  current_brick = brick_of (start_address);
23596     heap_segment* current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
23597
23598     PREFIX_ASSUME(current_heap_segment != NULL);
23599
23600     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
23601     size_t  end_brick = brick_of (end_address-1);
23602     make_free_args args;
23603     args.free_list_gen_number = min (max_generation, 1 + condemned_gen_number);
23604     args.current_gen_limit = (((condemned_gen_number == max_generation)) ?
23605                               MAX_PTR :
23606                               (generation_limit (args.free_list_gen_number)));
23607     args.free_list_gen = generation_of (args.free_list_gen_number);
23608     args.highest_plug = 0;
23609
23610     if ((start_address < end_address) ||
23611         (condemned_gen_number == max_generation))
23612     {
23613         while (1)
23614         {
23615             if ((current_brick > end_brick))
23616             {
23617                 if (args.current_gen_limit == MAX_PTR)
23618                 {
23619                     //We had an empty segment
23620                     //need to allocate the generation start
23621
23622                     generation* gen = generation_of (max_generation);
23623
23624                     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
23625
23626                     PREFIX_ASSUME(start_seg != NULL);
23627
23628                     uint8_t* gap = heap_segment_mem (start_seg);
23629
23630                     generation_allocation_start (gen) = gap;
23631                     heap_segment_allocated (start_seg) = gap + Align (min_obj_size);
23632                     make_unused_array (gap, Align (min_obj_size));
23633                     reset_allocation_pointers (gen, gap);
23634                     dprintf (3, ("Start segment empty, fixing generation start of %d to: %Ix",
23635                                  max_generation, (size_t)gap));
23636                     args.current_gen_limit = generation_limit (args.free_list_gen_number);
23637                 }
23638                 if (heap_segment_next_rw (current_heap_segment))
23639                 {
23640                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
23641                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
23642                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
23643
23644                     continue;
23645                 }
23646                 else
23647                 {
23648                     break;
23649                 }
23650             }
23651             {
23652                 int brick_entry =  brick_table [ current_brick ];
23653                 if ((brick_entry >= 0))
23654                 {
23655                     make_free_list_in_brick (brick_address (current_brick) + brick_entry-1, &args);
23656                     dprintf(3,("Fixing brick entry %Ix to %Ix",
23657                                current_brick, (size_t)args.highest_plug));
23658                     set_brick (current_brick,
23659                                (args.highest_plug - brick_address (current_brick)));
23660                 }
23661                 else
23662                 {
23663                     if ((brick_entry > -32768))
23664                     {
23665
23666 #ifdef _DEBUG
23667                         ptrdiff_t offset = brick_of (args.highest_plug) - current_brick;
23668                         if ((brick_entry != -32767) && (! ((offset == brick_entry))))
23669                         {
23670                             assert ((brick_entry == -1));
23671                         }
23672 #endif //_DEBUG
23673                         //init to -1 for faster find_first_object
23674                         set_brick (current_brick, -1);
23675                     }
23676                 }
23677             }
23678             current_brick++;
23679         }
23680     }
23681     {
23682         int bottom_gen = 0;
23683         args.free_list_gen_number--;
23684         while (args.free_list_gen_number >= bottom_gen)
23685         {
23686             uint8_t*  gap = 0;
23687             generation* gen2 = generation_of (args.free_list_gen_number);
23688             gap = allocate_at_end (Align(min_obj_size));
23689             generation_allocation_start (gen2) = gap;
23690             reset_allocation_pointers (gen2, gap);
23691             dprintf(3,("Fixing generation start of %d to: %Ix",
23692                        args.free_list_gen_number, (size_t)gap));
23693             PREFIX_ASSUME(gap != NULL);
23694             make_unused_array (gap, Align (min_obj_size));
23695
23696             args.free_list_gen_number--;
23697         }
23698
23699         //reset the allocated size
23700         uint8_t* start2 = generation_allocation_start (youngest_generation);
23701         alloc_allocated = start2 + Align (size (start2));
23702     }
23703
23704 #ifdef TIME_GC
23705     finish = GetCycleCount32();
23706     sweep_time = finish - start;
23707 #endif //TIME_GC
23708 }
23709
23710 void gc_heap::make_free_list_in_brick (uint8_t* tree, make_free_args* args)
23711 {
23712     assert ((tree != NULL));
23713     {
23714         int  right_node = node_right_child (tree);
23715         int left_node = node_left_child (tree);
23716         args->highest_plug = 0;
23717         if (! (0 == tree))
23718         {
23719             if (! (0 == left_node))
23720             {
23721                 make_free_list_in_brick (tree + left_node, args);
23722
23723             }
23724             {
23725                 uint8_t*  plug = tree;
23726                 size_t  gap_size = node_gap_size (tree);
23727                 uint8_t*  gap = (plug - gap_size);
23728                 dprintf (3,("Making free list %Ix len %d in %d",
23729                 //dprintf (3,("F: %Ix len %Ix in %d",
23730                         (size_t)gap, gap_size, args->free_list_gen_number));
23731                 args->highest_plug = tree;
23732 #ifdef SHORT_PLUGS
23733                 if (is_plug_padded (plug))
23734                 {
23735                     dprintf (3, ("%Ix padded", plug));
23736                     clear_plug_padded (plug);
23737                 }
23738 #endif //SHORT_PLUGS
23739             gen_crossing:
23740                 {
23741                     if ((args->current_gen_limit == MAX_PTR) ||
23742                         ((plug >= args->current_gen_limit) &&
23743                          ephemeral_pointer_p (plug)))
23744                     {
23745                         dprintf(3,(" Crossing Generation boundary at %Ix",
23746                                (size_t)args->current_gen_limit));
23747                         if (!(args->current_gen_limit == MAX_PTR))
23748                         {
23749                             args->free_list_gen_number--;
23750                             args->free_list_gen = generation_of (args->free_list_gen_number);
23751                         }
23752                         dprintf(3,( " Fixing generation start of %d to: %Ix",
23753                                 args->free_list_gen_number, (size_t)gap));
23754
23755                         reset_allocation_pointers (args->free_list_gen, gap);
23756                         args->current_gen_limit = generation_limit (args->free_list_gen_number);
23757
23758                         if ((gap_size >= (2*Align (min_obj_size))))
23759                         {
23760                             dprintf(3,(" Splitting the gap in two %Id left",
23761                                    gap_size));
23762                             make_unused_array (gap, Align(min_obj_size));
23763                             gap_size = (gap_size - Align(min_obj_size));
23764                             gap = (gap + Align(min_obj_size));
23765                         }
23766                         else
23767                         {
23768                             make_unused_array (gap, gap_size);
23769                             gap_size = 0;
23770                         }
23771                         goto gen_crossing;
23772                     }
23773                 }
23774
23775                 thread_gap (gap, gap_size, args->free_list_gen);
23776                 add_gen_free (args->free_list_gen->gen_num, gap_size);
23777             }
23778             if (! (0 == right_node))
23779             {
23780                 make_free_list_in_brick (tree + right_node, args);
23781             }
23782         }
23783     }
23784 }
23785
23786 void gc_heap::thread_gap (uint8_t* gap_start, size_t size, generation*  gen)
23787 {
23788     assert (generation_allocation_start (gen));
23789     if ((size > 0))
23790     {
23791         if ((gen->gen_num == 0) && (size > CLR_SIZE))
23792         {
23793             gen0_big_free_spaces += size;
23794         }
23795
23796         assert ((heap_segment_rw (generation_start_segment (gen))!=
23797                  ephemeral_heap_segment) ||
23798                 (gap_start > generation_allocation_start (gen)));
23799         // The beginning of a segment gap is not aligned
23800         assert (size >= Align (min_obj_size));
23801         make_unused_array (gap_start, size, 
23802                           (!settings.concurrent && (gen != youngest_generation)),
23803                           (gen->gen_num == max_generation));
23804         dprintf (3, ("fr: [%Ix, %Ix[", (size_t)gap_start, (size_t)gap_start+size));
23805
23806         if ((size >= min_free_list))
23807         {
23808             generation_free_list_space (gen) += size;
23809             generation_allocator (gen)->thread_item (gap_start, size);
23810         }
23811         else
23812         {
23813             generation_free_obj_space (gen) += size;
23814         }
23815     }
23816 }
23817
23818 void gc_heap::loh_thread_gap_front (uint8_t* gap_start, size_t size, generation*  gen)
23819 {
23820     assert (generation_allocation_start (gen));
23821     if (size >= min_free_list)
23822     {
23823         generation_free_list_space (gen) += size;
23824         generation_allocator (gen)->thread_item_front (gap_start, size);
23825     }
23826 }
23827
23828 void gc_heap::make_unused_array (uint8_t* x, size_t size, BOOL clearp, BOOL resetp)
23829 {
23830     dprintf (3, ("Making unused array [%Ix, %Ix[",
23831         (size_t)x, (size_t)(x+size)));
23832     assert (size >= Align (min_obj_size));
23833
23834 //#if defined (VERIFY_HEAP) && defined (BACKGROUND_GC)
23835 //    check_batch_mark_array_bits (x, x+size);
23836 //#endif //VERIFY_HEAP && BACKGROUND_GC
23837
23838     if (resetp)
23839         reset_memory (x, size);
23840
23841     ((CObjectHeader*)x)->SetFree(size);
23842
23843 #ifdef BIT64
23844
23845 #if BIGENDIAN
23846 #error "This won't work on big endian platforms"
23847 #endif
23848
23849     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23850
23851     if (size_as_object < size)
23852     {
23853         //
23854         // If the size is more than 4GB, we need to create multiple objects because of
23855         // the Array::m_NumComponents is uint32_t and the high 32 bits of unused array
23856         // size is ignored in regular object size computation.
23857         //
23858         uint8_t * tmp = x + size_as_object;
23859         size_t remaining_size = size - size_as_object;
23860
23861         while (remaining_size > UINT32_MAX)
23862         {
23863             // Make sure that there will be at least Align(min_obj_size) left
23864             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
23865                 - Align (min_obj_size, get_alignment_constant (FALSE));
23866
23867             ((CObjectHeader*)tmp)->SetFree(current_size);
23868
23869             remaining_size -= current_size;
23870             tmp += current_size;
23871         }
23872
23873         ((CObjectHeader*)tmp)->SetFree(remaining_size);
23874     }
23875 #endif
23876
23877     if (clearp)
23878         clear_card_for_addresses (x, x + Align(size));
23879 }
23880
23881 // Clear memory set by make_unused_array.
23882 void gc_heap::clear_unused_array (uint8_t* x, size_t size)
23883 {
23884     // Also clear the sync block
23885     *(((PTR_PTR)x)-1) = 0;
23886
23887     ((CObjectHeader*)x)->UnsetFree();
23888
23889 #ifdef BIT64
23890
23891 #if BIGENDIAN
23892 #error "This won't work on big endian platforms"
23893 #endif
23894
23895     // The memory could have been cleared in the meantime. We have to mirror the algorithm
23896     // from make_unused_array since we cannot depend on the object sizes in memory.
23897     size_t size_as_object = (uint32_t)(size - free_object_base_size) + free_object_base_size;
23898
23899     if (size_as_object < size)
23900     {
23901         uint8_t * tmp = x + size_as_object;
23902         size_t remaining_size = size - size_as_object;
23903
23904         while (remaining_size > UINT32_MAX)
23905         {
23906             size_t current_size = UINT32_MAX - get_alignment_constant (FALSE) 
23907                 - Align (min_obj_size, get_alignment_constant (FALSE));
23908
23909             ((CObjectHeader*)tmp)->UnsetFree();
23910
23911             remaining_size -= current_size;
23912             tmp += current_size;
23913         }
23914
23915         ((CObjectHeader*)tmp)->UnsetFree();
23916     }
23917 #else
23918     UNREFERENCED_PARAMETER(size);
23919 #endif
23920 }
23921
23922 inline
23923 uint8_t* tree_search (uint8_t* tree, uint8_t* old_address)
23924 {
23925     uint8_t* candidate = 0;
23926     int cn;
23927     while (1)
23928     {
23929         if (tree < old_address)
23930         {
23931             if ((cn = node_right_child (tree)) != 0)
23932             {
23933                 assert (candidate < tree);
23934                 candidate = tree;
23935                 tree = tree + cn;
23936                 Prefetch (tree - 8);
23937                 continue;
23938             }
23939             else
23940                 break;
23941         }
23942         else if (tree > old_address)
23943         {
23944             if ((cn = node_left_child (tree)) != 0)
23945             {
23946                 tree = tree + cn;
23947                 Prefetch (tree - 8);
23948                 continue;
23949             }
23950             else
23951                 break;
23952         } else
23953             break;
23954     }
23955     if (tree <= old_address)
23956         return tree;
23957     else if (candidate)
23958         return candidate;
23959     else
23960         return tree;
23961 }
23962
23963 #ifdef FEATURE_BASICFREEZE
23964 bool gc_heap::frozen_object_p (Object* obj)
23965 {
23966 #ifdef MULTIPLE_HEAPS
23967 #ifdef SEG_MAPPING_TABLE
23968     heap_segment* pSegment = seg_mapping_table_segment_of((uint8_t*)obj);
23969 #else
23970     ptrdiff_t delta = 0;
23971     heap_segment* pSegment = segment_of ((uint8_t*)obj, delta);
23972 #endif
23973 #else //MULTIPLE_HEAPS
23974     heap_segment* pSegment = gc_heap::find_segment ((uint8_t*)obj, FALSE);
23975     _ASSERTE(pSegment);
23976 #endif //MULTIPLE_HEAPS
23977
23978     return heap_segment_read_only_p(pSegment);
23979 }
23980 #endif // FEATURE_BASICFREEZE
23981
23982 #ifdef FEATURE_REDHAWK
23983 // TODO: this was added on RH, we have not done perf runs to see if this is the right
23984 // thing to do for other versions of the CLR.
23985 inline
23986 #endif // FEATURE_REDHAWK
23987 void gc_heap::relocate_address (uint8_t** pold_address THREAD_NUMBER_DCL)
23988 {
23989     uint8_t* old_address = *pold_address;
23990     if (!((old_address >= gc_low) && (old_address < gc_high)))
23991 #ifdef MULTIPLE_HEAPS
23992     {
23993         UNREFERENCED_PARAMETER(thread);
23994         if (old_address == 0)
23995             return;
23996         gc_heap* hp = heap_of (old_address);
23997         if ((hp == this) ||
23998             !((old_address >= hp->gc_low) && (old_address < hp->gc_high)))
23999             return;
24000     }
24001 #else //MULTIPLE_HEAPS
24002         return ;
24003 #endif //MULTIPLE_HEAPS
24004     // delta translates old_address into address_gc (old_address);
24005     size_t  brick = brick_of (old_address);
24006     int    brick_entry =  brick_table [ brick ];
24007     uint8_t*  new_address = old_address;
24008     if (! ((brick_entry == 0)))
24009     {
24010     retry:
24011         {
24012             while (brick_entry < 0)
24013             {
24014                 brick = (brick + brick_entry);
24015                 brick_entry =  brick_table [ brick ];
24016             }
24017             uint8_t* old_loc = old_address;
24018
24019             uint8_t* node = tree_search ((brick_address (brick) + brick_entry-1),
24020                                       old_loc);
24021             if ((node <= old_loc))
24022                 new_address = (old_address + node_relocation_distance (node));
24023             else
24024             {
24025                 if (node_left_p (node))
24026                 {
24027                     dprintf(3,(" L: %Ix", (size_t)node));
24028                     new_address = (old_address +
24029                                    (node_relocation_distance (node) +
24030                                     node_gap_size (node)));
24031                 }
24032                 else
24033                 {
24034                     brick = brick - 1;
24035                     brick_entry =  brick_table [ brick ];
24036                     goto retry;
24037                 }
24038             }
24039         }
24040
24041         *pold_address = new_address;
24042         return;
24043     }
24044
24045 #ifdef FEATURE_LOH_COMPACTION
24046     if (loh_compacted_p
24047 #ifdef FEATURE_BASICFREEZE
24048         && !frozen_object_p((Object*)old_address)
24049 #endif // FEATURE_BASICFREEZE
24050         )
24051     {
24052         *pold_address = old_address + loh_node_relocation_distance (old_address);
24053     }
24054     else
24055 #endif //FEATURE_LOH_COMPACTION
24056     {
24057         *pold_address = new_address;
24058     }
24059 }
24060
24061 inline void 
24062 gc_heap::check_class_object_demotion (uint8_t* obj)
24063 {
24064 #ifdef COLLECTIBLE_CLASS
24065     if (is_collectible(obj))
24066     {
24067         check_class_object_demotion_internal (obj);
24068     }
24069 #else
24070     UNREFERENCED_PARAMETER(obj);
24071 #endif //COLLECTIBLE_CLASS
24072 }
24073
24074 #ifdef COLLECTIBLE_CLASS
24075 NOINLINE void 
24076 gc_heap::check_class_object_demotion_internal (uint8_t* obj)
24077 {
24078     if (settings.demotion)
24079     {
24080 #ifdef MULTIPLE_HEAPS
24081         // We set the card without checking the demotion range 'cause at this point
24082         // the handle that points to the loader allocator object may or may not have
24083         // been relocated by other GC threads. 
24084         set_card (card_of (obj));
24085 #else
24086         THREAD_FROM_HEAP;
24087         uint8_t* class_obj = get_class_object (obj);
24088         dprintf (3, ("%Ix: got classobj %Ix", obj, class_obj));
24089         uint8_t* temp_class_obj = class_obj;
24090         uint8_t** temp = &temp_class_obj;
24091         relocate_address (temp THREAD_NUMBER_ARG);
24092
24093         check_demotion_helper (temp, obj);
24094 #endif //MULTIPLE_HEAPS
24095     }
24096 }
24097
24098 #endif //COLLECTIBLE_CLASS
24099
24100 inline void
24101 gc_heap::check_demotion_helper (uint8_t** pval, uint8_t* parent_obj)
24102 {
24103     // detect if we are demoting an object
24104     if ((*pval < demotion_high) &&
24105         (*pval >= demotion_low))
24106     {
24107         dprintf(3, ("setting card %Ix:%Ix",
24108                     card_of((uint8_t*)pval),
24109                     (size_t)pval));
24110
24111         set_card (card_of (parent_obj));
24112     }
24113 #ifdef MULTIPLE_HEAPS
24114     else if (settings.demotion)
24115     {
24116         dprintf (4, ("Demotion active, computing heap_of object"));
24117         gc_heap* hp = heap_of (*pval);
24118         if ((*pval < hp->demotion_high) &&
24119             (*pval >= hp->demotion_low))
24120         {
24121             dprintf(3, ("setting card %Ix:%Ix",
24122                         card_of((uint8_t*)pval),
24123                         (size_t)pval));
24124
24125             set_card (card_of (parent_obj));
24126         }
24127     }
24128 #endif //MULTIPLE_HEAPS
24129 }
24130
24131 inline void
24132 gc_heap::reloc_survivor_helper (uint8_t** pval)
24133 {
24134     THREAD_FROM_HEAP;
24135     relocate_address (pval THREAD_NUMBER_ARG);
24136
24137     check_demotion_helper (pval, (uint8_t*)pval);
24138 }
24139
24140 inline void
24141 gc_heap::relocate_obj_helper (uint8_t* x, size_t s)
24142 {
24143     THREAD_FROM_HEAP;
24144     if (contain_pointers (x))
24145     {
24146         dprintf (3, ("$%Ix$", (size_t)x));
24147
24148         go_through_object_nostart (method_table(x), x, s, pval,
24149                             {
24150                                 uint8_t* child = *pval;
24151                                 reloc_survivor_helper (pval);
24152                                 if (child)
24153                                 {
24154                                     dprintf (3, ("%Ix->%Ix->%Ix", (uint8_t*)pval, child, *pval));
24155                                 }
24156                             });
24157
24158     }
24159     check_class_object_demotion (x);
24160 }
24161
24162 inline 
24163 void gc_heap::reloc_ref_in_shortened_obj (uint8_t** address_to_set_card, uint8_t** address_to_reloc)
24164 {
24165     THREAD_FROM_HEAP;
24166
24167     uint8_t* old_val = (address_to_reloc ? *address_to_reloc : 0);
24168     relocate_address (address_to_reloc THREAD_NUMBER_ARG);
24169     if (address_to_reloc)
24170     {
24171         dprintf (3, ("SR %Ix: %Ix->%Ix", (uint8_t*)address_to_reloc, old_val, *address_to_reloc));
24172     }
24173
24174     //check_demotion_helper (current_saved_info_to_relocate, (uint8_t*)pval);
24175     uint8_t* relocated_addr = *address_to_reloc;
24176     if ((relocated_addr < demotion_high) &&
24177         (relocated_addr >= demotion_low))
24178     {
24179         dprintf (3, ("set card for location %Ix(%Ix)",
24180                     (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
24181
24182         set_card (card_of ((uint8_t*)address_to_set_card));
24183     }
24184 #ifdef MULTIPLE_HEAPS
24185     else if (settings.demotion)
24186     {
24187         gc_heap* hp = heap_of (relocated_addr);
24188         if ((relocated_addr < hp->demotion_high) &&
24189             (relocated_addr >= hp->demotion_low))
24190         {
24191             dprintf (3, ("%Ix on h%d, set card for location %Ix(%Ix)",
24192                         relocated_addr, hp->heap_number, (size_t)address_to_set_card, card_of((uint8_t*)address_to_set_card)));
24193
24194             set_card (card_of ((uint8_t*)address_to_set_card));
24195         }
24196     }
24197 #endif //MULTIPLE_HEAPS
24198 }
24199
24200 void gc_heap::relocate_pre_plug_info (mark* pinned_plug_entry)
24201 {
24202     THREAD_FROM_HEAP;
24203     uint8_t* plug = pinned_plug (pinned_plug_entry);
24204     uint8_t* pre_plug_start = plug - sizeof (plug_and_gap);
24205     // Note that we need to add one ptr size here otherwise we may not be able to find the relocated
24206     // address. Consider this scenario: 
24207     // gen1 start | 3-ptr sized NP | PP
24208     // 0          | 0x18           | 0x30
24209     // If we are asking for the reloc address of 0x10 we will AV in relocate_address because
24210     // the first plug we saw in the brick is 0x18 which means 0x10 will cause us to go back a brick
24211     // which is 0, and then we'll AV in tree_search when we try to do node_right_child (tree). 
24212     pre_plug_start += sizeof (uint8_t*);
24213     uint8_t** old_address = &pre_plug_start;
24214
24215     uint8_t* old_val = (old_address ? *old_address : 0);
24216     relocate_address (old_address THREAD_NUMBER_ARG);
24217     if (old_address)
24218     {
24219         dprintf (3, ("PreR %Ix: %Ix->%Ix, set reloc: %Ix", 
24220             (uint8_t*)old_address, old_val, *old_address, (pre_plug_start - sizeof (uint8_t*))));
24221     }
24222
24223     pinned_plug_entry->set_pre_plug_info_reloc_start (pre_plug_start - sizeof (uint8_t*));
24224 }
24225
24226 inline
24227 void gc_heap::relocate_shortened_obj_helper (uint8_t* x, size_t s, uint8_t* end, mark* pinned_plug_entry, BOOL is_pinned)
24228 {
24229     THREAD_FROM_HEAP;
24230     uint8_t* plug = pinned_plug (pinned_plug_entry);
24231
24232     if (!is_pinned)
24233     {
24234         //// Temporary - we just wanna make sure we are doing things right when padding is needed.
24235         //if ((x + s) < plug)
24236         //{
24237         //    dprintf (3, ("obj %Ix needed padding: end %Ix is %d bytes from pinned obj %Ix", 
24238         //        x, (x + s), (plug- (x + s)), plug));
24239         //    GCToOSInterface::DebugBreak();
24240         //}
24241
24242         relocate_pre_plug_info (pinned_plug_entry);
24243     }
24244
24245     verify_pins_with_post_plug_info("after relocate_pre_plug_info");
24246
24247     uint8_t* saved_plug_info_start = 0;
24248     uint8_t** saved_info_to_relocate = 0;
24249
24250     if (is_pinned)
24251     {
24252         saved_plug_info_start = (uint8_t*)(pinned_plug_entry->get_post_plug_info_start());
24253         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
24254     }
24255     else
24256     {
24257         saved_plug_info_start = (plug - sizeof (plug_and_gap));
24258         saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
24259     }
24260     
24261     uint8_t** current_saved_info_to_relocate = 0;
24262     uint8_t* child = 0;
24263
24264     dprintf (3, ("x: %Ix, pp: %Ix, end: %Ix", x, plug, end));
24265
24266     if (contain_pointers (x))
24267     {
24268         dprintf (3,("$%Ix$", (size_t)x));
24269
24270         go_through_object_nostart (method_table(x), x, s, pval,
24271         {
24272             dprintf (3, ("obj %Ix, member: %Ix->%Ix", x, (uint8_t*)pval, *pval));
24273
24274             if ((uint8_t*)pval >= end)
24275             {
24276                 current_saved_info_to_relocate = saved_info_to_relocate + ((uint8_t*)pval - saved_plug_info_start) / sizeof (uint8_t**);
24277                 child = *current_saved_info_to_relocate;
24278                 reloc_ref_in_shortened_obj (pval, current_saved_info_to_relocate);
24279                 dprintf (3, ("last part: R-%Ix(saved: %Ix)->%Ix ->%Ix",
24280                     (uint8_t*)pval, current_saved_info_to_relocate, child, *current_saved_info_to_relocate));
24281             }
24282             else
24283             {
24284                 reloc_survivor_helper (pval);
24285             }
24286         });
24287     }
24288
24289     check_class_object_demotion (x);
24290 }
24291
24292 void gc_heap::relocate_survivor_helper (uint8_t* plug, uint8_t* plug_end)
24293 {
24294     uint8_t*  x = plug;
24295     while (x < plug_end)
24296     {
24297         size_t s = size (x);
24298         uint8_t* next_obj = x + Align (s);
24299         Prefetch (next_obj);
24300         relocate_obj_helper (x, s);
24301         assert (s > 0);
24302         x = next_obj;
24303     }
24304 }
24305
24306 // if we expanded, right now we are not handling it as We are not saving the new reloc info.
24307 void gc_heap::verify_pins_with_post_plug_info (const char* msg)
24308 {
24309 #if defined  (_DEBUG) && defined (VERIFY_HEAP)
24310     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
24311     {
24312         if (!verify_pinned_queue_p)
24313             return;
24314
24315         if (settings.heap_expansion)
24316             return;
24317
24318         for (size_t i = 0; i < mark_stack_tos; i++)
24319         {
24320             mark& m = mark_stack_array[i];
24321
24322             mark* pinned_plug_entry = pinned_plug_of(i);
24323
24324             if (pinned_plug_entry->has_post_plug_info() && 
24325                 pinned_plug_entry->post_short_p() && 
24326                 (pinned_plug_entry->saved_post_plug_debug.gap != 1))
24327             {
24328                 uint8_t* next_obj = pinned_plug_entry->get_post_plug_info_start() + sizeof (plug_and_gap);
24329                 // object after pin
24330                 dprintf (3, ("OFP: %Ix, G: %Ix, R: %Ix, LC: %d, RC: %d", 
24331                     next_obj, node_gap_size (next_obj), node_relocation_distance (next_obj),
24332                     (int)node_left_child (next_obj), (int)node_right_child (next_obj)));
24333
24334                 size_t* post_plug_debug = (size_t*)(&m.saved_post_plug_debug);
24335
24336                 if (node_gap_size (next_obj) != *post_plug_debug)
24337                 {
24338                     dprintf (3, ("obj: %Ix gap should be %Ix but it is %Ix", 
24339                         next_obj, *post_plug_debug, (size_t)(node_gap_size (next_obj))));
24340                     FATAL_GC_ERROR();
24341                 }
24342                 post_plug_debug++;
24343                 // can't do node_relocation_distance here as it clears the left bit.
24344                 //if (node_relocation_distance (next_obj) != *post_plug_debug)
24345                 if (*((size_t*)(next_obj - 3 * sizeof (size_t))) != *post_plug_debug)
24346                 {
24347                     dprintf (3, ("obj: %Ix reloc should be %Ix but it is %Ix", 
24348                         next_obj, *post_plug_debug, (size_t)(node_relocation_distance (next_obj))));
24349                     FATAL_GC_ERROR();
24350                 }
24351                 if (node_left_child (next_obj) > 0)
24352                 {
24353                     dprintf (3, ("obj: %Ix, vLC: %d\n", next_obj, (int)(node_left_child (next_obj))));
24354                     FATAL_GC_ERROR();
24355                 }
24356             }
24357         }
24358
24359         dprintf (3, ("%s verified", msg));
24360     }
24361 #else // _DEBUG && VERIFY_HEAP
24362     UNREFERENCED_PARAMETER(msg);
24363 #endif // _DEBUG && VERIFY_HEAP
24364 }
24365
24366 #ifdef COLLECTIBLE_CLASS
24367 // We don't want to burn another ptr size space for pinned plugs to record this so just 
24368 // set the card unconditionally for collectible objects if we are demoting.
24369 inline void
24370 gc_heap::unconditional_set_card_collectible (uint8_t* obj)
24371 {
24372     if (settings.demotion)
24373     {
24374         set_card (card_of (obj));
24375     }
24376 }
24377 #endif //COLLECTIBLE_CLASS
24378
24379 void gc_heap::relocate_shortened_survivor_helper (uint8_t* plug, uint8_t* plug_end, mark* pinned_plug_entry)
24380 {
24381     uint8_t*  x = plug;
24382     uint8_t* p_plug = pinned_plug (pinned_plug_entry);
24383     BOOL is_pinned = (plug == p_plug);
24384     BOOL check_short_obj_p = (is_pinned ? pinned_plug_entry->post_short_p() : pinned_plug_entry->pre_short_p());
24385
24386     plug_end += sizeof (gap_reloc_pair);
24387
24388     //dprintf (3, ("%s %Ix is shortened, and last object %s overwritten", (is_pinned ? "PP" : "NP"), plug, (check_short_obj_p ? "is" : "is not")));
24389     dprintf (3, ("%s %Ix-%Ix short, LO: %s OW", (is_pinned ? "PP" : "NP"), plug, plug_end, (check_short_obj_p ? "is" : "is not")));
24390
24391     verify_pins_with_post_plug_info("begin reloc short surv");
24392
24393     while (x < plug_end)
24394     {
24395         if (check_short_obj_p && ((plug_end - x) < (DWORD)min_pre_pin_obj_size))
24396         {
24397             dprintf (3, ("last obj %Ix is short", x));
24398
24399             if (is_pinned)
24400             {
24401 #ifdef COLLECTIBLE_CLASS
24402                 if (pinned_plug_entry->post_short_collectible_p())
24403                     unconditional_set_card_collectible (x);
24404 #endif //COLLECTIBLE_CLASS
24405
24406                 // Relocate the saved references based on bits set.
24407                 uint8_t** saved_plug_info_start = (uint8_t**)(pinned_plug_entry->get_post_plug_info_start());
24408                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_post_plug_reloc_info());
24409                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
24410                 {
24411                     if (pinned_plug_entry->post_short_bit_p (i))
24412                     {
24413                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
24414                     }
24415                 }
24416             }
24417             else
24418             {
24419 #ifdef COLLECTIBLE_CLASS
24420                 if (pinned_plug_entry->pre_short_collectible_p())
24421                     unconditional_set_card_collectible (x);
24422 #endif //COLLECTIBLE_CLASS
24423
24424                 relocate_pre_plug_info (pinned_plug_entry);
24425
24426                 // Relocate the saved references based on bits set.
24427                 uint8_t** saved_plug_info_start = (uint8_t**)(p_plug - sizeof (plug_and_gap));
24428                 uint8_t** saved_info_to_relocate = (uint8_t**)(pinned_plug_entry->get_pre_plug_reloc_info());
24429                 for (size_t i = 0; i < pinned_plug_entry->get_max_short_bits(); i++)
24430                 {
24431                     if (pinned_plug_entry->pre_short_bit_p (i))
24432                     {
24433                         reloc_ref_in_shortened_obj ((saved_plug_info_start + i), (saved_info_to_relocate + i));
24434                     }
24435                 }
24436             }
24437
24438             break;
24439         }
24440
24441         size_t s = size (x);
24442         uint8_t* next_obj = x + Align (s);
24443         Prefetch (next_obj);
24444
24445         if (next_obj >= plug_end) 
24446         {
24447             dprintf (3, ("object %Ix is at the end of the plug %Ix->%Ix", 
24448                 next_obj, plug, plug_end));
24449
24450             verify_pins_with_post_plug_info("before reloc short obj");
24451
24452             relocate_shortened_obj_helper (x, s, (x + Align (s) - sizeof (plug_and_gap)), pinned_plug_entry, is_pinned);
24453         }
24454         else
24455         {
24456             relocate_obj_helper (x, s);
24457         }
24458
24459         assert (s > 0);
24460         x = next_obj;
24461     }
24462
24463     verify_pins_with_post_plug_info("end reloc short surv");
24464 }
24465
24466 void gc_heap::relocate_survivors_in_plug (uint8_t* plug, uint8_t* plug_end,
24467                                           BOOL check_last_object_p, 
24468                                           mark* pinned_plug_entry)
24469 {
24470     //dprintf(3,("Relocating pointers in Plug [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
24471     dprintf (3,("RP: [%Ix,%Ix[", (size_t)plug, (size_t)plug_end));
24472
24473     if (check_last_object_p)
24474     {
24475         relocate_shortened_survivor_helper (plug, plug_end, pinned_plug_entry);
24476     }
24477     else
24478     {
24479         relocate_survivor_helper (plug, plug_end);
24480     }
24481 }
24482
24483 void gc_heap::relocate_survivors_in_brick (uint8_t* tree, relocate_args* args)
24484 {
24485     assert ((tree != NULL));
24486
24487     dprintf (3, ("tree: %Ix, args->last_plug: %Ix, left: %Ix, right: %Ix, gap(t): %Ix",
24488         tree, args->last_plug, 
24489         (tree + node_left_child (tree)),
24490         (tree + node_right_child (tree)),
24491         node_gap_size (tree)));
24492
24493     if (node_left_child (tree))
24494     {
24495         relocate_survivors_in_brick (tree + node_left_child (tree), args);
24496     }
24497     {
24498         uint8_t*  plug = tree;
24499         BOOL   has_post_plug_info_p = FALSE;
24500         BOOL   has_pre_plug_info_p = FALSE;
24501
24502         if (tree == oldest_pinned_plug)
24503         {
24504             args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24505                                                                &has_post_plug_info_p);
24506             assert (tree == pinned_plug (args->pinned_plug_entry));
24507
24508             dprintf (3, ("tree is the oldest pin: %Ix", tree));
24509         }
24510         if (args->last_plug)
24511         {
24512             size_t  gap_size = node_gap_size (tree);
24513             uint8_t*  gap = (plug - gap_size);
24514             dprintf (3, ("tree: %Ix, gap: %Ix (%Ix)", tree, gap, gap_size));
24515             assert (gap_size >= Align (min_obj_size));
24516             uint8_t*  last_plug_end = gap;
24517
24518             BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24519
24520             {
24521                 relocate_survivors_in_plug (args->last_plug, last_plug_end, check_last_object_p, args->pinned_plug_entry);
24522             }
24523         }
24524         else
24525         {
24526             assert (!has_pre_plug_info_p);
24527         }
24528
24529         args->last_plug = plug;
24530         args->is_shortened = has_post_plug_info_p;
24531         if (has_post_plug_info_p)
24532         {
24533             dprintf (3, ("setting %Ix as shortened", plug));
24534         }
24535         dprintf (3, ("last_plug: %Ix(shortened: %d)", plug, (args->is_shortened ? 1 : 0)));
24536     }
24537     if (node_right_child (tree))
24538     {
24539         relocate_survivors_in_brick (tree + node_right_child (tree), args);
24540     }
24541 }
24542
24543 inline
24544 void gc_heap::update_oldest_pinned_plug()
24545 {
24546     oldest_pinned_plug = (pinned_plug_que_empty_p() ? 0 : pinned_plug (oldest_pin()));
24547 }
24548
24549 void gc_heap::relocate_survivors (int condemned_gen_number,
24550                                   uint8_t* first_condemned_address)
24551 {
24552     generation* condemned_gen = generation_of (condemned_gen_number);
24553     uint8_t*  start_address = first_condemned_address;
24554     size_t  current_brick = brick_of (start_address);
24555     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24556
24557     PREFIX_ASSUME(current_heap_segment != NULL);
24558
24559     uint8_t*  end_address = 0;
24560
24561     reset_pinned_queue_bos();
24562     update_oldest_pinned_plug();
24563     
24564     end_address = heap_segment_allocated (current_heap_segment);
24565
24566     size_t  end_brick = brick_of (end_address - 1);
24567     relocate_args args;
24568     args.low = gc_low;
24569     args.high = gc_high;
24570     args.is_shortened = FALSE;
24571     args.pinned_plug_entry = 0;
24572     args.last_plug = 0;
24573     while (1)
24574     {
24575         if (current_brick > end_brick)
24576         {
24577             if (args.last_plug)
24578             {
24579                 {
24580                     assert (!(args.is_shortened));
24581                     relocate_survivors_in_plug (args.last_plug,
24582                                                 heap_segment_allocated (current_heap_segment),
24583                                                 args.is_shortened, 
24584                                                 args.pinned_plug_entry);
24585                 }
24586
24587                 args.last_plug = 0;
24588             }
24589
24590             if (heap_segment_next_rw (current_heap_segment))
24591             {
24592                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24593                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24594                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24595                 continue;
24596             }
24597             else
24598             {
24599                 break;
24600             }
24601         }
24602         {
24603             int brick_entry =  brick_table [ current_brick ];
24604
24605             if (brick_entry >= 0)
24606             {
24607                 relocate_survivors_in_brick (brick_address (current_brick) +
24608                                              brick_entry -1,
24609                                              &args);
24610             }
24611         }
24612         current_brick++;
24613     }
24614 }
24615
24616 void gc_heap::walk_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, walk_relocate_args* args)
24617 {
24618     if (check_last_object_p)
24619     {
24620         size += sizeof (gap_reloc_pair);
24621         mark* entry = args->pinned_plug_entry;
24622
24623         if (args->is_shortened)
24624         {
24625             assert (entry->has_post_plug_info());
24626             entry->swap_post_plug_and_saved_for_profiler();
24627         }
24628         else
24629         {
24630             assert (entry->has_pre_plug_info());
24631             entry->swap_pre_plug_and_saved_for_profiler();
24632         }
24633     }
24634
24635     ptrdiff_t last_plug_relocation = node_relocation_distance (plug);
24636     STRESS_LOG_PLUG_MOVE(plug, (plug + size), -last_plug_relocation);
24637     ptrdiff_t reloc = settings.compaction ? last_plug_relocation : 0;
24638
24639     (args->fn) (plug, (plug + size), reloc, args->profiling_context, !!settings.compaction, false);
24640
24641     if (check_last_object_p)
24642     {
24643         mark* entry = args->pinned_plug_entry;
24644
24645         if (args->is_shortened)
24646         {
24647             entry->swap_post_plug_and_saved_for_profiler();
24648         }
24649         else
24650         {
24651             entry->swap_pre_plug_and_saved_for_profiler();
24652         }
24653     }
24654 }
24655
24656 void gc_heap::walk_relocation_in_brick (uint8_t* tree, walk_relocate_args* args)
24657 {
24658     assert ((tree != NULL));
24659     if (node_left_child (tree))
24660     {
24661         walk_relocation_in_brick (tree + node_left_child (tree), args);
24662     }
24663
24664     uint8_t*  plug = tree;
24665     BOOL   has_pre_plug_info_p = FALSE;
24666     BOOL   has_post_plug_info_p = FALSE;
24667
24668     if (tree == oldest_pinned_plug)
24669     {
24670         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
24671                                                            &has_post_plug_info_p);
24672         assert (tree == pinned_plug (args->pinned_plug_entry));
24673     }
24674
24675     if (args->last_plug != 0)
24676     {
24677         size_t gap_size = node_gap_size (tree);
24678         uint8_t*  gap = (plug - gap_size);
24679         uint8_t*  last_plug_end = gap;
24680         size_t last_plug_size = (last_plug_end - args->last_plug);
24681         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
24682             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
24683         
24684         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
24685         if (!check_last_object_p)
24686         {
24687             assert (last_plug_size >= Align (min_obj_size));
24688         }
24689
24690         walk_plug (args->last_plug, last_plug_size, check_last_object_p, args);
24691     }
24692     else
24693     {
24694         assert (!has_pre_plug_info_p);
24695     }
24696
24697     dprintf (3, ("set args last plug to plug: %Ix", plug));
24698     args->last_plug = plug;
24699     args->is_shortened = has_post_plug_info_p;
24700
24701     if (node_right_child (tree))
24702     {
24703         walk_relocation_in_brick (tree + node_right_child (tree), args);
24704     }
24705 }
24706
24707 void gc_heap::walk_relocation (void* profiling_context, record_surv_fn fn)
24708 {
24709     generation* condemned_gen = generation_of (settings.condemned_generation);
24710     uint8_t*  start_address = generation_allocation_start (condemned_gen);
24711     size_t  current_brick = brick_of (start_address);
24712     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
24713
24714     PREFIX_ASSUME(current_heap_segment != NULL);
24715
24716     reset_pinned_queue_bos();
24717     update_oldest_pinned_plug();
24718     size_t end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24719     walk_relocate_args args;
24720     args.is_shortened = FALSE;
24721     args.pinned_plug_entry = 0;
24722     args.last_plug = 0;
24723     args.profiling_context = profiling_context;
24724     args.fn = fn;
24725
24726     while (1)
24727     {
24728         if (current_brick > end_brick)
24729         {
24730             if (args.last_plug)
24731             {
24732                 walk_plug (args.last_plug, 
24733                            (heap_segment_allocated (current_heap_segment) - args.last_plug), 
24734                            args.is_shortened,
24735                            &args);
24736                 args.last_plug = 0;
24737             }
24738             if (heap_segment_next_rw (current_heap_segment))
24739             {
24740                 current_heap_segment = heap_segment_next_rw (current_heap_segment);
24741                 current_brick = brick_of (heap_segment_mem (current_heap_segment));
24742                 end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
24743                 continue;
24744             }
24745             else
24746             {
24747                 break;
24748             }
24749         }
24750         {
24751             int brick_entry =  brick_table [ current_brick ];
24752             if (brick_entry >= 0)
24753             {
24754                 walk_relocation_in_brick (brick_address (current_brick) +
24755                                           brick_entry - 1,
24756                                           &args);
24757             }
24758         }
24759         current_brick++;
24760     }
24761 }
24762
24763 void gc_heap::walk_survivors (record_surv_fn fn, void* context, walk_surv_type type)
24764 {
24765     if (type == walk_for_gc)
24766         walk_survivors_relocation (context, fn);
24767 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24768     else if (type == walk_for_bgc)
24769         walk_survivors_for_bgc (context, fn);
24770 #endif //BACKGROUND_GC && FEATURE_EVENT_TRACE
24771     else if (type == walk_for_loh)
24772         walk_survivors_for_loh (context, fn);
24773     else
24774         assert (!"unknown type!");
24775 }
24776
24777 #if defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24778 void gc_heap::walk_survivors_for_bgc (void* profiling_context, record_surv_fn fn)
24779 {
24780     // This should only be called for BGCs
24781     assert(settings.concurrent);
24782
24783     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
24784
24785     BOOL small_object_segments = TRUE;
24786     int align_const = get_alignment_constant (small_object_segments);
24787
24788     while (1)
24789     {
24790         if (seg == 0)
24791         {
24792             if (small_object_segments)
24793             {
24794                 //switch to large segment
24795                 small_object_segments = FALSE;
24796
24797                 align_const = get_alignment_constant (small_object_segments);
24798                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
24799
24800                 PREFIX_ASSUME(seg != NULL);
24801
24802                 continue;
24803             }
24804             else 
24805                 break;
24806         }
24807
24808         uint8_t* o = heap_segment_mem (seg);
24809         uint8_t* end = heap_segment_allocated (seg);
24810
24811         while (o < end)
24812         {
24813             if (method_table(o) == g_gc_pFreeObjectMethodTable)
24814             {
24815                 o += Align (size (o), align_const);
24816                 continue;
24817             }
24818
24819             // It's survived. Make a fake plug, starting at o,
24820             // and send the event
24821
24822             uint8_t* plug_start = o;
24823
24824             while (method_table(o) != g_gc_pFreeObjectMethodTable)
24825             {
24826                 o += Align (size (o), align_const);
24827                 if (o >= end)
24828                 {
24829                     break;
24830                 }
24831             }
24832                 
24833             uint8_t* plug_end = o;
24834
24835             fn (plug_start, 
24836                 plug_end,
24837                 0,              // Reloc distance == 0 as this is non-compacting
24838                 profiling_context,
24839                 false,          // Non-compacting
24840                 true);          // BGC
24841         }
24842
24843         seg = heap_segment_next (seg);
24844     }
24845 }
24846 #endif // defined(BACKGROUND_GC) && defined(FEATURE_EVENT_TRACE)
24847
24848 void gc_heap::relocate_phase (int condemned_gen_number,
24849                               uint8_t* first_condemned_address)
24850 {
24851     ScanContext sc;
24852     sc.thread_number = heap_number;
24853     sc.promotion = FALSE;
24854     sc.concurrent = FALSE;
24855
24856
24857 #ifdef TIME_GC
24858         unsigned start;
24859         unsigned finish;
24860         start = GetCycleCount32();
24861 #endif //TIME_GC
24862
24863 //  %type%  category = quote (relocate);
24864     dprintf (2,("---- Relocate phase -----"));
24865
24866 #ifdef MULTIPLE_HEAPS
24867     //join all threads to make sure they are synchronized
24868     dprintf(3, ("Joining after end of plan"));
24869     gc_t_join.join(this, gc_join_begin_relocate_phase);
24870     if (gc_t_join.joined())
24871 #endif //MULTIPLE_HEAPS
24872
24873     {
24874 #ifdef MULTIPLE_HEAPS
24875
24876         //join all threads to make sure they are synchronized
24877         dprintf(3, ("Restarting for relocation"));
24878         gc_t_join.restart();
24879 #endif //MULTIPLE_HEAPS
24880     }
24881
24882     dprintf(3,("Relocating roots"));
24883     GCScan::GcScanRoots(GCHeap::Relocate,
24884                             condemned_gen_number, max_generation, &sc);
24885
24886     verify_pins_with_post_plug_info("after reloc stack");
24887
24888 #ifdef BACKGROUND_GC
24889     if (recursive_gc_sync::background_running_p())
24890     {
24891         scan_background_roots (GCHeap::Relocate, heap_number, &sc);
24892     }
24893 #endif //BACKGROUND_GC
24894
24895     if (condemned_gen_number != max_generation)
24896     {
24897         dprintf(3,("Relocating cross generation pointers"));
24898         mark_through_cards_for_segments (&gc_heap::relocate_address, TRUE);
24899         verify_pins_with_post_plug_info("after reloc cards");
24900     }
24901     if (condemned_gen_number != max_generation)
24902     {
24903         dprintf(3,("Relocating cross generation pointers for large objects"));
24904         mark_through_cards_for_large_objects (&gc_heap::relocate_address, TRUE);
24905     }
24906     else
24907     {
24908 #ifdef FEATURE_LOH_COMPACTION
24909         if (loh_compacted_p)
24910         {
24911             assert (settings.condemned_generation == max_generation);
24912             relocate_in_loh_compact();
24913         }
24914         else
24915 #endif //FEATURE_LOH_COMPACTION
24916         {
24917             relocate_in_large_objects ();
24918         }
24919     }
24920     {
24921         dprintf(3,("Relocating survivors"));
24922         relocate_survivors (condemned_gen_number,
24923                             first_condemned_address);
24924     }
24925
24926 #ifdef FEATURE_PREMORTEM_FINALIZATION
24927         dprintf(3,("Relocating finalization data"));
24928         finalize_queue->RelocateFinalizationData (condemned_gen_number,
24929                                                        __this);
24930 #endif // FEATURE_PREMORTEM_FINALIZATION
24931
24932
24933 // MTHTS
24934     {
24935         dprintf(3,("Relocating handle table"));
24936         GCScan::GcScanHandles(GCHeap::Relocate,
24937                                   condemned_gen_number, max_generation, &sc);
24938     }
24939
24940 #ifdef MULTIPLE_HEAPS
24941     //join all threads to make sure they are synchronized
24942     dprintf(3, ("Joining after end of relocation"));
24943     gc_t_join.join(this, gc_join_relocate_phase_done);
24944
24945 #endif //MULTIPLE_HEAPS
24946
24947 #ifdef TIME_GC
24948         finish = GetCycleCount32();
24949         reloc_time = finish - start;
24950 #endif //TIME_GC
24951
24952     dprintf(2,( "---- End of Relocate phase ----"));
24953 }
24954
24955 // This compares to see if tree is the current pinned plug and returns info
24956 // for this pinned plug. Also advances the pinned queue if that's the case.
24957 //
24958 // We don't change the values of the plug info if tree is not the same as 
24959 // the current pinned plug - the caller is responsible for setting the right
24960 // values to begin with.
24961 //
24962 // POPO TODO: We are keeping this temporarily as this is also used by realloc 
24963 // where it passes FALSE to deque_p, change it to use the same optimization 
24964 // as relocate. Not as essential since realloc is already a slow path.
24965 mark* gc_heap::get_next_pinned_entry (uint8_t* tree,
24966                                       BOOL* has_pre_plug_info_p, 
24967                                       BOOL* has_post_plug_info_p,
24968                                       BOOL deque_p)
24969 {
24970     if (!pinned_plug_que_empty_p())
24971     {
24972         mark* oldest_entry = oldest_pin();
24973         uint8_t* oldest_plug = pinned_plug (oldest_entry);
24974         if (tree == oldest_plug)
24975         {
24976             *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
24977             *has_post_plug_info_p = oldest_entry->has_post_plug_info();
24978
24979             if (deque_p)
24980             {
24981                 deque_pinned_plug();
24982             }
24983
24984             dprintf (3, ("found a pinned plug %Ix, pre: %d, post: %d", 
24985                 tree, 
24986                 (*has_pre_plug_info_p ? 1 : 0),
24987                 (*has_post_plug_info_p ? 1 : 0)));
24988
24989             return oldest_entry;
24990         }
24991     }
24992
24993     return NULL;
24994 }
24995
24996 // This also deques the oldest entry and update the oldest plug
24997 mark* gc_heap::get_oldest_pinned_entry (BOOL* has_pre_plug_info_p, 
24998                                         BOOL* has_post_plug_info_p)
24999 {
25000     mark* oldest_entry = oldest_pin();
25001     *has_pre_plug_info_p =  oldest_entry->has_pre_plug_info();
25002     *has_post_plug_info_p = oldest_entry->has_post_plug_info();
25003
25004     deque_pinned_plug();
25005     update_oldest_pinned_plug();
25006     return oldest_entry;
25007 }
25008
25009 inline
25010 void gc_heap::copy_cards_range (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
25011 {
25012     if (copy_cards_p)
25013         copy_cards_for_addresses (dest, src, len);
25014     else
25015         clear_card_for_addresses (dest, dest + len);
25016 }
25017
25018 // POPO TODO: We should actually just recover the artifically made gaps here..because when we copy
25019 // we always copy the earlier plugs first which means we won't need the gap sizes anymore. This way
25020 // we won't need to individually recover each overwritten part of plugs.
25021 inline
25022 void  gc_heap::gcmemcopy (uint8_t* dest, uint8_t* src, size_t len, BOOL copy_cards_p)
25023 {
25024     if (dest != src)
25025     {
25026 #ifdef BACKGROUND_GC
25027         if (current_c_gc_state == c_gc_state_marking) 
25028         {
25029             //TODO: should look to see whether we should consider changing this
25030             // to copy a consecutive region of the mark array instead.
25031             copy_mark_bits_for_addresses (dest, src, len);
25032         }
25033 #endif //BACKGROUND_GC
25034         //dprintf(3,(" Memcopy [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
25035         dprintf(3,(" mc: [%Ix->%Ix, %Ix->%Ix[", (size_t)src, (size_t)dest, (size_t)src+len, (size_t)dest+len));
25036         memcopy (dest - plug_skew, src - plug_skew, len);
25037 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25038         if (SoftwareWriteWatch::IsEnabledForGCHeap())
25039         {
25040             // The ranges [src - plug_kew .. src[ and [src + len - plug_skew .. src + len[ are ObjHeaders, which don't have GC
25041             // references, and are not relevant for write watch. The latter range actually corresponds to the ObjHeader for the
25042             // object at (src + len), so it can be ignored anyway.
25043             SoftwareWriteWatch::SetDirtyRegion(dest, len - plug_skew);
25044         }
25045 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
25046         copy_cards_range (dest, src, len, copy_cards_p);
25047     }
25048 }
25049
25050 void gc_heap::compact_plug (uint8_t* plug, size_t size, BOOL check_last_object_p, compact_args* args)
25051 {
25052     args->print();
25053     uint8_t* reloc_plug = plug + args->last_plug_relocation;
25054
25055     if (check_last_object_p)
25056     {
25057         size += sizeof (gap_reloc_pair);
25058         mark* entry = args->pinned_plug_entry;
25059
25060         if (args->is_shortened)
25061         {
25062             assert (entry->has_post_plug_info());
25063             entry->swap_post_plug_and_saved();
25064         }
25065         else
25066         {
25067             assert (entry->has_pre_plug_info());
25068             entry->swap_pre_plug_and_saved();
25069         }
25070     }
25071
25072     int  old_brick_entry =  brick_table [brick_of (plug)];
25073
25074     assert (node_relocation_distance (plug) == args->last_plug_relocation);
25075
25076 #ifdef FEATURE_STRUCTALIGN
25077     ptrdiff_t alignpad = node_alignpad(plug);
25078     if (alignpad)
25079     {
25080         make_unused_array (reloc_plug - alignpad, alignpad);
25081         if (brick_of (reloc_plug - alignpad) != brick_of (reloc_plug))
25082         {
25083             // The alignment padding is straddling one or more bricks;
25084             // it has to be the last "object" of its first brick.
25085             fix_brick_to_highest (reloc_plug - alignpad, reloc_plug);
25086         }
25087     }
25088 #else // FEATURE_STRUCTALIGN
25089     size_t unused_arr_size = 0; 
25090     BOOL  already_padded_p = FALSE;
25091 #ifdef SHORT_PLUGS
25092     if (is_plug_padded (plug))
25093     {
25094         already_padded_p = TRUE;
25095         clear_plug_padded (plug);
25096         unused_arr_size = Align (min_obj_size);
25097     }
25098 #endif //SHORT_PLUGS
25099     if (node_realigned (plug))
25100     {
25101         unused_arr_size += switch_alignment_size (already_padded_p);
25102     }
25103
25104     if (unused_arr_size != 0) 
25105     {
25106         make_unused_array (reloc_plug - unused_arr_size, unused_arr_size);
25107
25108         if (brick_of (reloc_plug - unused_arr_size) != brick_of (reloc_plug))
25109         {
25110             dprintf (3, ("fix B for padding: %Id: %Ix->%Ix", 
25111                 unused_arr_size, (reloc_plug - unused_arr_size), reloc_plug));
25112             // The alignment padding is straddling one or more bricks;
25113             // it has to be the last "object" of its first brick.
25114             fix_brick_to_highest (reloc_plug - unused_arr_size, reloc_plug);
25115         }
25116     }
25117 #endif // FEATURE_STRUCTALIGN
25118
25119 #ifdef SHORT_PLUGS
25120     if (is_plug_padded (plug))
25121     {
25122         make_unused_array (reloc_plug - Align (min_obj_size), Align (min_obj_size));
25123
25124         if (brick_of (reloc_plug - Align (min_obj_size)) != brick_of (reloc_plug))
25125         {
25126             // The alignment padding is straddling one or more bricks;
25127             // it has to be the last "object" of its first brick.
25128             fix_brick_to_highest (reloc_plug - Align (min_obj_size), reloc_plug);
25129         }
25130     }
25131 #endif //SHORT_PLUGS
25132
25133     gcmemcopy (reloc_plug, plug, size, args->copy_cards_p);
25134
25135     if (args->check_gennum_p)
25136     {
25137         int src_gennum = args->src_gennum;
25138         if (src_gennum == -1)
25139         {
25140             src_gennum = object_gennum (plug);
25141         }
25142
25143         int dest_gennum = object_gennum_plan (reloc_plug);
25144
25145         if (src_gennum < dest_gennum)
25146         {
25147             generation_allocation_size (generation_of (dest_gennum)) += size;
25148         }
25149     }
25150
25151     size_t current_reloc_brick = args->current_compacted_brick;
25152
25153     if (brick_of (reloc_plug) != current_reloc_brick)
25154     {
25155         dprintf (3, ("last reloc B: %Ix, current reloc B: %Ix", 
25156             current_reloc_brick, brick_of (reloc_plug)));
25157
25158         if (args->before_last_plug)
25159         {
25160             dprintf (3,(" fixing last brick %Ix to point to last plug %Ix(%Ix)",
25161                      current_reloc_brick,
25162                      args->before_last_plug, 
25163                      (args->before_last_plug - brick_address (current_reloc_brick))));
25164
25165             {
25166                 set_brick (current_reloc_brick,
25167                         args->before_last_plug - brick_address (current_reloc_brick));
25168             }
25169         }
25170         current_reloc_brick = brick_of (reloc_plug);
25171     }
25172     size_t end_brick = brick_of (reloc_plug + size-1);
25173     if (end_brick != current_reloc_brick)
25174     {
25175         // The plug is straddling one or more bricks
25176         // It has to be the last plug of its first brick
25177         dprintf (3,("plug spanning multiple bricks, fixing first brick %Ix to %Ix(%Ix)",
25178                  current_reloc_brick, (size_t)reloc_plug,
25179                  (reloc_plug - brick_address (current_reloc_brick))));
25180
25181         {
25182             set_brick (current_reloc_brick,
25183                     reloc_plug - brick_address (current_reloc_brick));
25184         }
25185         // update all intervening brick
25186         size_t brick = current_reloc_brick + 1;
25187         dprintf (3,("setting intervening bricks %Ix->%Ix to -1",
25188             brick, (end_brick - 1)));
25189         while (brick < end_brick)
25190         {
25191             set_brick (brick, -1);
25192             brick++;
25193         }
25194         // code last brick offset as a plug address
25195         args->before_last_plug = brick_address (end_brick) -1;
25196         current_reloc_brick = end_brick;
25197         dprintf (3, ("setting before last to %Ix, last brick to %Ix",
25198             args->before_last_plug, current_reloc_brick));
25199     } 
25200     else
25201     {
25202         dprintf (3, ("still in the same brick: %Ix", end_brick));
25203         args->before_last_plug = reloc_plug;
25204     }
25205     args->current_compacted_brick = current_reloc_brick;
25206
25207     if (check_last_object_p)
25208     {
25209         mark* entry = args->pinned_plug_entry;
25210
25211         if (args->is_shortened)
25212         {
25213             entry->swap_post_plug_and_saved();
25214         }
25215         else
25216         {
25217             entry->swap_pre_plug_and_saved();
25218         }
25219     }
25220 }
25221
25222 void gc_heap::compact_in_brick (uint8_t* tree, compact_args* args)
25223 {
25224     assert (tree != NULL);
25225     int   left_node = node_left_child (tree);
25226     int   right_node = node_right_child (tree);
25227     ptrdiff_t relocation = node_relocation_distance (tree);
25228
25229     args->print();
25230
25231     if (left_node)
25232     {
25233         dprintf (3, ("B: L: %d->%Ix", left_node, (tree + left_node)));
25234         compact_in_brick ((tree + left_node), args);
25235     }
25236
25237     uint8_t*  plug = tree;
25238     BOOL   has_pre_plug_info_p = FALSE;
25239     BOOL   has_post_plug_info_p = FALSE;
25240
25241     if (tree == oldest_pinned_plug)
25242     {
25243         args->pinned_plug_entry = get_oldest_pinned_entry (&has_pre_plug_info_p,
25244                                                            &has_post_plug_info_p);
25245         assert (tree == pinned_plug (args->pinned_plug_entry));
25246     }
25247
25248     if (args->last_plug != 0)
25249     {
25250         size_t gap_size = node_gap_size (tree);
25251         uint8_t*  gap = (plug - gap_size);
25252         uint8_t*  last_plug_end = gap;
25253         size_t last_plug_size = (last_plug_end - args->last_plug);
25254         dprintf (3, ("tree: %Ix, last_plug: %Ix, gap: %Ix(%Ix), last_plug_end: %Ix, size: %Ix", 
25255             tree, args->last_plug, gap, gap_size, last_plug_end, last_plug_size));
25256         
25257         BOOL check_last_object_p = (args->is_shortened || has_pre_plug_info_p);
25258         if (!check_last_object_p)
25259         {
25260             assert (last_plug_size >= Align (min_obj_size));
25261         }
25262
25263         compact_plug (args->last_plug, last_plug_size, check_last_object_p, args);
25264     }
25265     else
25266     {
25267         assert (!has_pre_plug_info_p);
25268     }
25269
25270     dprintf (3, ("set args last plug to plug: %Ix, reloc: %Ix", plug, relocation));
25271     args->last_plug = plug;
25272     args->last_plug_relocation = relocation;
25273     args->is_shortened = has_post_plug_info_p;
25274
25275     if (right_node)
25276     {
25277         dprintf (3, ("B: R: %d->%Ix", right_node, (tree + right_node)));
25278         compact_in_brick ((tree + right_node), args);
25279     }
25280 }
25281
25282 void gc_heap::recover_saved_pinned_info()
25283 {
25284     reset_pinned_queue_bos();
25285
25286     while (!(pinned_plug_que_empty_p()))
25287     {
25288         mark* oldest_entry = oldest_pin();
25289         oldest_entry->recover_plug_info();
25290 #ifdef GC_CONFIG_DRIVEN
25291         if (oldest_entry->has_pre_plug_info() && oldest_entry->has_post_plug_info())
25292             record_interesting_data_point (idp_pre_and_post_pin);
25293         else if (oldest_entry->has_pre_plug_info())
25294             record_interesting_data_point (idp_pre_pin);
25295         else if (oldest_entry->has_post_plug_info())
25296             record_interesting_data_point (idp_post_pin);
25297 #endif //GC_CONFIG_DRIVEN
25298
25299         deque_pinned_plug();
25300     }
25301 }
25302
25303 void gc_heap::compact_phase (int condemned_gen_number,
25304                              uint8_t*  first_condemned_address,
25305                              BOOL clear_cards)
25306 {
25307 //  %type%  category = quote (compact);
25308 #ifdef TIME_GC
25309         unsigned start;
25310         unsigned finish;
25311         start = GetCycleCount32();
25312 #endif //TIME_GC
25313     generation*   condemned_gen = generation_of (condemned_gen_number);
25314     uint8_t*  start_address = first_condemned_address;
25315     size_t   current_brick = brick_of (start_address);
25316     heap_segment*  current_heap_segment = heap_segment_rw (generation_start_segment (condemned_gen));
25317
25318     PREFIX_ASSUME(current_heap_segment != NULL);
25319
25320     reset_pinned_queue_bos();
25321     update_oldest_pinned_plug();
25322
25323     BOOL reused_seg = expand_reused_seg_p();
25324     if (reused_seg)
25325     {
25326         for (int i = 1; i <= max_generation; i++)
25327         {
25328             generation_allocation_size (generation_of (i)) = 0;
25329         }
25330     }
25331
25332     uint8_t*  end_address = heap_segment_allocated (current_heap_segment);
25333
25334     size_t  end_brick = brick_of (end_address-1);
25335     compact_args args;
25336     args.last_plug = 0;
25337     args.before_last_plug = 0;
25338     args.current_compacted_brick = ~((size_t)1);
25339     args.is_shortened = FALSE;
25340     args.pinned_plug_entry = 0;
25341     args.copy_cards_p =  (condemned_gen_number >= 1) || !clear_cards;
25342     args.check_gennum_p = reused_seg;
25343     if (args.check_gennum_p)
25344     {
25345         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
25346     }
25347
25348     dprintf (2,("---- Compact Phase: %Ix(%Ix)----", 
25349         first_condemned_address, brick_of (first_condemned_address)));
25350
25351 #ifdef MULTIPLE_HEAPS
25352     //restart
25353     if (gc_t_join.joined())
25354     {
25355 #endif //MULTIPLE_HEAPS
25356
25357 #ifdef MULTIPLE_HEAPS
25358         dprintf(3, ("Restarting for compaction"));
25359         gc_t_join.restart();
25360     }
25361 #endif //MULTIPLE_HEAPS
25362
25363     reset_pinned_queue_bos();
25364
25365 #ifdef FEATURE_LOH_COMPACTION
25366     if (loh_compacted_p)
25367     {
25368         compact_loh();
25369     }
25370 #endif //FEATURE_LOH_COMPACTION
25371
25372     if ((start_address < end_address) ||
25373         (condemned_gen_number == max_generation))
25374     {
25375         while (1)
25376         {
25377             if (current_brick > end_brick)
25378             {
25379                 if (args.last_plug != 0)
25380                 {
25381                     dprintf (3, ("compacting last plug: %Ix", args.last_plug))
25382                     compact_plug (args.last_plug,
25383                                   (heap_segment_allocated (current_heap_segment) - args.last_plug),
25384                                   args.is_shortened,
25385                                   &args);
25386                 }
25387
25388                 if (heap_segment_next_rw (current_heap_segment))
25389                 {
25390                     current_heap_segment = heap_segment_next_rw (current_heap_segment);
25391                     current_brick = brick_of (heap_segment_mem (current_heap_segment));
25392                     end_brick = brick_of (heap_segment_allocated (current_heap_segment)-1);
25393                     args.last_plug = 0;
25394                     if (args.check_gennum_p)
25395                     {
25396                         args.src_gennum = ((current_heap_segment == ephemeral_heap_segment) ? -1 : 2);
25397                     }
25398                     continue;
25399                 }
25400                 else
25401                 {
25402                     if (args.before_last_plug !=0)
25403                     {
25404                         dprintf (3, ("Fixing last brick %Ix to point to plug %Ix",
25405                                     args.current_compacted_brick, (size_t)args.before_last_plug));
25406                         assert (args.current_compacted_brick != ~1u);
25407                         set_brick (args.current_compacted_brick,
25408                                    args.before_last_plug - brick_address (args.current_compacted_brick));
25409                     }
25410                     break;
25411                 }
25412             }
25413             {
25414                 int  brick_entry =  brick_table [ current_brick ];
25415                 dprintf (3, ("B: %Ix(%Ix)->%Ix", 
25416                     current_brick, (size_t)brick_entry, (brick_address (current_brick) + brick_entry - 1)));
25417
25418                 if (brick_entry >= 0)
25419                 {
25420                     compact_in_brick ((brick_address (current_brick) + brick_entry -1),
25421                                       &args);
25422
25423                 }
25424             }
25425             current_brick++;
25426         }
25427     }
25428
25429     recover_saved_pinned_info();
25430
25431 #ifdef TIME_GC
25432     finish = GetCycleCount32();
25433     compact_time = finish - start;
25434 #endif //TIME_GC
25435
25436     concurrent_print_time_delta ("compact end");
25437
25438     dprintf(2,("---- End of Compact phase ----"));
25439 }
25440
25441 #ifdef MULTIPLE_HEAPS
25442
25443 #ifdef _MSC_VER
25444 #pragma warning(push)
25445 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
25446 #endif //_MSC_VER
25447 void gc_heap::gc_thread_stub (void* arg)
25448 {
25449     gc_heap* heap = (gc_heap*)arg;
25450     if (!gc_thread_no_affinitize_p)
25451     {
25452         GCThreadAffinity affinity;
25453         affinity.Group = GCThreadAffinity::None;
25454         affinity.Processor = GCThreadAffinity::None;
25455
25456         // We are about to set affinity for GC threads. It is a good place to set up NUMA and
25457         // CPU groups because the process mask, processor number, and group number are all
25458         // readily available.
25459         if (GCToOSInterface::CanEnableGCCPUGroups())
25460             set_thread_group_affinity_for_heap(heap->heap_number, &affinity);
25461         else
25462             set_thread_affinity_mask_for_heap(heap->heap_number, &affinity);
25463
25464         if (!GCToOSInterface::SetThreadAffinity(&affinity))
25465         {
25466             dprintf(1, ("Failed to set thread affinity for server GC thread"));
25467         }
25468     }
25469
25470     // server GC threads run at a higher priority than normal.
25471     GCToOSInterface::BoostThreadPriority();
25472     _alloca (256*heap->heap_number);
25473     heap->gc_thread_function();
25474 }
25475 #ifdef _MSC_VER
25476 #pragma warning(pop)
25477 #endif //_MSC_VER
25478
25479 #endif //MULTIPLE_HEAPS
25480
25481 #ifdef BACKGROUND_GC
25482
25483 #ifdef _MSC_VER
25484 #pragma warning(push)
25485 #pragma warning(disable:4702) // C4702: unreachable code: gc_thread_function may not return
25486 #endif //_MSC_VER
25487 void gc_heap::bgc_thread_stub (void* arg)
25488 {
25489     gc_heap* heap = (gc_heap*)arg;
25490     heap->bgc_thread = GCToEEInterface::GetThread();
25491     assert(heap->bgc_thread != nullptr);
25492     heap->bgc_thread_function();
25493 }
25494 #ifdef _MSC_VER
25495 #pragma warning(pop)
25496 #endif //_MSC_VER
25497
25498 #endif //BACKGROUND_GC
25499
25500 /*------------------ Background GC ----------------------------*/
25501
25502 #ifdef BACKGROUND_GC
25503
25504 void gc_heap::background_drain_mark_list (int thread)
25505 {
25506     UNREFERENCED_PARAMETER(thread);
25507
25508     size_t saved_c_mark_list_index = c_mark_list_index;
25509
25510     if (saved_c_mark_list_index)
25511     {
25512         concurrent_print_time_delta ("SML");
25513     }
25514     while (c_mark_list_index != 0)
25515     {
25516         size_t current_index = c_mark_list_index - 1;
25517         uint8_t* o = c_mark_list [current_index];
25518         background_mark_object (o THREAD_NUMBER_ARG);
25519         c_mark_list_index--;
25520     }
25521     if (saved_c_mark_list_index)
25522     {
25523
25524         concurrent_print_time_delta ("EML");
25525     }
25526
25527     fire_drain_mark_list_event (saved_c_mark_list_index);
25528 }
25529
25530
25531 // The background GC version of scan_dependent_handles (see that method for a more in-depth comment).
25532 #ifdef MULTIPLE_HEAPS
25533 // Since we only scan dependent handles while we are stopped we'll never interfere with FGCs scanning
25534 // them. So we can use the same static variables.
25535 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
25536 {
25537     // Whenever we call this method there may have been preceding object promotions. So set
25538     // s_fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
25539     // based on the how the scanning proceeded).
25540     s_fUnscannedPromotions = TRUE;
25541
25542     // We don't know how many times we need to loop yet. In particular we can't base the loop condition on
25543     // the state of this thread's portion of the dependent handle table. That's because promotions on other
25544     // threads could cause handle promotions to become necessary here. Even if there are definitely no more
25545     // promotions possible in this thread's handles, we still have to stay in lock-step with those worker
25546     // threads that haven't finished yet (each GC worker thread has to join exactly the same number of times
25547     // as all the others or they'll get out of step).
25548     while (true)
25549     {
25550         // The various worker threads are all currently racing in this code. We need to work out if at least
25551         // one of them think they have work to do this cycle. Each thread needs to rescan its portion of the
25552         // dependent handle table when both of the following conditions apply:
25553         //  1) At least one (arbitrary) object might have been promoted since the last scan (because if this
25554         //     object happens to correspond to a primary in one of our handles we might potentially have to
25555         //     promote the associated secondary).
25556         //  2) The table for this thread has at least one handle with a secondary that isn't promoted yet.
25557         //
25558         // The first condition is represented by s_fUnscannedPromotions. This is always non-zero for the first
25559         // iteration of this loop (see comment above) and in subsequent cycles each thread updates this
25560         // whenever a mark stack overflow occurs or scanning their dependent handles results in a secondary
25561         // being promoted. This value is cleared back to zero in a synchronized fashion in the join that
25562         // follows below. Note that we can't read this outside of the join since on any iteration apart from
25563         // the first threads will be racing between reading this value and completing their previous
25564         // iteration's table scan.
25565         //
25566         // The second condition is tracked by the dependent handle code itself on a per worker thread basis
25567         // (and updated by the GcDhReScan() method). We call GcDhUnpromotedHandlesExist() on each thread to
25568         // determine the local value and collect the results into the s_fUnpromotedHandles variable in what is
25569         // effectively an OR operation. As per s_fUnscannedPromotions we can't read the final result until
25570         // we're safely joined.
25571         if (GCScan::GcDhUnpromotedHandlesExist(sc))
25572             s_fUnpromotedHandles = TRUE;
25573
25574         // Synchronize all the threads so we can read our state variables safely. The following shared
25575         // variable (indicating whether we should scan the tables or terminate the loop) will be set by a
25576         // single thread inside the join.
25577         bgc_t_join.join(this, gc_join_scan_dependent_handles);
25578         if (bgc_t_join.joined())
25579         {
25580             // We're synchronized so it's safe to read our shared state variables. We update another shared
25581             // variable to indicate to all threads whether we'll be scanning for another cycle or terminating
25582             // the loop. We scan if there has been at least one object promotion since last time and at least
25583             // one thread has a dependent handle table with a potential handle promotion possible.
25584             s_fScanRequired = s_fUnscannedPromotions && s_fUnpromotedHandles;
25585
25586             // Reset our shared state variables (ready to be set again on this scan or with a good initial
25587             // value for the next call if we're terminating the loop).
25588             s_fUnscannedPromotions = FALSE;
25589             s_fUnpromotedHandles = FALSE;
25590
25591             if (!s_fScanRequired)
25592             {
25593                 uint8_t* all_heaps_max = 0;
25594                 uint8_t* all_heaps_min = MAX_PTR;
25595                 int i;
25596                 for (i = 0; i < n_heaps; i++)
25597                 {
25598                     if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
25599                         all_heaps_max = g_heaps[i]->background_max_overflow_address;
25600                     if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
25601                         all_heaps_min = g_heaps[i]->background_min_overflow_address;
25602                 }
25603                 for (i = 0; i < n_heaps; i++)
25604                 {
25605                     g_heaps[i]->background_max_overflow_address = all_heaps_max;
25606                     g_heaps[i]->background_min_overflow_address = all_heaps_min;
25607                 }
25608             }
25609
25610             // Restart all the workers.
25611             dprintf(2, ("Starting all gc thread mark stack overflow processing"));
25612             bgc_t_join.restart();
25613         }
25614
25615         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25616         // being visible. If there really was an overflow (process_mark_overflow returns true) then set the
25617         // global flag indicating that at least one object promotion may have occurred (the usual comment
25618         // about races applies). (Note it's OK to set this flag even if we're about to terminate the loop and
25619         // exit the method since we unconditionally set this variable on method entry anyway).
25620         if (background_process_mark_overflow (sc->concurrent))
25621             s_fUnscannedPromotions = TRUE;
25622
25623         // If we decided that no scan was required we can terminate the loop now.
25624         if (!s_fScanRequired)
25625             break;
25626
25627         // Otherwise we must join with the other workers to ensure that all mark stack overflows have been
25628         // processed before we start scanning dependent handle tables (if overflows remain while we scan we
25629         // could miss noting the promotion of some primary objects).
25630         bgc_t_join.join(this, gc_join_rescan_dependent_handles);
25631         if (bgc_t_join.joined())
25632         {
25633             // Restart all the workers.
25634             dprintf(3, ("Starting all gc thread for dependent handle promotion"));
25635             bgc_t_join.restart();
25636         }
25637
25638         // If the portion of the dependent handle table managed by this worker has handles that could still be
25639         // promoted perform a rescan. If the rescan resulted in at least one promotion note this fact since it
25640         // could require a rescan of handles on this or other workers.
25641         if (GCScan::GcDhUnpromotedHandlesExist(sc))
25642             if (GCScan::GcDhReScan(sc))
25643                 s_fUnscannedPromotions = TRUE;
25644     }
25645 }
25646 #else
25647 void gc_heap::background_scan_dependent_handles (ScanContext *sc)
25648 {
25649     // Whenever we call this method there may have been preceding object promotions. So set
25650     // fUnscannedPromotions unconditionally (during further iterations of the scanning loop this will be set
25651     // based on the how the scanning proceeded).
25652     bool fUnscannedPromotions = true;
25653
25654     // Scan dependent handles repeatedly until there are no further promotions that can be made or we made a
25655     // scan without performing any new promotions.
25656     while (GCScan::GcDhUnpromotedHandlesExist(sc) && fUnscannedPromotions)
25657     {
25658         // On each iteration of the loop start with the assumption that no further objects have been promoted.
25659         fUnscannedPromotions = false;
25660
25661         // Handle any mark stack overflow: scanning dependent handles relies on all previous object promotions
25662         // being visible. If there was an overflow (background_process_mark_overflow returned true) then
25663         // additional objects now appear to be promoted and we should set the flag.
25664         if (background_process_mark_overflow (sc->concurrent))
25665             fUnscannedPromotions = true;
25666
25667         // Perform the scan and set the flag if any promotions resulted.
25668         if (GCScan::GcDhReScan (sc))
25669             fUnscannedPromotions = true;
25670     }
25671
25672     // Perform a last processing of any overflowed mark stack.
25673     background_process_mark_overflow (sc->concurrent);
25674 }
25675 #endif //MULTIPLE_HEAPS
25676
25677 void gc_heap::recover_bgc_settings()
25678 {
25679     if ((settings.condemned_generation < max_generation) && recursive_gc_sync::background_running_p())
25680     {
25681         dprintf (2, ("restoring bgc settings"));
25682         settings = saved_bgc_settings;
25683         GCHeap::GcCondemnedGeneration = gc_heap::settings.condemned_generation;
25684     }
25685 }
25686
25687 void gc_heap::allow_fgc()
25688 {
25689     assert (bgc_thread == GCToEEInterface::GetThread());
25690     bool bToggleGC = false;
25691
25692     if (g_fSuspensionPending > 0)
25693     {
25694         bToggleGC = GCToEEInterface::EnablePreemptiveGC();
25695         if (bToggleGC)
25696         {
25697             GCToEEInterface::DisablePreemptiveGC();
25698         }
25699     }
25700 }
25701
25702 BOOL gc_heap::should_commit_mark_array()
25703 {
25704     return (recursive_gc_sync::background_running_p() || (current_bgc_state == bgc_initialized));
25705 }
25706
25707 void gc_heap::clear_commit_flag()
25708 {
25709     generation* gen = generation_of (max_generation);
25710     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25711     while (1)
25712     {
25713         if (seg == 0)
25714         {
25715             if (gen != large_object_generation)
25716             {
25717                 gen = large_object_generation;
25718                 seg = heap_segment_in_range (generation_start_segment (gen));
25719             }
25720             else
25721             {
25722                 break;
25723             }
25724         }
25725
25726         if (seg->flags & heap_segment_flags_ma_committed)
25727         {
25728             seg->flags &= ~heap_segment_flags_ma_committed;
25729         }
25730
25731         if (seg->flags & heap_segment_flags_ma_pcommitted)
25732         {
25733             seg->flags &= ~heap_segment_flags_ma_pcommitted;
25734         }
25735
25736         seg = heap_segment_next (seg);
25737     }
25738 }
25739
25740 void gc_heap::clear_commit_flag_global()
25741 {
25742 #ifdef MULTIPLE_HEAPS
25743     for (int i = 0; i < n_heaps; i++)
25744     {
25745         g_heaps[i]->clear_commit_flag();
25746     }
25747 #else
25748     clear_commit_flag();
25749 #endif //MULTIPLE_HEAPS
25750 }
25751
25752 void gc_heap::verify_mark_array_cleared (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25753 {
25754 #ifdef _DEBUG
25755     size_t  markw = mark_word_of (begin);
25756     size_t  markw_end = mark_word_of (end);
25757
25758     while (markw < markw_end)
25759     {
25760         if (mark_array_addr[markw])
25761         {
25762             dprintf  (1, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
25763                             markw, mark_array_addr[markw], mark_word_address (markw)));
25764             FATAL_GC_ERROR();
25765         }
25766         markw++;
25767     }
25768 #else // _DEBUG
25769     UNREFERENCED_PARAMETER(begin);
25770     UNREFERENCED_PARAMETER(end);
25771     UNREFERENCED_PARAMETER(mark_array_addr);
25772 #endif //_DEBUG
25773 }
25774
25775 void gc_heap::verify_mark_array_cleared (heap_segment* seg, uint32_t* mark_array_addr)
25776 {
25777     verify_mark_array_cleared (heap_segment_mem (seg), heap_segment_reserved (seg), mark_array_addr);
25778 }
25779
25780 BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp, 
25781                                          heap_segment* seg,
25782                                          uint32_t* new_card_table,
25783                                          uint8_t* new_lowest_address)
25784 {
25785     UNREFERENCED_PARAMETER(hp); // compiler bug? -- this *is*, indeed, referenced
25786
25787     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25788     uint8_t* end = heap_segment_reserved (seg);
25789
25790     uint8_t* lowest = hp->background_saved_lowest_address;
25791     uint8_t* highest = hp->background_saved_highest_address;
25792
25793     uint8_t* commit_start = NULL;
25794     uint8_t* commit_end = NULL;
25795     size_t commit_flag = 0;
25796
25797     if ((highest >= start) &&
25798         (lowest <= end))
25799     {
25800         if ((start >= lowest) && (end <= highest))
25801         {
25802             dprintf (GC_TABLE_LOG, ("completely in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25803                                     start, end, lowest, highest));
25804             commit_flag = heap_segment_flags_ma_committed;
25805         }
25806         else
25807         {
25808             dprintf (GC_TABLE_LOG, ("partially in bgc range: seg %Ix-%Ix, bgc: %Ix-%Ix",
25809                                     start, end, lowest, highest));
25810             commit_flag = heap_segment_flags_ma_pcommitted;
25811         }
25812
25813         commit_start = max (lowest, start);
25814         commit_end = min (highest, end);
25815
25816         if (!commit_mark_array_by_range (commit_start, commit_end, hp->mark_array))
25817         {
25818             return FALSE;
25819         }
25820
25821         if (new_card_table == 0)
25822         {
25823             new_card_table = g_gc_card_table;
25824         }
25825
25826         if (hp->card_table != new_card_table)
25827         {
25828             if (new_lowest_address == 0)
25829             {
25830                 new_lowest_address = g_gc_lowest_address;
25831             }
25832
25833             uint32_t* ct = &new_card_table[card_word (gcard_of (new_lowest_address))];
25834             uint32_t* ma = (uint32_t*)((uint8_t*)card_table_mark_array (ct) - size_mark_array_of (0, new_lowest_address));
25835
25836             dprintf (GC_TABLE_LOG, ("table realloc-ed: %Ix->%Ix, MA: %Ix->%Ix", 
25837                                     hp->card_table, new_card_table,
25838                                     hp->mark_array, ma));
25839
25840             if (!commit_mark_array_by_range (commit_start, commit_end, ma))
25841             {
25842                 return FALSE;
25843             }
25844         }
25845
25846         seg->flags |= commit_flag;
25847     }
25848
25849     return TRUE;
25850 }
25851
25852 BOOL gc_heap::commit_mark_array_by_range (uint8_t* begin, uint8_t* end, uint32_t* mark_array_addr)
25853 {
25854     size_t beg_word = mark_word_of (begin);
25855     size_t end_word = mark_word_of (align_on_mark_word (end));
25856     uint8_t* commit_start = align_lower_page ((uint8_t*)&mark_array_addr[beg_word]);
25857     uint8_t* commit_end = align_on_page ((uint8_t*)&mark_array_addr[end_word]);
25858     size_t size = (size_t)(commit_end - commit_start);
25859
25860 #ifdef SIMPLE_DPRINTF
25861     dprintf (GC_TABLE_LOG, ("range: %Ix->%Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), commit %Ix->%Ix(%Id)",
25862                             begin, end,
25863                             beg_word, end_word,
25864                             (end_word - beg_word) * sizeof (uint32_t),
25865                             &mark_array_addr[beg_word],
25866                             &mark_array_addr[end_word],
25867                             (size_t)(&mark_array_addr[end_word] - &mark_array_addr[beg_word]),
25868                             commit_start, commit_end,
25869                             size));
25870 #endif //SIMPLE_DPRINTF
25871
25872     if (virtual_commit (commit_start, size))
25873     {
25874         // We can only verify the mark array is cleared from begin to end, the first and the last
25875         // page aren't necessarily all cleared 'cause they could be used by other segments or 
25876         // card bundle.
25877         verify_mark_array_cleared (begin, end, mark_array_addr);
25878         return TRUE;
25879     }
25880     else
25881     {
25882         dprintf (GC_TABLE_LOG, ("failed to commit %Id bytes", (end_word - beg_word) * sizeof (uint32_t)));
25883         return FALSE;
25884     }
25885 }
25886
25887 BOOL gc_heap::commit_mark_array_with_check (heap_segment* seg, uint32_t* new_mark_array_addr)
25888 {
25889     uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
25890     uint8_t* end = heap_segment_reserved (seg);
25891
25892 #ifdef MULTIPLE_HEAPS
25893     uint8_t* lowest = heap_segment_heap (seg)->background_saved_lowest_address;
25894     uint8_t* highest = heap_segment_heap (seg)->background_saved_highest_address;
25895 #else
25896     uint8_t* lowest = background_saved_lowest_address;
25897     uint8_t* highest = background_saved_highest_address;
25898 #endif //MULTIPLE_HEAPS
25899
25900     if ((highest >= start) &&
25901         (lowest <= end))
25902     {
25903         start = max (lowest, start);
25904         end = min (highest, end);
25905         if (!commit_mark_array_by_range (start, end, new_mark_array_addr))
25906         {
25907             return FALSE;
25908         }
25909     }
25910
25911     return TRUE;
25912 }
25913
25914 BOOL gc_heap::commit_mark_array_by_seg (heap_segment* seg, uint32_t* mark_array_addr)
25915 {
25916     dprintf (GC_TABLE_LOG, ("seg: %Ix->%Ix; MA: %Ix",
25917         seg,
25918         heap_segment_reserved (seg),
25919         mark_array_addr));
25920     uint8_t* start = (heap_segment_read_only_p (seg) ? heap_segment_mem (seg) : (uint8_t*)seg);
25921
25922     return commit_mark_array_by_range (start, heap_segment_reserved (seg), mark_array_addr);
25923 }
25924
25925 BOOL gc_heap::commit_mark_array_bgc_init (uint32_t* mark_array_addr)
25926 {
25927     UNREFERENCED_PARAMETER(mark_array_addr);
25928
25929     dprintf (GC_TABLE_LOG, ("BGC init commit: lowest: %Ix, highest: %Ix, mark_array: %Ix", 
25930                             lowest_address, highest_address, mark_array));
25931
25932     generation* gen = generation_of (max_generation);
25933     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
25934     while (1)
25935     {
25936         if (seg == 0)
25937         {
25938             if (gen != large_object_generation)
25939             {
25940                 gen = large_object_generation;
25941                 seg = heap_segment_in_range (generation_start_segment (gen));
25942             }
25943             else
25944             {
25945                 break;
25946             }
25947         }
25948
25949         dprintf (GC_TABLE_LOG, ("seg: %Ix, flags: %Id", seg, seg->flags));
25950
25951         if (!(seg->flags & heap_segment_flags_ma_committed))
25952         {
25953             // For ro segments they could always be only partially in range so we'd
25954             // be calling this at the beginning of every BGC. We are not making this 
25955             // more efficient right now - ro segments are currently only used by redhawk.
25956             if (heap_segment_read_only_p (seg))
25957             {
25958                 if ((heap_segment_mem (seg) >= lowest_address) && 
25959                     (heap_segment_reserved (seg) <= highest_address))
25960                 {
25961                     if (commit_mark_array_by_seg (seg, mark_array))
25962                     {
25963                         seg->flags |= heap_segment_flags_ma_committed;
25964                     }
25965                     else
25966                     {
25967                         return FALSE;
25968                     }
25969                 }
25970                 else
25971                 {
25972                     uint8_t* start = max (lowest_address, heap_segment_mem (seg));
25973                     uint8_t* end = min (highest_address, heap_segment_reserved (seg));
25974                     if (commit_mark_array_by_range (start, end, mark_array))
25975                     {
25976                         seg->flags |= heap_segment_flags_ma_pcommitted;
25977                     }
25978                     else
25979                     {
25980                         return FALSE;
25981                     }
25982                 }
25983             }
25984             else
25985             {
25986                 // For normal segments they are by design completely in range so just 
25987                 // commit the whole mark array for each seg.
25988                 if (commit_mark_array_by_seg (seg, mark_array))
25989                 {
25990                     if (seg->flags & heap_segment_flags_ma_pcommitted)
25991                     {
25992                         seg->flags &= ~heap_segment_flags_ma_pcommitted;
25993                     }
25994                     seg->flags |= heap_segment_flags_ma_committed;
25995                 }
25996                 else
25997                 {
25998                     return FALSE;
25999                 }
26000             }
26001         }
26002
26003         seg = heap_segment_next (seg);
26004     }
26005
26006     return TRUE;
26007 }
26008
26009 // This function doesn't check the commit flag since it's for a new array -
26010 // the mark_array flag for these segments will remain the same.
26011 BOOL gc_heap::commit_new_mark_array (uint32_t* new_mark_array_addr)
26012 {
26013     dprintf (GC_TABLE_LOG, ("commiting existing segs on MA %Ix", new_mark_array_addr));
26014     generation* gen = generation_of (max_generation);
26015     heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
26016     while (1)
26017     {
26018         if (seg == 0)
26019         {
26020             if (gen != large_object_generation)
26021             {
26022                 gen = large_object_generation;
26023                 seg = heap_segment_in_range (generation_start_segment (gen));
26024             }
26025             else
26026             {
26027                 break;
26028             }
26029         }
26030
26031         if (!commit_mark_array_with_check (seg, new_mark_array_addr))
26032         {
26033             return FALSE;
26034         }
26035
26036         seg = heap_segment_next (seg);
26037     }
26038
26039 #ifdef MULTIPLE_HEAPS
26040     if (new_heap_segment)
26041     {
26042         if (!commit_mark_array_with_check (new_heap_segment, new_mark_array_addr))
26043         {
26044             return FALSE;
26045         }        
26046     }
26047 #endif //MULTIPLE_HEAPS
26048
26049     return TRUE;
26050 }
26051
26052 BOOL gc_heap::commit_new_mark_array_global (uint32_t* new_mark_array)
26053 {
26054 #ifdef MULTIPLE_HEAPS
26055     for (int i = 0; i < n_heaps; i++)
26056     {
26057         if (!g_heaps[i]->commit_new_mark_array (new_mark_array))
26058         {
26059             return FALSE;
26060         }
26061     }
26062 #else
26063     if (!commit_new_mark_array (new_mark_array))
26064     {
26065         return FALSE;
26066     }
26067 #endif //MULTIPLE_HEAPS
26068
26069     return TRUE;
26070 }
26071
26072 void gc_heap::decommit_mark_array_by_seg (heap_segment* seg)
26073 {
26074     // if BGC is disabled (the finalize watchdog does this at shutdown), the mark array could have
26075     // been set to NULL. 
26076     if (mark_array == NULL)
26077     {
26078         return;
26079     }
26080
26081     dprintf (GC_TABLE_LOG, ("decommitting seg %Ix(%Ix), MA: %Ix", seg, seg->flags, mark_array));
26082
26083     size_t flags = seg->flags;
26084
26085     if ((flags & heap_segment_flags_ma_committed) ||
26086         (flags & heap_segment_flags_ma_pcommitted))
26087     {
26088         uint8_t* start = (heap_segment_read_only_p(seg) ? heap_segment_mem(seg) : (uint8_t*)seg);
26089         uint8_t* end = heap_segment_reserved (seg);
26090
26091         if (flags & heap_segment_flags_ma_pcommitted)
26092         {
26093             start = max (lowest_address, start);
26094             end = min (highest_address, end);
26095         }
26096
26097         size_t beg_word = mark_word_of (start);
26098         size_t end_word = mark_word_of (align_on_mark_word (end));
26099         uint8_t* decommit_start = align_on_page ((uint8_t*)&mark_array[beg_word]);
26100         uint8_t* decommit_end = align_lower_page ((uint8_t*)&mark_array[end_word]);
26101         size_t size = (size_t)(decommit_end - decommit_start);
26102
26103 #ifdef SIMPLE_DPRINTF
26104         dprintf (GC_TABLE_LOG, ("seg: %Ix mark word: %Ix->%Ix(%Id), mark array: %Ix->%Ix(%Id), decommit %Ix->%Ix(%Id)",
26105                                 seg,
26106                                 beg_word, end_word,
26107                                 (end_word - beg_word) * sizeof (uint32_t),
26108                                 &mark_array[beg_word],
26109                                 &mark_array[end_word],
26110                                 (size_t)(&mark_array[end_word] - &mark_array[beg_word]),
26111                                 decommit_start, decommit_end,
26112                                 size));
26113 #endif //SIMPLE_DPRINTF
26114         
26115         if (decommit_start < decommit_end)
26116         {
26117             if (!virtual_decommit (decommit_start, size))
26118             {
26119                 dprintf (GC_TABLE_LOG, ("decommit on %Ix for %Id bytes failed", 
26120                                         decommit_start, size));
26121                 assert (!"decommit failed");
26122             }
26123         }
26124
26125         dprintf (GC_TABLE_LOG, ("decommited [%Ix for address [%Ix", beg_word, seg));
26126     }
26127 }
26128
26129 void gc_heap::background_mark_phase ()
26130 {
26131     verify_mark_array_cleared();
26132
26133     ScanContext sc;
26134     sc.thread_number = heap_number;
26135     sc.promotion = TRUE;
26136     sc.concurrent = FALSE;
26137
26138     THREAD_FROM_HEAP;
26139     BOOL cooperative_mode = TRUE;
26140 #ifndef MULTIPLE_HEAPS
26141     const int thread = heap_number;
26142 #endif //!MULTIPLE_HEAPS
26143
26144     dprintf(2,("-(GC%d)BMark-", VolatileLoad(&settings.gc_index)));
26145
26146     assert (settings.concurrent);
26147
26148 #ifdef TIME_GC
26149     unsigned start;
26150     unsigned finish;
26151     start = GetCycleCount32();
26152 #endif //TIME_GC
26153
26154 #ifdef FFIND_OBJECT
26155     if (gen0_must_clear_bricks > 0)
26156         gen0_must_clear_bricks--;
26157 #endif //FFIND_OBJECT
26158
26159     background_soh_alloc_count = 0;
26160     background_loh_alloc_count = 0;
26161     bgc_overflow_count = 0;
26162
26163     bpromoted_bytes (heap_number) = 0;
26164     static uint32_t num_sizedrefs = 0;
26165
26166     background_min_overflow_address = MAX_PTR;
26167     background_max_overflow_address = 0;
26168     background_min_soh_overflow_address = MAX_PTR;
26169     background_max_soh_overflow_address = 0;
26170     processed_soh_overflow_p = FALSE;
26171
26172     {
26173         //set up the mark lists from g_mark_list
26174         assert (g_mark_list);
26175         mark_list = g_mark_list;
26176         //dont use the mark list for full gc
26177         //because multiple segments are more complex to handle and the list
26178         //is likely to overflow
26179         mark_list_end = &mark_list [0];
26180         mark_list_index = &mark_list [0];
26181
26182         c_mark_list_index = 0;
26183
26184 #ifndef MULTIPLE_HEAPS
26185         shigh = (uint8_t*) 0;
26186         slow  = MAX_PTR;
26187 #endif //MULTIPLE_HEAPS
26188
26189         generation*   gen = generation_of (max_generation);
26190
26191         dprintf(3,("BGC: stack marking"));
26192         sc.concurrent = TRUE;
26193
26194         GCScan::GcScanRoots(background_promote_callback,
26195                                 max_generation, max_generation,
26196                                 &sc);
26197     }
26198
26199     {
26200         dprintf(3,("BGC: finalization marking"));
26201         finalize_queue->GcScanRoots(background_promote_callback, heap_number, 0);
26202     }
26203
26204     size_t total_loh_size = generation_size (max_generation + 1);
26205     bgc_begin_loh_size = total_loh_size;
26206     bgc_alloc_spin_loh = 0;
26207     bgc_loh_size_increased = 0;
26208     bgc_loh_allocated_in_free = 0;
26209     size_t total_soh_size = generation_sizes (generation_of (max_generation));
26210
26211     dprintf (GTC_LOG, ("BM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
26212
26213     {
26214         //concurrent_print_time_delta ("copying stack roots");
26215         concurrent_print_time_delta ("CS");
26216
26217         FIRE_EVENT(BGC1stNonConEnd);
26218
26219         expanded_in_fgc = FALSE;
26220         saved_overflow_ephemeral_seg = 0;
26221         current_bgc_state = bgc_reset_ww;
26222
26223         // we don't need a join here - just whichever thread that gets here
26224         // first can change the states and call restart_vm.
26225         // this is not true - we can't let the EE run when we are scanning stack.
26226         // since we now allow reset ww to run concurrently and have a join for it,
26227         // we can do restart ee on the 1st thread that got here. Make sure we handle the 
26228         // sizedref handles correctly.
26229 #ifdef MULTIPLE_HEAPS
26230         bgc_t_join.join(this, gc_join_restart_ee);
26231         if (bgc_t_join.joined())
26232 #endif //MULTIPLE_HEAPS
26233         {
26234 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26235             // Resetting write watch for software write watch is pretty fast, much faster than for hardware write watch. Reset
26236             // can be done while the runtime is suspended or after the runtime is restarted, the preference was to reset while
26237             // the runtime is suspended. The reset for hardware write watch is done after the runtime is restarted below.
26238 #ifdef WRITE_WATCH
26239             concurrent_print_time_delta ("CRWW begin");
26240
26241 #ifdef MULTIPLE_HEAPS
26242             for (int i = 0; i < n_heaps; i++)
26243             {
26244                 g_heaps[i]->reset_write_watch (FALSE);
26245             }
26246 #else
26247             reset_write_watch (FALSE);
26248 #endif //MULTIPLE_HEAPS
26249
26250             concurrent_print_time_delta ("CRWW");
26251 #endif //WRITE_WATCH
26252 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26253
26254             num_sizedrefs = GCToEEInterface::GetTotalNumSizedRefHandles();
26255
26256             // this c_write is not really necessary because restart_vm
26257             // has an instruction that will flush the cpu cache (interlocked
26258             // or whatever) but we don't want to rely on that.
26259             dprintf (BGC_LOG, ("setting cm_in_progress"));
26260             c_write (cm_in_progress, TRUE);
26261
26262             //restart all thread, doing the marking from the array
26263             assert (dont_restart_ee_p);
26264             dont_restart_ee_p = FALSE;
26265
26266             restart_vm();
26267             GCToOSInterface::YieldThread (0);
26268 #ifdef MULTIPLE_HEAPS
26269             dprintf(3, ("Starting all gc threads for gc"));
26270             bgc_t_join.restart();
26271 #endif //MULTIPLE_HEAPS
26272         }
26273
26274 #ifdef MULTIPLE_HEAPS
26275         bgc_t_join.join(this, gc_join_after_reset);
26276         if (bgc_t_join.joined())
26277 #endif //MULTIPLE_HEAPS
26278         {
26279             disable_preemptive (true);
26280
26281 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26282             // When software write watch is enabled, resetting write watch is done while the runtime is suspended above. The
26283             // post-reset call to revisit_written_pages is only necessary for concurrent reset_write_watch, to discard dirtied
26284             // pages during the concurrent reset.
26285
26286 #ifdef WRITE_WATCH
26287             concurrent_print_time_delta ("CRWW begin");
26288
26289 #ifdef MULTIPLE_HEAPS
26290             for (int i = 0; i < n_heaps; i++)
26291             {
26292                 g_heaps[i]->reset_write_watch (TRUE);
26293             }
26294 #else
26295             reset_write_watch (TRUE);
26296 #endif //MULTIPLE_HEAPS
26297
26298             concurrent_print_time_delta ("CRWW");
26299 #endif //WRITE_WATCH
26300
26301 #ifdef MULTIPLE_HEAPS
26302             for (int i = 0; i < n_heaps; i++)
26303             {
26304                 g_heaps[i]->revisit_written_pages (TRUE, TRUE);
26305             }
26306 #else
26307             revisit_written_pages (TRUE, TRUE);
26308 #endif //MULTIPLE_HEAPS
26309
26310             concurrent_print_time_delta ("CRW");
26311 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26312
26313 #ifdef MULTIPLE_HEAPS
26314             for (int i = 0; i < n_heaps; i++)
26315             {
26316                 g_heaps[i]->current_bgc_state = bgc_mark_handles;
26317             }
26318 #else
26319             current_bgc_state = bgc_mark_handles;
26320 #endif //MULTIPLE_HEAPS
26321
26322             current_c_gc_state = c_gc_state_marking;
26323
26324             enable_preemptive ();
26325
26326 #ifdef MULTIPLE_HEAPS
26327             dprintf(3, ("Joining BGC threads after resetting writewatch"));
26328             bgc_t_join.restart();
26329 #endif //MULTIPLE_HEAPS
26330         }
26331
26332         disable_preemptive (true);
26333
26334         if (num_sizedrefs > 0)
26335         {
26336             GCScan::GcScanSizedRefs(background_promote, max_generation, max_generation, &sc);
26337
26338             enable_preemptive ();
26339
26340 #ifdef MULTIPLE_HEAPS
26341             bgc_t_join.join(this, gc_join_scan_sizedref_done);
26342             if (bgc_t_join.joined())
26343             {
26344                 dprintf(3, ("Done with marking all sized refs. Starting all bgc thread for marking other strong roots"));
26345                 bgc_t_join.restart();
26346             }
26347 #endif //MULTIPLE_HEAPS
26348
26349             disable_preemptive (true);
26350         }
26351
26352         dprintf (3,("BGC: handle table marking"));
26353         GCScan::GcScanHandles(background_promote,
26354                                   max_generation, max_generation,
26355                                   &sc);
26356         //concurrent_print_time_delta ("concurrent marking handle table");
26357         concurrent_print_time_delta ("CRH");
26358
26359         current_bgc_state = bgc_mark_stack;
26360         dprintf (2,("concurrent draining mark list"));
26361         background_drain_mark_list (thread);
26362         //concurrent_print_time_delta ("concurrent marking stack roots");
26363         concurrent_print_time_delta ("CRS");
26364
26365         dprintf (2,("concurrent revisiting dirtied pages"));
26366         revisit_written_pages (TRUE);
26367         revisit_written_pages (TRUE);
26368         //concurrent_print_time_delta ("concurrent marking dirtied pages on LOH");
26369         concurrent_print_time_delta ("CRre");
26370
26371         enable_preemptive ();
26372
26373 #ifdef MULTIPLE_HEAPS
26374         bgc_t_join.join(this, gc_join_concurrent_overflow);
26375         if (bgc_t_join.joined())
26376         {
26377             uint8_t* all_heaps_max = 0;
26378             uint8_t* all_heaps_min = MAX_PTR;
26379             int i;
26380             for (i = 0; i < n_heaps; i++)
26381             {
26382                 dprintf (3, ("heap %d overflow max is %Ix, min is %Ix", 
26383                     i,
26384                     g_heaps[i]->background_max_overflow_address,
26385                     g_heaps[i]->background_min_overflow_address));
26386                 if (all_heaps_max < g_heaps[i]->background_max_overflow_address)
26387                     all_heaps_max = g_heaps[i]->background_max_overflow_address;
26388                 if (all_heaps_min > g_heaps[i]->background_min_overflow_address)
26389                     all_heaps_min = g_heaps[i]->background_min_overflow_address;
26390             }
26391             for (i = 0; i < n_heaps; i++)
26392             {
26393                 g_heaps[i]->background_max_overflow_address = all_heaps_max;
26394                 g_heaps[i]->background_min_overflow_address = all_heaps_min;
26395             }
26396             dprintf(3, ("Starting all bgc threads after updating the overflow info"));
26397             bgc_t_join.restart();
26398         }
26399 #endif //MULTIPLE_HEAPS
26400
26401         disable_preemptive (true);
26402
26403         dprintf (2, ("before CRov count: %d", bgc_overflow_count));
26404         bgc_overflow_count = 0;
26405         background_process_mark_overflow (TRUE);
26406         dprintf (2, ("after CRov count: %d", bgc_overflow_count));
26407         bgc_overflow_count = 0;
26408         //concurrent_print_time_delta ("concurrent processing mark overflow");
26409         concurrent_print_time_delta ("CRov");
26410
26411         // Stop all threads, crawl all stacks and revisit changed pages.
26412         FIRE_EVENT(BGC1stConEnd);
26413
26414         dprintf (2, ("Stopping the EE"));
26415
26416         enable_preemptive ();
26417
26418 #ifdef MULTIPLE_HEAPS
26419         bgc_t_join.join(this, gc_join_suspend_ee);
26420         if (bgc_t_join.joined())
26421         {
26422             bgc_threads_sync_event.Reset();
26423
26424             dprintf(3, ("Joining BGC threads for non concurrent final marking"));
26425             bgc_t_join.restart();
26426         }
26427 #endif //MULTIPLE_HEAPS
26428
26429         if (heap_number == 0)
26430         {
26431             enter_spin_lock (&gc_lock);
26432
26433             bgc_suspend_EE ();
26434             //suspend_EE ();
26435             bgc_threads_sync_event.Set();
26436         }
26437         else
26438         {
26439             bgc_threads_sync_event.Wait(INFINITE, FALSE);
26440             dprintf (2, ("bgc_threads_sync_event is signalled"));
26441         }
26442
26443         assert (settings.concurrent);
26444         assert (settings.condemned_generation == max_generation);
26445
26446         dprintf (2, ("clearing cm_in_progress"));
26447         c_write (cm_in_progress, FALSE);
26448
26449         bgc_alloc_lock->check();
26450
26451         current_bgc_state = bgc_final_marking;
26452
26453         //concurrent_print_time_delta ("concurrent marking ended");
26454         concurrent_print_time_delta ("CR");
26455
26456         FIRE_EVENT(BGC2ndNonConBegin);
26457
26458         mark_absorb_new_alloc();
26459
26460         // We need a join here 'cause find_object would complain if the gen0
26461         // bricks of another heap haven't been fixed up. So we need to make sure
26462         // that every heap's gen0 bricks are fixed up before we proceed.
26463 #ifdef MULTIPLE_HEAPS
26464         bgc_t_join.join(this, gc_join_after_absorb);
26465         if (bgc_t_join.joined())
26466         {
26467             dprintf(3, ("Joining BGC threads after absorb"));
26468             bgc_t_join.restart();
26469         }
26470 #endif //MULTIPLE_HEAPS
26471
26472         // give VM a chance to do work
26473         GCToEEInterface::GcBeforeBGCSweepWork();
26474
26475         //reset the flag, indicating that the EE no longer expect concurrent
26476         //marking
26477         sc.concurrent = FALSE;
26478
26479         total_loh_size = generation_size (max_generation + 1);
26480         total_soh_size = generation_sizes (generation_of (max_generation));
26481
26482         dprintf (GTC_LOG, ("FM: h%d: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
26483
26484         dprintf (2, ("nonconcurrent marking stack roots"));
26485         GCScan::GcScanRoots(background_promote,
26486                                 max_generation, max_generation,
26487                                 &sc);
26488         //concurrent_print_time_delta ("nonconcurrent marking stack roots");
26489         concurrent_print_time_delta ("NRS");
26490
26491 //        finalize_queue->EnterFinalizeLock();
26492         finalize_queue->GcScanRoots(background_promote, heap_number, 0);
26493 //        finalize_queue->LeaveFinalizeLock();
26494
26495         dprintf (2, ("nonconcurrent marking handle table"));
26496         GCScan::GcScanHandles(background_promote,
26497                                   max_generation, max_generation,
26498                                   &sc);
26499         //concurrent_print_time_delta ("nonconcurrent marking handle table");
26500         concurrent_print_time_delta ("NRH");
26501
26502         dprintf (2,("---- (GC%d)final going through written pages ----", VolatileLoad(&settings.gc_index)));
26503         revisit_written_pages (FALSE);
26504         //concurrent_print_time_delta ("nonconcurrent revisit dirtied pages on LOH");
26505         concurrent_print_time_delta ("NRre LOH");
26506
26507 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26508 #ifdef MULTIPLE_HEAPS
26509         bgc_t_join.join(this, gc_join_disable_software_write_watch);
26510         if (bgc_t_join.joined())
26511 #endif // MULTIPLE_HEAPS
26512         {
26513             // The runtime is suspended, and we will be doing a final query of dirty pages, so pause tracking written pages to
26514             // avoid further perf penalty after the runtime is restarted
26515             SoftwareWriteWatch::DisableForGCHeap();
26516
26517 #ifdef MULTIPLE_HEAPS
26518             dprintf(3, ("Restarting BGC threads after disabling software write watch"));
26519             bgc_t_join.restart();
26520 #endif // MULTIPLE_HEAPS
26521         }
26522 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26523
26524         dprintf (2, ("before NR 1st Hov count: %d", bgc_overflow_count));
26525         bgc_overflow_count = 0;
26526
26527         // Dependent handles need to be scanned with a special algorithm (see the header comment on
26528         // scan_dependent_handles for more detail). We perform an initial scan without processing any mark
26529         // stack overflow. This is not guaranteed to complete the operation but in a common case (where there
26530         // are no dependent handles that are due to be collected) it allows us to optimize away further scans.
26531         // The call to background_scan_dependent_handles is what will cycle through more iterations if
26532         // required and will also perform processing of any mark stack overflow once the dependent handle
26533         // table has been fully promoted.
26534         dprintf (2, ("1st dependent handle scan and process mark overflow"));
26535         GCScan::GcDhInitialScan(background_promote, max_generation, max_generation, &sc);
26536         background_scan_dependent_handles (&sc);
26537         //concurrent_print_time_delta ("1st nonconcurrent dependent handle scan and process mark overflow");
26538         concurrent_print_time_delta ("NR 1st Hov");
26539
26540         dprintf (2, ("after NR 1st Hov count: %d", bgc_overflow_count));
26541         bgc_overflow_count = 0;
26542
26543 #ifdef MULTIPLE_HEAPS
26544         bgc_t_join.join(this, gc_join_null_dead_short_weak);
26545         if (bgc_t_join.joined())
26546 #endif //MULTIPLE_HEAPS
26547         {
26548             GCToEEInterface::AfterGcScanRoots (max_generation, max_generation, &sc);
26549
26550 #ifdef MULTIPLE_HEAPS
26551             dprintf(3, ("Joining BGC threads for short weak handle scan"));
26552             bgc_t_join.restart();
26553 #endif //MULTIPLE_HEAPS
26554         }
26555
26556         // null out the target of short weakref that were not promoted.
26557         GCScan::GcShortWeakPtrScan(background_promote, max_generation, max_generation,&sc);
26558
26559         //concurrent_print_time_delta ("bgc GcShortWeakPtrScan");
26560         concurrent_print_time_delta ("NR GcShortWeakPtrScan");
26561     }
26562
26563     {
26564 #ifdef MULTIPLE_HEAPS
26565         bgc_t_join.join(this, gc_join_scan_finalization);
26566         if (bgc_t_join.joined())
26567         {
26568             dprintf(3, ("Joining BGC threads for finalization"));
26569             bgc_t_join.restart();
26570         }
26571 #endif //MULTIPLE_HEAPS
26572
26573         //Handle finalization.
26574         dprintf(3,("Marking finalization data"));
26575         //concurrent_print_time_delta ("bgc joined to mark finalization");
26576         concurrent_print_time_delta ("NRj");
26577
26578 //        finalize_queue->EnterFinalizeLock();
26579         finalize_queue->ScanForFinalization (background_promote, max_generation, FALSE, __this);
26580 //        finalize_queue->LeaveFinalizeLock();
26581
26582         concurrent_print_time_delta ("NRF");
26583     }
26584
26585     dprintf (2, ("before NR 2nd Hov count: %d", bgc_overflow_count));
26586     bgc_overflow_count = 0;
26587
26588     // Scan dependent handles again to promote any secondaries associated with primaries that were promoted
26589     // for finalization. As before background_scan_dependent_handles will also process any mark stack
26590     // overflow.
26591     dprintf (2, ("2nd dependent handle scan and process mark overflow"));
26592     background_scan_dependent_handles (&sc);
26593     //concurrent_print_time_delta ("2nd nonconcurrent dependent handle scan and process mark overflow");
26594     concurrent_print_time_delta ("NR 2nd Hov");
26595
26596 #ifdef MULTIPLE_HEAPS
26597     bgc_t_join.join(this, gc_join_null_dead_long_weak);
26598     if (bgc_t_join.joined())
26599     {
26600         dprintf(2, ("Joining BGC threads for weak pointer deletion"));
26601         bgc_t_join.restart();
26602     }
26603 #endif //MULTIPLE_HEAPS
26604
26605     // null out the target of long weakref that were not promoted.
26606     GCScan::GcWeakPtrScan (background_promote, max_generation, max_generation, &sc);
26607     concurrent_print_time_delta ("NR GcWeakPtrScan");
26608
26609 #ifdef MULTIPLE_HEAPS
26610     bgc_t_join.join(this, gc_join_null_dead_syncblk);
26611     if (bgc_t_join.joined())
26612 #endif //MULTIPLE_HEAPS
26613     {
26614         dprintf (2, ("calling GcWeakPtrScanBySingleThread"));
26615         // scan for deleted entries in the syncblk cache
26616         GCScan::GcWeakPtrScanBySingleThread (max_generation, max_generation, &sc);
26617         concurrent_print_time_delta ("NR GcWeakPtrScanBySingleThread");
26618 #ifdef MULTIPLE_HEAPS
26619         dprintf(2, ("Starting BGC threads for end of background mark phase"));
26620         bgc_t_join.restart();
26621 #endif //MULTIPLE_HEAPS
26622     }
26623
26624     gen0_bricks_cleared = FALSE;
26625
26626     dprintf (2, ("end of bgc mark: loh: %d, soh: %d", 
26627                  generation_size (max_generation + 1), 
26628                  generation_sizes (generation_of (max_generation))));
26629
26630     for (int gen_idx = max_generation; gen_idx <= (max_generation + 1); gen_idx++)
26631     {
26632         generation* gen = generation_of (gen_idx);
26633         dynamic_data* dd = dynamic_data_of (gen_idx);
26634         dd_begin_data_size (dd) = generation_size (gen_idx) - 
26635                                    (generation_free_list_space (gen) + generation_free_obj_space (gen)) -
26636                                    Align (size (generation_allocation_start (gen)));
26637         dd_survived_size (dd) = 0;
26638         dd_pinned_survived_size (dd) = 0;
26639         dd_artificial_pinned_survived_size (dd) = 0;
26640         dd_added_pinned_size (dd) = 0;
26641     }
26642
26643     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26644     PREFIX_ASSUME(seg != NULL);
26645
26646     while (seg)
26647     {
26648         seg->flags &= ~heap_segment_flags_swept;
26649
26650         if (heap_segment_allocated (seg) == heap_segment_mem (seg))
26651         {
26652             // This can't happen...
26653             FATAL_GC_ERROR();
26654         }
26655
26656         if (seg == ephemeral_heap_segment)
26657         {
26658             heap_segment_background_allocated (seg) = generation_allocation_start (generation_of (max_generation - 1));
26659         }
26660         else
26661         {
26662             heap_segment_background_allocated (seg) = heap_segment_allocated (seg);
26663         }
26664
26665         dprintf (2, ("seg %Ix background allocated is %Ix", 
26666                       heap_segment_mem (seg), 
26667                       heap_segment_background_allocated (seg)));
26668         seg = heap_segment_next_rw (seg);
26669     }
26670
26671     // We need to void alloc contexts here 'cause while background_ephemeral_sweep is running
26672     // we can't let the user code consume the left over parts in these alloc contexts.
26673     repair_allocation_contexts (FALSE);
26674
26675 #ifdef TIME_GC
26676         finish = GetCycleCount32();
26677         mark_time = finish - start;
26678 #endif //TIME_GC
26679
26680     dprintf (2, ("end of bgc mark: gen2 free list space: %d, free obj space: %d", 
26681         generation_free_list_space (generation_of (max_generation)), 
26682         generation_free_obj_space (generation_of (max_generation))));
26683
26684     dprintf(2,("---- (GC%d)End of background mark phase ----", VolatileLoad(&settings.gc_index)));
26685 }
26686
26687 void
26688 gc_heap::suspend_EE ()
26689 {
26690     dprintf (2, ("suspend_EE"));
26691 #ifdef MULTIPLE_HEAPS
26692     gc_heap* hp = gc_heap::g_heaps[0];
26693     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26694 #else
26695     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26696 #endif //MULTIPLE_HEAPS
26697 }
26698
26699 #ifdef MULTIPLE_HEAPS
26700 void
26701 gc_heap::bgc_suspend_EE ()
26702 {
26703     for (int i = 0; i < n_heaps; i++)
26704     {
26705         gc_heap::g_heaps[i]->reset_gc_done();
26706     }
26707     gc_started = TRUE;
26708     dprintf (2, ("bgc_suspend_EE"));
26709     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26710
26711     gc_started = FALSE;
26712     for (int i = 0; i < n_heaps; i++)
26713     {
26714         gc_heap::g_heaps[i]->set_gc_done();
26715     }
26716 }
26717 #else
26718 void
26719 gc_heap::bgc_suspend_EE ()
26720 {
26721     reset_gc_done();
26722     gc_started = TRUE;
26723     dprintf (2, ("bgc_suspend_EE"));
26724     GCToEEInterface::SuspendEE(SUSPEND_FOR_GC_PREP);
26725     gc_started = FALSE;
26726     set_gc_done();
26727 }
26728 #endif //MULTIPLE_HEAPS
26729
26730 void
26731 gc_heap::restart_EE ()
26732 {
26733     dprintf (2, ("restart_EE"));
26734 #ifdef MULTIPLE_HEAPS
26735     GCToEEInterface::RestartEE(FALSE);
26736 #else
26737     GCToEEInterface::RestartEE(FALSE);
26738 #endif //MULTIPLE_HEAPS
26739 }
26740
26741 inline uint8_t* gc_heap::high_page ( heap_segment* seg, BOOL concurrent_p)
26742 {
26743     if (concurrent_p)
26744     {
26745         uint8_t* end = ((seg == ephemeral_heap_segment) ?
26746                      generation_allocation_start (generation_of (max_generation-1)) :
26747                      heap_segment_allocated (seg));
26748         return align_lower_page (end);
26749     }
26750     else 
26751     {
26752         return heap_segment_allocated (seg);
26753     }
26754 }
26755
26756 void gc_heap::revisit_written_page (uint8_t* page,
26757                                     uint8_t* end,
26758                                     BOOL concurrent_p,
26759                                     heap_segment* seg,
26760                                     uint8_t*& last_page,
26761                                     uint8_t*& last_object,
26762                                     BOOL large_objects_p,
26763                                     size_t& num_marked_objects)
26764 {
26765     UNREFERENCED_PARAMETER(seg);
26766
26767     uint8_t*   start_address = page;
26768     uint8_t*   o             = 0;
26769     int align_const = get_alignment_constant (!large_objects_p);
26770     uint8_t* high_address = end;
26771     uint8_t* current_lowest_address = background_saved_lowest_address;
26772     uint8_t* current_highest_address = background_saved_highest_address;
26773     BOOL no_more_loop_p = FALSE;
26774
26775     THREAD_FROM_HEAP;
26776 #ifndef MULTIPLE_HEAPS
26777     const int thread = heap_number;
26778 #endif //!MULTIPLE_HEAPS
26779
26780     if (large_objects_p)
26781     {
26782         o = last_object;
26783     }
26784     else
26785     {
26786         if (((last_page + WRITE_WATCH_UNIT_SIZE) == page)
26787             || (start_address <= last_object))
26788         {
26789             o = last_object;
26790         }
26791         else
26792         {
26793             o = find_first_object (start_address, last_object);
26794             // We can visit the same object again, but on a different page.
26795             assert (o >= last_object);
26796         }
26797     }
26798
26799     dprintf (3,("page %Ix start: %Ix, %Ix[ ",
26800                (size_t)page, (size_t)o,
26801                (size_t)(min (high_address, page + WRITE_WATCH_UNIT_SIZE))));
26802
26803     while (o < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26804     {
26805         size_t s;
26806
26807         if (concurrent_p && large_objects_p)
26808         {
26809             bgc_alloc_lock->bgc_mark_set (o);
26810
26811             if (((CObjectHeader*)o)->IsFree())
26812             {
26813                 s = unused_array_size (o);
26814             }
26815             else
26816             {
26817                 s = size (o);
26818             }
26819         }
26820         else
26821         {
26822             s = size (o);
26823         }
26824
26825         dprintf (3,("Considering object %Ix(%s)", (size_t)o, (background_object_marked (o, FALSE) ? "bm" : "nbm")));
26826
26827         assert (Align (s) >= Align (min_obj_size));
26828
26829         uint8_t* next_o =  o + Align (s, align_const);
26830
26831         if (next_o >= start_address) 
26832         {
26833 #ifdef MULTIPLE_HEAPS
26834             if (concurrent_p)
26835             {
26836                 // We set last_object here for SVR BGC here because SVR BGC has more than 
26837                 // one GC thread. When we have more than one GC thread we would run into this 
26838                 // situation if we skipped unmarked objects:
26839                 // bgc thread 1 calls GWW, and detect object X not marked so it would skip it 
26840                 // for revisit. 
26841                 // bgc thread 2 marks X and all its current children.
26842                 // user thread comes along and dirties more (and later) pages in X.
26843                 // bgc thread 1 calls GWW again and gets those later pages but it will not mark anything
26844                 // on them because it had already skipped X. We need to detect that this object is now
26845                 // marked and mark the children on the dirtied pages.
26846                 // In the future if we have less BGC threads than we have heaps we should add
26847                 // the check to the number of BGC threads.
26848                 last_object = o;
26849             }
26850 #endif //MULTIPLE_HEAPS
26851
26852             if (contain_pointers (o) &&
26853                 (!((o >= current_lowest_address) && (o < current_highest_address)) ||
26854                 background_marked (o)))
26855             {
26856                 dprintf (3, ("going through %Ix", (size_t)o));
26857                 go_through_object (method_table(o), o, s, poo, start_address, use_start, (o + s),
26858                                     if ((uint8_t*)poo >= min (high_address, page + WRITE_WATCH_UNIT_SIZE))
26859                                     {
26860                                         no_more_loop_p = TRUE;
26861                                         goto end_limit;
26862                                     }
26863                                     uint8_t* oo = *poo;
26864
26865                                     num_marked_objects++;
26866                                     background_mark_object (oo THREAD_NUMBER_ARG);
26867                                 );
26868             }
26869             else if (
26870                 concurrent_p &&
26871 #ifndef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP // see comment below
26872                 large_objects_p &&
26873 #endif // !FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
26874                 ((CObjectHeader*)o)->IsFree() &&
26875                 (next_o > min (high_address, page + WRITE_WATCH_UNIT_SIZE)))
26876             {
26877                 // We need to not skip the object here because of this corner scenario:
26878                 // A large object was being allocated during BGC mark so we first made it 
26879                 // into a free object, then cleared its memory. In this loop we would detect
26880                 // that it's a free object which normally we would skip. But by the next time
26881                 // we call GetWriteWatch we could still be on this object and the object had
26882                 // been made into a valid object and some of its memory was changed. We need
26883                 // to be sure to process those written pages so we can't skip the object just
26884                 // yet.
26885                 //
26886                 // Similarly, when using software write watch, don't advance last_object when
26887                 // the current object is a free object that spans beyond the current page or
26888                 // high_address. Software write watch acquires gc_lock before the concurrent
26889                 // GetWriteWatch() call during revisit_written_pages(). A foreground GC may
26890                 // happen at that point and allocate from this free region, so when
26891                 // revisit_written_pages() continues, it cannot skip now-valid objects in this
26892                 // region.
26893                 no_more_loop_p = TRUE;
26894                 goto end_limit;                
26895             }
26896         }
26897 end_limit:
26898         if (concurrent_p && large_objects_p)
26899         {
26900             bgc_alloc_lock->bgc_mark_done ();
26901         }
26902         if (no_more_loop_p)
26903         {
26904             break;
26905         }
26906         o = next_o;
26907     }
26908
26909 #ifdef MULTIPLE_HEAPS
26910     if (concurrent_p)
26911     {
26912         assert (last_object < (min (high_address, page + WRITE_WATCH_UNIT_SIZE)));
26913     }
26914     else
26915 #endif //MULTIPLE_HEAPS
26916     {
26917         last_object = o;
26918     }
26919
26920     dprintf (3,("Last object: %Ix", (size_t)last_object));
26921     last_page = align_write_watch_lower_page (o);
26922 }
26923
26924 // When reset_only_p is TRUE, we should only reset pages that are in range
26925 // because we need to consider the segments or part of segments that were
26926 // allocated out of range all live.
26927 void gc_heap::revisit_written_pages (BOOL concurrent_p, BOOL reset_only_p)
26928 {
26929 #ifdef WRITE_WATCH
26930     if (concurrent_p && !reset_only_p)
26931     {
26932         current_bgc_state = bgc_revisit_soh;
26933     }
26934
26935     size_t total_dirtied_pages = 0;
26936     size_t total_marked_objects = 0;
26937
26938     heap_segment* seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
26939
26940     PREFIX_ASSUME(seg != NULL);
26941
26942     bool reset_watch_state = !!concurrent_p;
26943     bool is_runtime_suspended = !concurrent_p;
26944     BOOL small_object_segments = TRUE;
26945     int align_const = get_alignment_constant (small_object_segments);
26946
26947     while (1)
26948     {
26949         if (seg == 0)
26950         {
26951             if (small_object_segments)
26952             {
26953                 //switch to large segment
26954                 if (concurrent_p && !reset_only_p)
26955                 {
26956                     current_bgc_state = bgc_revisit_loh;
26957                 }
26958
26959                 if (!reset_only_p)
26960                 {
26961                     dprintf (GTC_LOG, ("h%d: SOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26962                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26963                     concurrent_print_time_delta (concurrent_p ? "CR SOH" : "NR SOH");
26964                     total_dirtied_pages = 0;
26965                     total_marked_objects = 0;
26966                 }
26967
26968                 small_object_segments = FALSE;
26969                 //concurrent_print_time_delta (concurrent_p ? "concurrent marking dirtied pages on SOH" : "nonconcurrent marking dirtied pages on SOH");
26970
26971                 dprintf (3, ("now revisiting large object segments"));
26972                 align_const = get_alignment_constant (small_object_segments);
26973                 seg = heap_segment_rw (generation_start_segment (large_object_generation));
26974
26975                 PREFIX_ASSUME(seg != NULL);
26976
26977                 continue;
26978             }
26979             else
26980             {
26981                 if (reset_only_p)
26982                 {
26983                     dprintf (GTC_LOG, ("h%d: tdp: %Id", heap_number, total_dirtied_pages));
26984                 } 
26985                 else
26986                 {
26987                     dprintf (GTC_LOG, ("h%d: LOH: dp:%Id; mo: %Id", heap_number, total_dirtied_pages, total_marked_objects));
26988                     fire_revisit_event (total_dirtied_pages, total_marked_objects, !small_object_segments);
26989                 }
26990                 break;
26991             }
26992         }
26993         uint8_t* base_address = (uint8_t*)heap_segment_mem (seg);
26994         //we need to truncate to the base of the page because
26995         //some newly allocated could exist beyond heap_segment_allocated
26996         //and if we reset the last page write watch status,
26997         // they wouldn't be guaranteed to be visited -> gc hole.
26998         uintptr_t bcount = array_size;
26999         uint8_t* last_page = 0;
27000         uint8_t* last_object = heap_segment_mem (seg);
27001         uint8_t* high_address = 0;
27002
27003         BOOL skip_seg_p = FALSE;
27004
27005         if (reset_only_p)
27006         {
27007             if ((heap_segment_mem (seg) >= background_saved_lowest_address) ||
27008                 (heap_segment_reserved (seg) <= background_saved_highest_address))
27009             {
27010                 dprintf (3, ("h%d: sseg: %Ix(-%Ix)", heap_number, 
27011                     heap_segment_mem (seg), heap_segment_reserved (seg)));
27012                 skip_seg_p = TRUE;
27013             }
27014         }
27015
27016         if (!skip_seg_p)
27017         {
27018             dprintf (3, ("looking at seg %Ix", (size_t)last_object));
27019
27020             if (reset_only_p)
27021             {
27022                 base_address = max (base_address, background_saved_lowest_address);
27023                 dprintf (3, ("h%d: reset only starting %Ix", heap_number, base_address));
27024             }
27025
27026             dprintf (3, ("h%d: starting: %Ix, seg %Ix-%Ix", heap_number, base_address, 
27027                 heap_segment_mem (seg), heap_segment_reserved (seg)));
27028
27029
27030             while (1)
27031             {
27032                 if (reset_only_p)
27033                 {
27034                     high_address = ((seg == ephemeral_heap_segment) ? alloc_allocated : heap_segment_allocated (seg));
27035                     high_address = min (high_address, background_saved_highest_address);
27036                 }
27037                 else
27038                 {
27039                     high_address = high_page (seg, concurrent_p);
27040                 }
27041
27042                 if ((base_address < high_address) &&
27043                     (bcount >= array_size))
27044                 {
27045                     ptrdiff_t region_size = high_address - base_address;
27046                     dprintf (3, ("h%d: gw: [%Ix(%Id)", heap_number, (size_t)base_address, (size_t)region_size));
27047
27048 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
27049                     // When the runtime is not suspended, it's possible for the table to be resized concurrently with the scan
27050                     // for dirty pages below. Prevent that by synchronizing with grow_brick_card_tables(). When the runtime is
27051                     // suspended, it's ok to scan for dirty pages concurrently from multiple background GC threads for disjoint
27052                     // memory regions.
27053                     if (!is_runtime_suspended)
27054                     {
27055                         enter_spin_lock(&gc_lock);
27056                     }
27057 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
27058
27059                     get_write_watch_for_gc_heap (reset_watch_state, base_address, region_size,
27060                                                  (void**)background_written_addresses,
27061                                                  &bcount, is_runtime_suspended);
27062
27063 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
27064                     if (!is_runtime_suspended)
27065                     {
27066                         leave_spin_lock(&gc_lock);
27067                     }
27068 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
27069
27070                     if (bcount != 0)
27071                     {
27072                         total_dirtied_pages += bcount;
27073
27074                         dprintf (3, ("Found %d pages [%Ix, %Ix[", 
27075                                         bcount, (size_t)base_address, (size_t)high_address));
27076                     }
27077
27078                     if (!reset_only_p)
27079                     {
27080                         for (unsigned i = 0; i < bcount; i++)
27081                         {
27082                             uint8_t* page = (uint8_t*)background_written_addresses[i];
27083                             dprintf (3, ("looking at page %d at %Ix(h: %Ix)", i, 
27084                                 (size_t)page, (size_t)high_address));
27085                             if (page < high_address)
27086                             {
27087                                 //search for marked objects in the page
27088                                 revisit_written_page (page, high_address, concurrent_p,
27089                                                     seg, last_page, last_object,
27090                                                     !small_object_segments,
27091                                                     total_marked_objects);
27092                             }
27093                             else
27094                             {
27095                                 dprintf (3, ("page %d at %Ix is >= %Ix!", i, (size_t)page, (size_t)high_address));
27096                                 assert (!"page shouldn't have exceeded limit");
27097                             }
27098                         }
27099                     }
27100
27101                     if (bcount >= array_size){
27102                         base_address = background_written_addresses [array_size-1] + WRITE_WATCH_UNIT_SIZE;
27103                         bcount = array_size;
27104                     }
27105                 }
27106                 else
27107                 {
27108                     break;
27109                 }
27110             }
27111         }
27112
27113         seg = heap_segment_next_rw (seg);
27114     }
27115
27116 #endif //WRITE_WATCH
27117 }
27118
27119 void gc_heap::background_grow_c_mark_list()
27120 {
27121     assert (c_mark_list_index >= c_mark_list_length);
27122     BOOL should_drain_p = FALSE;
27123     THREAD_FROM_HEAP;
27124 #ifndef MULTIPLE_HEAPS
27125     const int thread = heap_number;
27126 #endif //!MULTIPLE_HEAPS
27127
27128     dprintf (2, ("stack copy buffer overflow"));
27129     uint8_t** new_c_mark_list = 0;
27130     {
27131         FAULT_NOT_FATAL();
27132         if (c_mark_list_length >= (SIZE_T_MAX / (2 * sizeof (uint8_t*))))
27133         {
27134             should_drain_p = TRUE;
27135         }
27136         else
27137         {
27138             new_c_mark_list = new (nothrow) uint8_t*[c_mark_list_length*2];
27139             if (new_c_mark_list == 0)
27140             {
27141                 should_drain_p = TRUE;
27142             }
27143         }
27144     }
27145     if (should_drain_p)
27146
27147     {
27148         dprintf (2, ("No more memory for the stacks copy, draining.."));
27149         //drain the list by marking its elements
27150         background_drain_mark_list (thread);
27151     }
27152     else
27153     {
27154         assert (new_c_mark_list);
27155         memcpy (new_c_mark_list, c_mark_list, c_mark_list_length*sizeof(uint8_t*));
27156         c_mark_list_length = c_mark_list_length*2;
27157         delete c_mark_list;
27158         c_mark_list = new_c_mark_list;
27159     }
27160 }
27161
27162 void gc_heap::background_promote_callback (Object** ppObject, ScanContext* sc,
27163                                   uint32_t flags)
27164 {
27165     UNREFERENCED_PARAMETER(sc);
27166     //in order to save space on the array, mark the object,
27167     //knowing that it will be visited later
27168     assert (settings.concurrent);
27169
27170     THREAD_NUMBER_FROM_CONTEXT;
27171 #ifndef MULTIPLE_HEAPS
27172     const int thread = 0;
27173 #endif //!MULTIPLE_HEAPS
27174
27175     uint8_t* o = (uint8_t*)*ppObject;
27176
27177     if (o == 0)
27178         return;
27179
27180     HEAP_FROM_THREAD;
27181
27182     gc_heap* hp = gc_heap::heap_of (o);
27183
27184     if ((o < hp->background_saved_lowest_address) || (o >= hp->background_saved_highest_address))
27185     {
27186         return;
27187     }
27188
27189 #ifdef INTERIOR_POINTERS
27190     if (flags & GC_CALL_INTERIOR)
27191     {
27192         o = hp->find_object (o, hp->background_saved_lowest_address);
27193         if (o == 0)
27194             return;
27195     }
27196 #endif //INTERIOR_POINTERS
27197
27198 #ifdef FEATURE_CONSERVATIVE_GC
27199     // For conservative GC, a value on stack may point to middle of a free object.
27200     // In this case, we don't need to promote the pointer.
27201     if (GCConfig::GetConservativeGC() && ((CObjectHeader*)o)->IsFree())
27202     {
27203         return;
27204     }
27205 #endif //FEATURE_CONSERVATIVE_GC
27206
27207 #ifdef _DEBUG
27208     ((CObjectHeader*)o)->Validate();
27209 #endif //_DEBUG
27210
27211     dprintf (3, ("Concurrent Background Promote %Ix", (size_t)o));
27212     if (o && (size (o) > loh_size_threshold))
27213     {
27214         dprintf (3, ("Brc %Ix", (size_t)o));
27215     }
27216
27217     if (hpt->c_mark_list_index >= hpt->c_mark_list_length)
27218     {
27219         hpt->background_grow_c_mark_list();
27220     }
27221     dprintf (3, ("pushing %08x into mark_list", (size_t)o));
27222     hpt->c_mark_list [hpt->c_mark_list_index++] = o;
27223
27224     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);
27225 }
27226
27227 void gc_heap::mark_absorb_new_alloc()
27228 {
27229     fix_allocation_contexts (FALSE);
27230     
27231     gen0_bricks_cleared = FALSE;
27232
27233     clear_gen0_bricks();
27234 }
27235
27236 BOOL gc_heap::prepare_bgc_thread(gc_heap* gh)
27237 {
27238     BOOL success = FALSE;
27239     BOOL thread_created = FALSE;
27240     dprintf (2, ("Preparing gc thread"));
27241     gh->bgc_threads_timeout_cs.Enter();
27242     if (!(gh->bgc_thread_running))
27243     {
27244         dprintf (2, ("GC thread not runnning"));
27245         if ((gh->bgc_thread == 0) && create_bgc_thread(gh))
27246         {
27247             success = TRUE;
27248             thread_created = TRUE;
27249         }
27250     }
27251     else
27252     {
27253         dprintf (3, ("GC thread already running"));
27254         success = TRUE;
27255     }
27256     gh->bgc_threads_timeout_cs.Leave();
27257
27258     if(thread_created)
27259         FIRE_EVENT(GCCreateConcurrentThread_V1);
27260
27261     return success;
27262 }
27263
27264 BOOL gc_heap::create_bgc_thread(gc_heap* gh)
27265 {
27266     assert (background_gc_done_event.IsValid());
27267
27268     //dprintf (2, ("Creating BGC thread"));
27269
27270     gh->bgc_thread_running = GCToEEInterface::CreateThread(gh->bgc_thread_stub, gh, true, ".NET Background GC");
27271     return gh->bgc_thread_running;
27272 }
27273
27274 BOOL gc_heap::create_bgc_threads_support (int number_of_heaps)
27275 {
27276     BOOL ret = FALSE;
27277     dprintf (3, ("Creating concurrent GC thread for the first time"));
27278     if (!background_gc_done_event.CreateManualEventNoThrow(TRUE))
27279     {
27280         goto cleanup;
27281     }
27282     if (!bgc_threads_sync_event.CreateManualEventNoThrow(FALSE))
27283     {
27284         goto cleanup;
27285     }
27286     if (!ee_proceed_event.CreateAutoEventNoThrow(FALSE))
27287     {
27288         goto cleanup;
27289     }
27290     if (!bgc_start_event.CreateManualEventNoThrow(FALSE))
27291     {
27292         goto cleanup;
27293     }
27294
27295 #ifdef MULTIPLE_HEAPS
27296     bgc_t_join.init (number_of_heaps, join_flavor_bgc);
27297 #else
27298     UNREFERENCED_PARAMETER(number_of_heaps);
27299 #endif //MULTIPLE_HEAPS
27300
27301     ret = TRUE;
27302
27303 cleanup:
27304
27305     if (!ret)
27306     {
27307         if (background_gc_done_event.IsValid())
27308         {
27309             background_gc_done_event.CloseEvent();
27310         }
27311         if (bgc_threads_sync_event.IsValid())
27312         {
27313             bgc_threads_sync_event.CloseEvent();
27314         }
27315         if (ee_proceed_event.IsValid())
27316         {
27317             ee_proceed_event.CloseEvent();
27318         }
27319         if (bgc_start_event.IsValid())
27320         {
27321             bgc_start_event.CloseEvent();
27322         }
27323     }
27324
27325     return ret;
27326 }
27327
27328 BOOL gc_heap::create_bgc_thread_support()
27329 {
27330     BOOL ret = FALSE;
27331     uint8_t** parr;
27332     
27333     if (!gc_lh_block_event.CreateManualEventNoThrow(FALSE))
27334     {
27335         goto cleanup;
27336     }
27337
27338     //needs to have room for enough smallest objects fitting on a page
27339     parr = new (nothrow) uint8_t*[1 + OS_PAGE_SIZE / MIN_OBJECT_SIZE];
27340     if (!parr)
27341     {
27342         goto cleanup;
27343     }
27344
27345     make_c_mark_list (parr);
27346
27347     ret = TRUE;
27348
27349 cleanup:
27350
27351     if (!ret)
27352     {
27353         if (gc_lh_block_event.IsValid())
27354         {
27355             gc_lh_block_event.CloseEvent();
27356         }
27357     }
27358
27359     return ret;
27360 }
27361
27362 int gc_heap::check_for_ephemeral_alloc()
27363 {
27364     int gen = ((settings.reason == reason_oos_soh) ? (max_generation - 1) : -1);
27365
27366     if (gen == -1)
27367     {
27368 #ifdef MULTIPLE_HEAPS
27369         for (int heap_index = 0; heap_index < n_heaps; heap_index++)
27370 #endif //MULTIPLE_HEAPS
27371         {
27372             for (int i = 0; i <= (max_generation - 1); i++)
27373             {
27374 #ifdef MULTIPLE_HEAPS
27375                 if (g_heaps[heap_index]->get_new_allocation (i) <= 0)
27376 #else
27377                 if (get_new_allocation (i) <= 0)
27378 #endif //MULTIPLE_HEAPS
27379                 {
27380                     gen = max (gen, i);
27381                 }
27382                 else
27383                     break;
27384             }
27385         }
27386     }
27387
27388     return gen;
27389 }
27390
27391 // Wait for gc to finish sequential part
27392 void gc_heap::wait_to_proceed()
27393 {
27394     assert (background_gc_done_event.IsValid());
27395     assert (bgc_start_event.IsValid());
27396
27397     user_thread_wait(&ee_proceed_event, FALSE);
27398 }
27399
27400 // Start a new concurrent gc
27401 void gc_heap::start_c_gc()
27402 {
27403     assert (background_gc_done_event.IsValid());
27404     assert (bgc_start_event.IsValid());
27405
27406 //Need to make sure that the gc thread is in the right place.
27407     background_gc_done_event.Wait(INFINITE, FALSE);
27408     background_gc_done_event.Reset();
27409     bgc_start_event.Set();
27410 }
27411
27412 void gc_heap::do_background_gc()
27413 {
27414     dprintf (2, ("starting a BGC"));
27415 #ifdef MULTIPLE_HEAPS
27416     for (int i = 0; i < n_heaps; i++)
27417     {
27418         g_heaps[i]->init_background_gc();
27419     }
27420 #else
27421     init_background_gc();
27422 #endif //MULTIPLE_HEAPS
27423     //start the background gc
27424     start_c_gc ();
27425
27426     //wait until we get restarted by the BGC.
27427     wait_to_proceed();
27428 }
27429
27430 void gc_heap::kill_gc_thread()
27431 {
27432     //assert (settings.concurrent == FALSE);
27433
27434     // We are doing a two-stage shutdown now.
27435     // In the first stage, we do minimum work, and call ExitProcess at the end.
27436     // In the secodn stage, we have the Loader lock and only one thread is
27437     // alive.  Hence we do not need to kill gc thread.
27438     background_gc_done_event.CloseEvent();
27439     gc_lh_block_event.CloseEvent();
27440     bgc_start_event.CloseEvent();
27441     bgc_threads_timeout_cs.Destroy();
27442     bgc_thread = 0;
27443     recursive_gc_sync::shutdown();
27444 }
27445
27446 void gc_heap::bgc_thread_function()
27447 {
27448     assert (background_gc_done_event.IsValid());
27449     assert (bgc_start_event.IsValid());
27450
27451     dprintf (3, ("gc_thread thread starting..."));
27452
27453     BOOL do_exit = FALSE;
27454
27455     bool cooperative_mode = true;
27456     bgc_thread_id.SetToCurrentThread();
27457     dprintf (1, ("bgc_thread_id is set to %x", (uint32_t)GCToOSInterface::GetCurrentThreadIdForLogging()));
27458     while (1)
27459     {
27460         // Wait for work to do...
27461         dprintf (3, ("bgc thread: waiting..."));
27462
27463         cooperative_mode = enable_preemptive ();
27464         //current_thread->m_fPreemptiveGCDisabled = 0;
27465
27466         uint32_t result = bgc_start_event.Wait(
27467 #ifdef _DEBUG
27468 #ifdef MULTIPLE_HEAPS
27469                                              INFINITE,
27470 #else
27471                                              2000,
27472 #endif //MULTIPLE_HEAPS
27473 #else //_DEBUG
27474 #ifdef MULTIPLE_HEAPS
27475                                              INFINITE,
27476 #else
27477                                              20000,
27478 #endif //MULTIPLE_HEAPS
27479 #endif //_DEBUG
27480             FALSE);
27481         dprintf (2, ("gc thread: finished waiting"));
27482
27483         // not calling disable_preemptive here 'cause we 
27484         // can't wait for GC complete here - RestartEE will be called 
27485         // when we've done the init work.
27486
27487         if (result == WAIT_TIMEOUT)
27488         {
27489             // Should join the bgc threads and terminate all of them
27490             // at once.
27491             dprintf (1, ("GC thread timeout"));
27492             bgc_threads_timeout_cs.Enter();
27493             if (!keep_bgc_threads_p)
27494             {
27495                 dprintf (2, ("GC thread exiting"));
27496                 bgc_thread_running = FALSE;
27497                 bgc_thread = 0;
27498                 bgc_thread_id.Clear();
27499                 do_exit = TRUE;
27500             }
27501             bgc_threads_timeout_cs.Leave();
27502             if (do_exit)
27503                 break;
27504             else
27505             {
27506                 dprintf (3, ("GC thread needed, not exiting"));
27507                 continue;
27508             }
27509         }
27510         // if we signal the thread with no concurrent work to do -> exit
27511         if (!settings.concurrent)
27512         {
27513             dprintf (3, ("no concurrent GC needed, exiting"));
27514             break;
27515         }
27516 #ifdef TRACE_GC
27517         //trace_gc = TRUE;
27518 #endif //TRACE_GC
27519         recursive_gc_sync::begin_background();
27520         dprintf (2, ("beginning of bgc: gen2 FL: %d, FO: %d, frag: %d", 
27521             generation_free_list_space (generation_of (max_generation)),
27522             generation_free_obj_space (generation_of (max_generation)),
27523             dd_fragmentation (dynamic_data_of (max_generation))));
27524
27525         gc1();
27526
27527         current_bgc_state = bgc_not_in_process;
27528
27529 #ifdef TRACE_GC
27530         //trace_gc = FALSE;
27531 #endif //TRACE_GC
27532
27533         enable_preemptive ();
27534 #ifdef MULTIPLE_HEAPS
27535         bgc_t_join.join(this, gc_join_done);
27536         if (bgc_t_join.joined())
27537 #endif //MULTIPLE_HEAPS
27538         {
27539             enter_spin_lock (&gc_lock);
27540             dprintf (SPINLOCK_LOG, ("bgc Egc"));
27541             
27542             bgc_start_event.Reset();
27543             do_post_gc();
27544 #ifdef MULTIPLE_HEAPS
27545             for (int gen = max_generation; gen <= (max_generation + 1); gen++)
27546             {
27547                 size_t desired_per_heap = 0;
27548                 size_t total_desired = 0;
27549                 gc_heap* hp = 0;
27550                 dynamic_data* dd;
27551                 for (int i = 0; i < n_heaps; i++)
27552                 {
27553                     hp = g_heaps[i];
27554                     dd = hp->dynamic_data_of (gen);
27555                     size_t temp_total_desired = total_desired + dd_desired_allocation (dd);
27556                     if (temp_total_desired < total_desired)
27557                     {
27558                         // we overflowed.
27559                         total_desired = (size_t)MAX_PTR;
27560                         break;
27561                     }
27562                     total_desired = temp_total_desired;
27563                 }
27564
27565                 desired_per_heap = Align ((total_desired/n_heaps), get_alignment_constant (FALSE));
27566
27567                 for (int i = 0; i < n_heaps; i++)
27568                 {
27569                     hp = gc_heap::g_heaps[i];
27570                     dd = hp->dynamic_data_of (gen);
27571                     dd_desired_allocation (dd) = desired_per_heap;
27572                     dd_gc_new_allocation (dd) = desired_per_heap;
27573                     dd_new_allocation (dd) = desired_per_heap;
27574                 }
27575             }
27576 #endif //MULTIPLE_HEAPS
27577 #ifdef MULTIPLE_HEAPS
27578             fire_pevents();
27579 #endif //MULTIPLE_HEAPS
27580
27581             c_write (settings.concurrent, FALSE);
27582             recursive_gc_sync::end_background();
27583             keep_bgc_threads_p = FALSE;
27584             background_gc_done_event.Set();
27585
27586             dprintf (SPINLOCK_LOG, ("bgc Lgc"));
27587             leave_spin_lock (&gc_lock);
27588 #ifdef MULTIPLE_HEAPS
27589             dprintf(1, ("End of BGC - starting all BGC threads"));
27590             bgc_t_join.restart();
27591 #endif //MULTIPLE_HEAPS
27592         }
27593         // We can't disable preempt here because there might've been a GC already
27594         // started and decided to do a BGC and waiting for a BGC thread to restart 
27595         // vm. That GC will be waiting in wait_to_proceed and we are waiting for it
27596         // to restart the VM so we deadlock.
27597         //gc_heap::disable_preemptive (true);
27598     }
27599
27600     FIRE_EVENT(GCTerminateConcurrentThread_V1);
27601
27602     dprintf (3, ("bgc_thread thread exiting"));
27603     return;
27604 }
27605
27606 #endif //BACKGROUND_GC
27607
27608 //Clear the cards [start_card, end_card[
27609 void gc_heap::clear_cards (size_t start_card, size_t end_card)
27610 {
27611     if (start_card < end_card)
27612     {
27613         size_t start_word = card_word (start_card);
27614         size_t end_word = card_word (end_card);
27615         if (start_word < end_word)
27616         {
27617             // Figure out the bit positions of the cards within their words
27618             unsigned bits = card_bit (start_card);
27619             card_table [start_word] &= lowbits (~0, bits);
27620             for (size_t i = start_word+1; i < end_word; i++)
27621                 card_table [i] = 0;
27622             bits = card_bit (end_card);
27623             // Don't write beyond end_card (and possibly uncommitted card table space).
27624             if (bits != 0)
27625             {
27626                 card_table [end_word] &= highbits (~0, bits);
27627             }
27628         }
27629         else
27630         {
27631             // If the start and end cards are in the same word, just clear the appropriate card
27632             // bits in that word.
27633             card_table [start_word] &= (lowbits (~0, card_bit (start_card)) |
27634                                         highbits (~0, card_bit (end_card)));
27635         }
27636 #ifdef VERYSLOWDEBUG
27637         size_t  card = start_card;
27638         while (card < end_card)
27639         {
27640             assert (! (card_set_p (card)));
27641             card++;
27642         }
27643 #endif //VERYSLOWDEBUG
27644         dprintf (3,("Cleared cards [%Ix:%Ix, %Ix:%Ix[",
27645                   start_card, (size_t)card_address (start_card),
27646                   end_card, (size_t)card_address (end_card)));
27647     }
27648 }
27649
27650 void gc_heap::clear_card_for_addresses (uint8_t* start_address, uint8_t* end_address)
27651 {
27652     size_t   start_card = card_of (align_on_card (start_address));
27653     size_t   end_card = card_of (align_lower_card (end_address));
27654     clear_cards (start_card, end_card);
27655 }
27656
27657 // copy [srccard, ...[ to [dst_card, end_card[
27658 // This will set the same bit twice. Can be optimized.
27659 inline
27660 void gc_heap::copy_cards (size_t dst_card,
27661                           size_t src_card,
27662                           size_t end_card, 
27663                           BOOL nextp)
27664 {
27665     // If the range is empty, this function is a no-op - with the subtlety that
27666     // either of the accesses card_table[srcwrd] or card_table[dstwrd] could be
27667     // outside the committed region.  To avoid the access, leave early.
27668     if (!(dst_card < end_card))
27669         return;
27670
27671     unsigned int srcbit = card_bit (src_card);
27672     unsigned int dstbit = card_bit (dst_card);
27673     size_t srcwrd = card_word (src_card);
27674     size_t dstwrd = card_word (dst_card);
27675     unsigned int srctmp = card_table[srcwrd];
27676     unsigned int dsttmp = card_table[dstwrd];
27677
27678     for (size_t card = dst_card; card < end_card; card++)
27679     {
27680         if (srctmp & (1 << srcbit))
27681             dsttmp |= 1 << dstbit;
27682         else
27683             dsttmp &= ~(1 << dstbit);
27684         if (!(++srcbit % 32))
27685         {
27686             srctmp = card_table[++srcwrd];
27687             srcbit = 0;
27688         }
27689
27690         if (nextp)
27691         {
27692             if (srctmp & (1 << srcbit))
27693                 dsttmp |= 1 << dstbit;
27694         }
27695
27696         if (!(++dstbit % 32))
27697         {
27698             card_table[dstwrd] = dsttmp;
27699
27700 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27701             if (dsttmp != 0)
27702             {
27703                 card_bundle_set(cardw_card_bundle(dstwrd));
27704             }
27705 #endif
27706
27707             dstwrd++;
27708             dsttmp = card_table[dstwrd];
27709             dstbit = 0;
27710         }
27711     }
27712
27713     card_table[dstwrd] = dsttmp;
27714
27715 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27716     if (dsttmp != 0)
27717     {
27718         card_bundle_set(cardw_card_bundle(dstwrd));
27719     }
27720 #endif
27721 }
27722
27723 void gc_heap::copy_cards_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27724 {
27725     ptrdiff_t relocation_distance = src - dest;
27726     size_t start_dest_card = card_of (align_on_card (dest));
27727     size_t end_dest_card = card_of (dest + len - 1);
27728     size_t dest_card = start_dest_card;
27729     size_t src_card = card_of (card_address (dest_card)+relocation_distance);
27730     dprintf (3,("Copying cards [%Ix:%Ix->%Ix:%Ix, ",
27731                  src_card, (size_t)src, dest_card, (size_t)dest));
27732     dprintf (3,(" %Ix->%Ix:%Ix[",
27733               (size_t)src+len, end_dest_card, (size_t)dest+len));
27734
27735     dprintf (3, ("dest: %Ix, src: %Ix, len: %Ix, reloc: %Ix, align_on_card(dest) is %Ix",
27736         dest, src, len, relocation_distance, (align_on_card (dest))));
27737
27738     dprintf (3, ("start_dest_card: %Ix (address: %Ix), end_dest_card: %Ix(addr: %Ix), card_of (dest): %Ix",
27739         start_dest_card, card_address (start_dest_card), end_dest_card, card_address (end_dest_card), card_of (dest)));
27740
27741     //First card has two boundaries
27742     if (start_dest_card != card_of (dest))
27743     {
27744         if ((card_of (card_address (start_dest_card) + relocation_distance) <= card_of (src + len - 1))&&
27745             card_set_p (card_of (card_address (start_dest_card) + relocation_distance)))
27746         {
27747             dprintf (3, ("card_address (start_dest_card) + reloc is %Ix, card: %Ix(set), src+len-1: %Ix, card: %Ix",
27748                     (card_address (start_dest_card) + relocation_distance),
27749                     card_of (card_address (start_dest_card) + relocation_distance),
27750                     (src + len - 1),
27751                     card_of (src + len - 1)));
27752
27753             dprintf (3, ("setting card: %Ix", card_of (dest)));
27754             set_card (card_of (dest));
27755         }
27756     }
27757
27758     if (card_set_p (card_of (src)))
27759         set_card (card_of (dest));
27760
27761
27762     copy_cards (dest_card, src_card, end_dest_card,
27763                 ((dest - align_lower_card (dest)) != (src - align_lower_card (src))));
27764
27765     //Last card has two boundaries.
27766     if ((card_of (card_address (end_dest_card) + relocation_distance) >= card_of (src)) &&
27767         card_set_p (card_of (card_address (end_dest_card) + relocation_distance)))
27768     {
27769         dprintf (3, ("card_address (end_dest_card) + reloc is %Ix, card: %Ix(set), src: %Ix, card: %Ix",
27770                 (card_address (end_dest_card) + relocation_distance),
27771                 card_of (card_address (end_dest_card) + relocation_distance),
27772                 src,
27773                 card_of (src)));
27774
27775         dprintf (3, ("setting card: %Ix", end_dest_card));
27776         set_card (end_dest_card);
27777     }
27778
27779     if (card_set_p (card_of (src + len - 1)))
27780         set_card (end_dest_card);
27781
27782 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
27783     card_bundles_set(cardw_card_bundle(card_word(card_of(dest))), cardw_card_bundle(align_cardw_on_bundle(card_word(end_dest_card))));
27784 #endif
27785 }
27786
27787 #ifdef BACKGROUND_GC
27788 // this does not need the Interlocked version of mark_array_set_marked.
27789 void gc_heap::copy_mark_bits_for_addresses (uint8_t* dest, uint8_t* src, size_t len)
27790 {
27791     dprintf (3, ("Copying mark_bits for addresses [%Ix->%Ix, %Ix->%Ix[",
27792                  (size_t)src, (size_t)dest,
27793                  (size_t)src+len, (size_t)dest+len));
27794
27795     uint8_t* src_o = src;
27796     uint8_t* dest_o;
27797     uint8_t* src_end = src + len;
27798     int align_const = get_alignment_constant (TRUE);
27799     ptrdiff_t reloc = dest - src;
27800
27801     while (src_o < src_end)
27802     {
27803         uint8_t*  next_o = src_o + Align (size (src_o), align_const);
27804
27805         if (background_object_marked (src_o, TRUE))
27806         {
27807             dest_o = src_o + reloc;
27808
27809             //if (background_object_marked (dest_o, FALSE))
27810             //{
27811             //    dprintf (3, ("*%Ix shouldn't have already been marked!", (size_t)(dest_o)));
27812             //    FATAL_GC_ERROR();
27813             //}
27814
27815             background_mark (dest_o, 
27816                              background_saved_lowest_address, 
27817                              background_saved_highest_address);
27818             dprintf (3, ("bc*%Ix*bc, b*%Ix*b", (size_t)src_o, (size_t)(dest_o)));
27819         }
27820
27821         src_o = next_o;
27822     }
27823 }
27824 #endif //BACKGROUND_GC
27825
27826 void gc_heap::fix_brick_to_highest (uint8_t* o, uint8_t* next_o)
27827 {
27828     size_t new_current_brick = brick_of (o);
27829     set_brick (new_current_brick,
27830                (o - brick_address (new_current_brick)));
27831     size_t b = 1 + new_current_brick;
27832     size_t limit = brick_of (next_o);
27833     //dprintf(3,(" fixing brick %Ix to point to object %Ix, till %Ix(%Ix)",
27834     dprintf(3,("b:%Ix->%Ix-%Ix", 
27835                new_current_brick, (size_t)o, (size_t)next_o));
27836     while (b < limit)
27837     {
27838         set_brick (b,(new_current_brick - b));
27839         b++;
27840     }
27841 }
27842
27843 // start can not be >= heap_segment_allocated for the segment.
27844 uint8_t* gc_heap::find_first_object (uint8_t* start, uint8_t* first_object)
27845 {
27846     size_t brick = brick_of (start);
27847     uint8_t* o = 0;
27848     //last_object == null -> no search shortcut needed
27849     if ((brick == brick_of (first_object) || (start <= first_object)))
27850     {
27851         o = first_object;
27852     }
27853     else
27854     {
27855         ptrdiff_t  min_brick = (ptrdiff_t)brick_of (first_object);
27856         ptrdiff_t  prev_brick = (ptrdiff_t)brick - 1;
27857         int         brick_entry = 0;
27858         while (1)
27859         {
27860             if (prev_brick < min_brick)
27861             {
27862                 break;
27863             }
27864             if ((brick_entry = get_brick_entry(prev_brick)) >= 0)
27865             {
27866                 break;
27867             }
27868             assert (! ((brick_entry == 0)));
27869             prev_brick = (brick_entry + prev_brick);
27870
27871         }
27872         o = ((prev_brick < min_brick) ? first_object :
27873                       brick_address (prev_brick) + brick_entry - 1);
27874         assert (o <= start);
27875     }
27876
27877     assert (Align (size (o)) >= Align (min_obj_size));
27878     uint8_t*  next_o = o + Align (size (o));
27879     size_t curr_cl = (size_t)next_o / brick_size;
27880     size_t min_cl = (size_t)first_object / brick_size;
27881
27882     //dprintf (3,( "Looking for intersection with %Ix from %Ix", (size_t)start, (size_t)o));
27883 #ifdef TRACE_GC
27884     unsigned int n_o = 1;
27885 #endif //TRACE_GC
27886
27887     uint8_t* next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27888
27889     while (next_o <= start)
27890     {
27891         do
27892         {
27893 #ifdef TRACE_GC
27894             n_o++;
27895 #endif //TRACE_GC
27896             o = next_o;
27897             assert (Align (size (o)) >= Align (min_obj_size));
27898             next_o = o + Align (size (o));
27899             Prefetch (next_o);
27900         }while (next_o < next_b);
27901
27902         if (((size_t)next_o / brick_size) != curr_cl)
27903         {
27904             if (curr_cl >= min_cl)
27905             {
27906                 fix_brick_to_highest (o, next_o);
27907             }
27908             curr_cl = (size_t) next_o / brick_size;
27909         }
27910         next_b = min (align_lower_brick (next_o) + brick_size, start+1);
27911     }
27912
27913     size_t bo = brick_of (o);
27914     //dprintf (3, ("Looked at %Id objects, fixing brick [%Ix-[%Ix", 
27915     dprintf (3, ("%Id o, [%Ix-[%Ix", 
27916         n_o, bo, brick));
27917     if (bo < brick)
27918     {
27919         set_brick (bo, (o - brick_address(bo)));
27920         size_t b = 1 + bo;
27921         int x = -1;
27922         while (b < brick)
27923         {
27924             set_brick (b,x--);
27925             b++;
27926         }
27927     }
27928
27929     return o;
27930 }
27931
27932 #ifdef CARD_BUNDLE
27933
27934 // Find the first non-zero card word between cardw and cardw_end.
27935 // The index of the word we find is returned in cardw.
27936 BOOL gc_heap::find_card_dword (size_t& cardw, size_t cardw_end)
27937 {
27938     dprintf (3, ("gc: %d, find_card_dword cardw: %Ix, cardw_end: %Ix",
27939                  dd_collection_count (dynamic_data_of (0)), cardw, cardw_end));
27940
27941     if (card_bundles_enabled())
27942     {
27943         size_t cardb = cardw_card_bundle (cardw);
27944         size_t end_cardb = cardw_card_bundle (align_cardw_on_bundle (cardw_end));
27945         while (1)
27946         {
27947             // Find a non-zero bundle
27948             while ((cardb < end_cardb) && (card_bundle_set_p (cardb) == 0))
27949             {
27950                 cardb++;
27951             }
27952             if (cardb == end_cardb)
27953                 return FALSE;
27954
27955             uint32_t* card_word = &card_table[max(card_bundle_cardw (cardb),cardw)];
27956             uint32_t* card_word_end = &card_table[min(card_bundle_cardw (cardb+1),cardw_end)];
27957             while ((card_word < card_word_end) && !(*card_word))
27958             {
27959                 card_word++;
27960             }
27961
27962             if (card_word != card_word_end)
27963             {
27964                 cardw = (card_word - &card_table[0]);
27965                 return TRUE;
27966             }
27967             else if ((cardw <= card_bundle_cardw (cardb)) &&
27968                      (card_word == &card_table [card_bundle_cardw (cardb+1)]))
27969             {
27970                 // a whole bundle was explored and is empty
27971                 dprintf  (3, ("gc: %d, find_card_dword clear bundle: %Ix cardw:[%Ix,%Ix[",
27972                         dd_collection_count (dynamic_data_of (0)), 
27973                         cardb, card_bundle_cardw (cardb),
27974                         card_bundle_cardw (cardb+1)));
27975                 card_bundle_clear (cardb);
27976             }
27977
27978             cardb++;
27979         }
27980     }
27981     else
27982     {
27983         uint32_t* card_word = &card_table[cardw];
27984         uint32_t* card_word_end = &card_table [cardw_end];
27985
27986         while (card_word < card_word_end)
27987         {
27988             if ((*card_word) != 0)
27989             {
27990                 cardw = (card_word - &card_table [0]);
27991                 return TRUE;
27992             }
27993
27994             card_word++;
27995         }
27996         return FALSE;
27997
27998     }
27999
28000 }
28001
28002 #endif //CARD_BUNDLE
28003
28004 // Find cards that are set between two points in a card table.
28005 // Parameters
28006 //     card_table    : The card table.
28007 //     card          : [in/out] As input, the card to start searching from.
28008 //                              As output, the first card that's set.
28009 //     card_word_end : The card word at which to stop looking.
28010 //     end_card      : [out] The last card which is set.
28011 BOOL gc_heap::find_card(uint32_t* card_table,
28012                         size_t&   card,
28013                         size_t    card_word_end,
28014                         size_t&   end_card)
28015 {
28016     uint32_t* last_card_word;
28017     uint32_t card_word_value;
28018     uint32_t bit_position;
28019     
28020     // Find the first card which is set
28021     last_card_word = &card_table [card_word (card)];
28022     bit_position = card_bit (card);
28023     card_word_value = (*last_card_word) >> bit_position;
28024     if (!card_word_value)
28025     {
28026         bit_position = 0;
28027 #ifdef CARD_BUNDLE
28028         // Using the card bundle, go through the remaining card words between here and 
28029         // card_word_end until we find one that is non-zero.
28030         size_t lcw = card_word(card) + 1;
28031         if (gc_heap::find_card_dword (lcw, card_word_end) == FALSE)
28032         {
28033             return FALSE;
28034         }
28035         else
28036         {
28037             last_card_word = &card_table [lcw];
28038             card_word_value = *last_card_word;
28039         }
28040
28041 #else //CARD_BUNDLE
28042         // Go through the remaining card words between here and card_word_end until we find
28043         // one that is non-zero.
28044         do
28045         {
28046             ++last_card_word;
28047         }
28048
28049         while ((last_card_word < &card_table [card_word_end]) && !(*last_card_word));
28050         if (last_card_word < &card_table [card_word_end])
28051         {
28052             card_word_value = *last_card_word;
28053         }
28054         else
28055         {
28056             // We failed to find any non-zero card words before we got to card_word_end
28057             return FALSE;
28058         }
28059 #endif //CARD_BUNDLE
28060     }
28061
28062
28063     // Look for the lowest bit set
28064     if (card_word_value)
28065     {
28066         while (!(card_word_value & 1))
28067         {
28068             bit_position++;
28069             card_word_value = card_word_value / 2;
28070         }
28071     }
28072     
28073     // card is the card word index * card size + the bit index within the card
28074     card = (last_card_word - &card_table[0]) * card_word_width + bit_position;
28075
28076     do
28077     {
28078         // Keep going until we get to an un-set card.
28079         bit_position++;
28080         card_word_value = card_word_value / 2;
28081
28082         // If we reach the end of the card word and haven't hit a 0 yet, start going
28083         // card word by card word until we get to one that's not fully set (0xFFFF...)
28084         // or we reach card_word_end.
28085         if ((bit_position == card_word_width) && (last_card_word < &card_table [card_word_end]))
28086         {
28087             do
28088             {
28089                 card_word_value = *(++last_card_word);
28090             } while ((last_card_word < &card_table [card_word_end]) &&
28091
28092 #ifdef _MSC_VER
28093                      (card_word_value == (1 << card_word_width)-1)
28094 #else
28095                      // if left shift count >= width of type,
28096                      // gcc reports error.
28097                      (card_word_value == ~0u)
28098 #endif // _MSC_VER
28099                 );
28100             bit_position = 0;
28101         }
28102     } while (card_word_value & 1);
28103
28104     end_card = (last_card_word - &card_table [0])* card_word_width + bit_position;
28105     
28106     //dprintf (3, ("find_card: [%Ix, %Ix[ set", card, end_card));
28107     dprintf (3, ("fc: [%Ix, %Ix[", card, end_card));
28108     return TRUE;
28109 }
28110
28111
28112     //because of heap expansion, computing end is complicated.
28113 uint8_t* compute_next_end (heap_segment* seg, uint8_t* low)
28114 {
28115     if ((low >=  heap_segment_mem (seg)) &&
28116         (low < heap_segment_allocated (seg)))
28117         return low;
28118     else
28119         return heap_segment_allocated (seg);
28120 }
28121
28122 uint8_t*
28123 gc_heap::compute_next_boundary (uint8_t* low, int gen_number,
28124                                 BOOL relocating)
28125 {
28126     UNREFERENCED_PARAMETER(low);
28127
28128     //when relocating, the fault line is the plan start of the younger
28129     //generation because the generation is promoted.
28130     if (relocating && (gen_number == (settings.condemned_generation + 1)))
28131     {
28132         generation* gen = generation_of (gen_number - 1);
28133         uint8_t* gen_alloc = generation_plan_allocation_start (gen);
28134         assert (gen_alloc);
28135         return gen_alloc;
28136     }
28137     else
28138     {
28139         assert (gen_number > settings.condemned_generation);
28140         return generation_allocation_start (generation_of (gen_number - 1 ));
28141     }
28142
28143 }
28144
28145 inline void
28146 gc_heap::keep_card_live (uint8_t* o, size_t& n_gen,
28147                          size_t& cg_pointers_found)
28148 {
28149     THREAD_FROM_HEAP;
28150     if ((gc_low <= o) && (gc_high > o))
28151     {
28152         n_gen++;
28153     }
28154 #ifdef MULTIPLE_HEAPS
28155     else if (o)
28156     {
28157         gc_heap* hp = heap_of (o);
28158         if (hp != this)
28159         {
28160             if ((hp->gc_low <= o) &&
28161                 (hp->gc_high > o))
28162             {
28163                 n_gen++;
28164             }
28165         }
28166     }
28167 #endif //MULTIPLE_HEAPS
28168     cg_pointers_found ++;
28169     dprintf (4, ("keep card live for %Ix", o));
28170 }
28171
28172 inline void
28173 gc_heap::mark_through_cards_helper (uint8_t** poo, size_t& n_gen,
28174                                     size_t& cg_pointers_found,
28175                                     card_fn fn, uint8_t* nhigh,
28176                                     uint8_t* next_boundary)
28177 {
28178     THREAD_FROM_HEAP;
28179     if ((gc_low <= *poo) && (gc_high > *poo))
28180     {
28181         n_gen++;
28182         call_fn(fn) (poo THREAD_NUMBER_ARG);
28183     }
28184 #ifdef MULTIPLE_HEAPS
28185     else if (*poo)
28186     {
28187         gc_heap* hp = heap_of_gc (*poo);
28188         if (hp != this)
28189         {
28190             if ((hp->gc_low <= *poo) &&
28191                 (hp->gc_high > *poo))
28192             {
28193                 n_gen++;
28194                 call_fn(fn) (poo THREAD_NUMBER_ARG);
28195             }
28196             if ((fn == &gc_heap::relocate_address) ||
28197                 ((hp->ephemeral_low <= *poo) &&
28198                  (hp->ephemeral_high > *poo)))
28199             {
28200                 cg_pointers_found++;
28201             }
28202         }
28203     }
28204 #endif //MULTIPLE_HEAPS
28205     if ((next_boundary <= *poo) && (nhigh > *poo))
28206     {
28207         cg_pointers_found ++;
28208         dprintf (4, ("cg pointer %Ix found, %Id so far",
28209                      (size_t)*poo, cg_pointers_found ));
28210
28211     }
28212 }
28213
28214 BOOL gc_heap::card_transition (uint8_t* po, uint8_t* end, size_t card_word_end,
28215                                size_t& cg_pointers_found, 
28216                                size_t& n_eph, size_t& n_card_set,
28217                                size_t& card, size_t& end_card,
28218                                BOOL& foundp, uint8_t*& start_address,
28219                                uint8_t*& limit, size_t& n_cards_cleared)
28220 {
28221     dprintf (3, ("pointer %Ix past card %Ix", (size_t)po, (size_t)card));
28222     dprintf (3, ("ct: %Id cg", cg_pointers_found));
28223     BOOL passed_end_card_p = FALSE;
28224     foundp = FALSE;
28225
28226     if (cg_pointers_found == 0)
28227     {
28228         //dprintf(3,(" Clearing cards [%Ix, %Ix[ ",
28229         dprintf(3,(" CC [%Ix, %Ix[ ",
28230                 (size_t)card_address(card), (size_t)po));
28231         clear_cards (card, card_of(po));
28232         n_card_set -= (card_of (po) - card);
28233         n_cards_cleared += (card_of (po) - card);
28234
28235     }
28236     n_eph +=cg_pointers_found;
28237     cg_pointers_found = 0;
28238     card = card_of (po);
28239     if (card >= end_card)
28240     {
28241         passed_end_card_p = TRUE;
28242         dprintf (3, ("card %Ix exceeding end_card %Ix",
28243                     (size_t)card, (size_t)end_card));
28244         foundp = find_card (card_table, card, card_word_end, end_card);
28245         if (foundp)
28246         {
28247             n_card_set+= end_card - card;
28248             start_address = card_address (card);
28249             dprintf (3, ("NewC: %Ix, start: %Ix, end: %Ix",
28250                         (size_t)card, (size_t)start_address,
28251                         (size_t)card_address (end_card)));
28252         }
28253         limit = min (end, card_address (end_card));
28254
28255         assert (!((limit == card_address (end_card))&&
28256                 card_set_p (end_card)));
28257     }
28258
28259     return passed_end_card_p;
28260 }
28261
28262 void gc_heap::mark_through_cards_for_segments (card_fn fn, BOOL relocating)
28263 {
28264 #ifdef BACKGROUND_GC
28265     dprintf (3, ("current_sweep_pos is %Ix, saved_sweep_ephemeral_seg is %Ix(%Ix)",
28266                  current_sweep_pos, saved_sweep_ephemeral_seg, saved_sweep_ephemeral_start));
28267
28268     heap_segment* soh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
28269     PREFIX_ASSUME(soh_seg != NULL);
28270
28271     while (soh_seg)
28272     {
28273         dprintf (3, ("seg %Ix, bgc_alloc: %Ix, alloc: %Ix", 
28274             soh_seg, 
28275             heap_segment_background_allocated (soh_seg),
28276             heap_segment_allocated (soh_seg)));
28277
28278         soh_seg = heap_segment_next_rw (soh_seg);
28279     }
28280 #endif //BACKGROUND_GC
28281
28282     uint8_t* low = gc_low;
28283     uint8_t* high = gc_high;
28284     size_t end_card = 0;
28285
28286     generation*   oldest_gen        = generation_of (max_generation);
28287     int           curr_gen_number   = max_generation;
28288     uint8_t*      gen_boundary      = generation_allocation_start(generation_of(curr_gen_number - 1));
28289     uint8_t*      next_boundary     = compute_next_boundary(gc_low, curr_gen_number, relocating);
28290     
28291     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
28292     PREFIX_ASSUME(seg != NULL);
28293
28294     uint8_t*      beg               = generation_allocation_start (oldest_gen);
28295     uint8_t*      end               = compute_next_end (seg, low);
28296     uint8_t*      last_object       = beg;
28297
28298     size_t  cg_pointers_found = 0;
28299
28300     size_t  card_word_end = (card_of (align_on_card_word (end)) / card_word_width);
28301
28302     size_t        n_eph             = 0;
28303     size_t        n_gen             = 0;
28304     size_t        n_card_set        = 0;
28305     uint8_t*      nhigh             = (relocating ?
28306                                        heap_segment_plan_allocated (ephemeral_heap_segment) : high);
28307
28308     BOOL          foundp            = FALSE;
28309     uint8_t*      start_address     = 0;
28310     uint8_t*      limit             = 0;
28311     size_t        card              = card_of (beg);
28312 #ifdef BACKGROUND_GC
28313     BOOL consider_bgc_mark_p        = FALSE;
28314     BOOL check_current_sweep_p      = FALSE;
28315     BOOL check_saved_sweep_p        = FALSE;
28316     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
28317 #endif //BACKGROUND_GC
28318
28319     dprintf(3, ("CMs: %Ix->%Ix", (size_t)beg, (size_t)end));
28320     size_t total_cards_cleared = 0;
28321
28322     while (1)
28323     {
28324         if (card_of(last_object) > card)
28325         {
28326             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
28327             if (cg_pointers_found == 0)
28328             {
28329                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)last_object));
28330                 clear_cards (card, card_of(last_object));
28331                 n_card_set -= (card_of (last_object) - card);
28332                 total_cards_cleared += (card_of (last_object) - card);
28333             }
28334
28335             n_eph += cg_pointers_found;
28336             cg_pointers_found = 0;
28337             card = card_of (last_object);
28338         }
28339
28340         if (card >= end_card)
28341         {
28342             foundp = find_card (card_table, card, card_word_end, end_card);
28343             if (foundp)
28344             {
28345                 n_card_set += end_card - card;
28346                 start_address = max (beg, card_address (card));
28347             }
28348             limit = min (end, card_address (end_card));
28349         }
28350         if (!foundp || (last_object >= end) || (card_address (card) >= end))
28351         {
28352             if (foundp && (cg_pointers_found == 0))
28353             {
28354                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
28355                            (size_t)end));
28356                 clear_cards (card, card_of (end));
28357                 n_card_set -= (card_of (end) - card);
28358                 total_cards_cleared += (card_of (end) - card);
28359             }
28360             n_eph += cg_pointers_found;
28361             cg_pointers_found = 0;
28362             if ((seg = heap_segment_next_in_range (seg)) != 0)
28363             {
28364 #ifdef BACKGROUND_GC
28365                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
28366 #endif //BACKGROUND_GC
28367                 beg = heap_segment_mem (seg);
28368                 end = compute_next_end (seg, low);
28369                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
28370                 card = card_of (beg);
28371                 last_object = beg;
28372                 end_card = 0;
28373                 continue;
28374             }
28375             else
28376             {
28377                 break;
28378             }
28379         }
28380
28381         assert (card_set_p (card));
28382         {
28383             uint8_t* o = last_object;
28384
28385             o = find_first_object (start_address, last_object);
28386             // Never visit an object twice.
28387             assert (o >= last_object);
28388
28389             //dprintf(3,("Considering card %Ix start object: %Ix, %Ix[ boundary: %Ix",
28390             dprintf(3, ("c: %Ix, o: %Ix, l: %Ix[ boundary: %Ix",
28391                    card, (size_t)o, (size_t)limit, (size_t)gen_boundary));
28392
28393             while (o < limit)
28394             {
28395                 assert (Align (size (o)) >= Align (min_obj_size));
28396                 size_t s = size (o);
28397
28398                 uint8_t* next_o =  o + Align (s);
28399                 Prefetch (next_o);
28400
28401                 if ((o >= gen_boundary) &&
28402                     (seg == ephemeral_heap_segment))
28403                 {
28404                     dprintf (3, ("switching gen boundary %Ix", (size_t)gen_boundary));
28405                     curr_gen_number--;
28406                     assert ((curr_gen_number > 0));
28407                     gen_boundary = generation_allocation_start
28408                         (generation_of (curr_gen_number - 1));
28409                     next_boundary = (compute_next_boundary
28410                                      (low, curr_gen_number, relocating));
28411                 }
28412
28413                 dprintf (4, ("|%Ix|", (size_t)o));
28414
28415                 if (next_o < start_address)
28416                 {
28417                     goto end_object;
28418                 }
28419
28420 #ifdef BACKGROUND_GC
28421                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
28422                 {
28423                     goto end_object;
28424                 }
28425 #endif //BACKGROUND_GC
28426
28427 #ifdef COLLECTIBLE_CLASS
28428                 if (is_collectible(o))
28429                 {
28430                     BOOL passed_end_card_p = FALSE;
28431
28432                     if (card_of (o) > card)
28433                     {
28434                         passed_end_card_p = card_transition (o, end, card_word_end,
28435                             cg_pointers_found, 
28436                             n_eph, n_card_set,
28437                             card, end_card,
28438                             foundp, start_address,
28439                             limit, total_cards_cleared);
28440                     }
28441
28442                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
28443                     {
28444                         // card is valid and it covers the head of the object
28445                         if (fn == &gc_heap::relocate_address)
28446                         {
28447                             keep_card_live (o, n_gen, cg_pointers_found);
28448                         }
28449                         else
28450                         {
28451                             uint8_t* class_obj = get_class_object (o);
28452                             mark_through_cards_helper (&class_obj, n_gen,
28453                                                     cg_pointers_found, fn,
28454                                                     nhigh, next_boundary);
28455                         }
28456                     }
28457
28458                     if (passed_end_card_p)
28459                     {
28460                         if (foundp && (card_address (card) < next_o))
28461                         {
28462                             goto go_through_refs;
28463                         }
28464                         else if (foundp && (start_address < limit))
28465                         {
28466                             next_o = find_first_object (start_address, o);
28467                             goto end_object;
28468                         }
28469                         else
28470                             goto end_limit;                            
28471                     }
28472                 }
28473
28474 go_through_refs:
28475 #endif //COLLECTIBLE_CLASS
28476
28477                 if (contain_pointers (o))
28478                 {
28479                     dprintf(3,("Going through %Ix start_address: %Ix", (size_t)o, (size_t)start_address));
28480
28481                     {
28482                         dprintf (4, ("normal object path"));
28483                         go_through_object
28484                             (method_table(o), o, s, poo,
28485                              start_address, use_start, (o + s),
28486                              {
28487                                  dprintf (4, ("<%Ix>:%Ix", (size_t)poo, (size_t)*poo));
28488                                  if (card_of ((uint8_t*)poo) > card)
28489                                  {
28490                                     BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
28491                                             card_word_end,
28492                                             cg_pointers_found, 
28493                                             n_eph, n_card_set,
28494                                             card, end_card,
28495                                             foundp, start_address,
28496                                             limit, total_cards_cleared);
28497
28498                                      if (passed_end_card_p)
28499                                      {
28500                                         if (foundp && (card_address (card) < next_o))
28501                                         {
28502                                              //new_start();
28503                                              {
28504                                                  if (ppstop <= (uint8_t**)start_address)
28505                                                      {break;}
28506                                                  else if (poo < (uint8_t**)start_address)
28507                                                      {poo = (uint8_t**)start_address;}
28508                                              }
28509                                         }
28510                                         else if (foundp && (start_address < limit))
28511                                         {
28512                                             next_o = find_first_object (start_address, o);
28513                                             goto end_object;
28514                                         }
28515                                          else
28516                                             goto end_limit;
28517                                      }
28518                                  }
28519
28520                                  mark_through_cards_helper (poo, n_gen,
28521                                                             cg_pointers_found, fn,
28522                                                             nhigh, next_boundary);
28523                              }
28524                             );
28525                     }
28526                 }
28527
28528             end_object:
28529                 if (((size_t)next_o / brick_size) != ((size_t) o / brick_size))
28530                 {
28531                     if (brick_table [brick_of (o)] <0)
28532                         fix_brick_to_highest (o, next_o);
28533                 }
28534                 o = next_o;
28535             }
28536         end_limit:
28537             last_object = o;
28538         }
28539     }
28540     // compute the efficiency ratio of the card table
28541     if (!relocating)
28542     {
28543         generation_skip_ratio = ((n_eph > 400)? (int)(((float)n_gen / (float)n_eph) * 100) : 100);
28544         dprintf (3, ("Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
28545             n_eph, n_gen , n_card_set, total_cards_cleared, generation_skip_ratio));
28546     }
28547     else
28548     {
28549         dprintf (3, ("R: Msoh: cross: %Id, useful: %Id, cards set: %Id, cards cleared: %Id, ratio: %d", 
28550             n_gen, n_eph, n_card_set, total_cards_cleared, generation_skip_ratio));
28551     }
28552 }
28553
28554 #ifdef SEG_REUSE_STATS
28555 size_t gc_heap::dump_buckets (size_t* ordered_indices, int count, size_t* total_size)
28556 {
28557     size_t total_items = 0;
28558     *total_size = 0;
28559     for (int i = 0; i < count; i++)
28560     {
28561         total_items += ordered_indices[i];
28562         *total_size += ordered_indices[i] << (MIN_INDEX_POWER2 + i);
28563         dprintf (SEG_REUSE_LOG_0, ("[%d]%4d 2^%2d", heap_number, ordered_indices[i], (MIN_INDEX_POWER2 + i)));
28564     } 
28565     dprintf (SEG_REUSE_LOG_0, ("[%d]Total %d items, total size is 0x%Ix", heap_number, total_items, *total_size));
28566     return total_items;
28567 }
28568 #endif // SEG_REUSE_STATS
28569
28570 void gc_heap::count_plug (size_t last_plug_size, uint8_t*& last_plug)
28571 {
28572     // detect pinned plugs
28573     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
28574     {
28575         deque_pinned_plug();
28576         update_oldest_pinned_plug();
28577         dprintf (3, ("dequed pin,now oldest pin is %Ix", pinned_plug (oldest_pin())));
28578     }
28579     else
28580     {
28581         size_t plug_size = last_plug_size + Align(min_obj_size);
28582         BOOL is_padded = FALSE;
28583
28584 #ifdef SHORT_PLUGS
28585         plug_size += Align (min_obj_size);
28586         is_padded = TRUE;
28587 #endif //SHORT_PLUGS
28588
28589 #ifdef RESPECT_LARGE_ALIGNMENT
28590         plug_size += switch_alignment_size (is_padded);
28591 #endif //RESPECT_LARGE_ALIGNMENT
28592
28593         total_ephemeral_plugs += plug_size;
28594         size_t plug_size_power2 = round_up_power2 (plug_size);
28595         ordered_plug_indices[relative_index_power2_plug (plug_size_power2)]++;
28596         dprintf (SEG_REUSE_LOG_1, ("[%d]count_plug: adding 0x%Ix - %Id (2^%d) to ordered plug array", 
28597             heap_number, 
28598             last_plug, 
28599             plug_size, 
28600             (relative_index_power2_plug (plug_size_power2) + MIN_INDEX_POWER2)));
28601     }
28602 }
28603
28604 void gc_heap::count_plugs_in_brick (uint8_t* tree, uint8_t*& last_plug)
28605 {
28606     assert ((tree != NULL));
28607     if (node_left_child (tree))
28608     {
28609         count_plugs_in_brick (tree + node_left_child (tree), last_plug);
28610     }
28611
28612     if (last_plug != 0)
28613     {
28614         uint8_t*  plug = tree;
28615         size_t gap_size = node_gap_size (plug);
28616         uint8_t*   gap = (plug - gap_size);
28617         uint8_t*  last_plug_end = gap;
28618         size_t  last_plug_size = (last_plug_end - last_plug);
28619         dprintf (3, ("tree: %Ix, last plug: %Ix, gap size: %Ix, gap: %Ix, last plug size: %Ix",
28620             tree, last_plug, gap_size, gap, last_plug_size));
28621
28622         if (tree == oldest_pinned_plug)
28623         {
28624             dprintf (3, ("tree %Ix is pinned, last plug is %Ix, size is %Ix",
28625                 tree, last_plug, last_plug_size));
28626             mark* m = oldest_pin();
28627             if (m->has_pre_plug_info())
28628             {
28629                 last_plug_size += sizeof (gap_reloc_pair);
28630                 dprintf (3, ("pin %Ix has pre plug, adjusting plug size to %Ix", tree, last_plug_size));
28631             }
28632         }
28633         // Can't assert here - if it's a pinned plug it can be less.
28634         //assert (last_plug_size >= Align (min_obj_size));
28635
28636         count_plug (last_plug_size, last_plug);
28637     }
28638
28639     last_plug = tree;
28640
28641     if (node_right_child (tree))
28642     {
28643         count_plugs_in_brick (tree + node_right_child (tree), last_plug);
28644     }
28645 }
28646
28647 void gc_heap::build_ordered_plug_indices ()
28648 {
28649     memset (ordered_plug_indices, 0, sizeof(ordered_plug_indices));
28650     memset (saved_ordered_plug_indices, 0, sizeof(saved_ordered_plug_indices));
28651
28652     uint8_t*  start_address = generation_limit (max_generation);
28653     uint8_t* end_address = heap_segment_allocated (ephemeral_heap_segment);
28654     size_t  current_brick = brick_of (start_address);
28655     size_t  end_brick = brick_of (end_address - 1);
28656     uint8_t* last_plug = 0;
28657
28658     //Look for the right pinned plug to start from.
28659     reset_pinned_queue_bos();
28660     while (!pinned_plug_que_empty_p())
28661     {
28662         mark* m = oldest_pin();
28663         if ((m->first >= start_address) && (m->first < end_address))
28664         {
28665             dprintf (3, ("found a pin %Ix between %Ix and %Ix", m->first, start_address, end_address));
28666
28667             break;
28668         }
28669         else
28670             deque_pinned_plug();
28671     }
28672     
28673     update_oldest_pinned_plug();
28674
28675     while (current_brick <= end_brick)
28676     {
28677         int brick_entry =  brick_table [ current_brick ];
28678         if (brick_entry >= 0)
28679         {
28680             count_plugs_in_brick (brick_address (current_brick) + brick_entry -1, last_plug);
28681         }
28682
28683         current_brick++;
28684     }
28685
28686     if (last_plug !=0)
28687     {
28688         count_plug (end_address - last_plug, last_plug);
28689     }
28690
28691     // we need to make sure that after fitting all the existing plugs, we
28692     // have big enough free space left to guarantee that the next allocation
28693     // will succeed.
28694     size_t extra_size = END_SPACE_AFTER_GC + Align (min_obj_size);
28695     total_ephemeral_plugs += extra_size;
28696     dprintf (SEG_REUSE_LOG_0, ("Making sure we can fit a large object after fitting all plugs"));
28697     ordered_plug_indices[relative_index_power2_plug (round_up_power2 (extra_size))]++;
28698     
28699     memcpy (saved_ordered_plug_indices, ordered_plug_indices, sizeof(ordered_plug_indices));
28700
28701 #ifdef SEG_REUSE_STATS
28702     dprintf (SEG_REUSE_LOG_0, ("Plugs:"));
28703     size_t total_plug_power2 = 0;
28704     dump_buckets (ordered_plug_indices, MAX_NUM_BUCKETS, &total_plug_power2);
28705     dprintf (SEG_REUSE_LOG_0, ("plugs: 0x%Ix (rounded up to 0x%Ix (%d%%))", 
28706                 total_ephemeral_plugs, 
28707                 total_plug_power2, 
28708                 (total_ephemeral_plugs ? 
28709                     (total_plug_power2 * 100 / total_ephemeral_plugs) :
28710                     0)));
28711     dprintf (SEG_REUSE_LOG_0, ("-------------------"));
28712 #endif // SEG_REUSE_STATS
28713 }
28714
28715 void gc_heap::init_ordered_free_space_indices ()
28716 {
28717     memset (ordered_free_space_indices, 0, sizeof(ordered_free_space_indices));
28718     memset (saved_ordered_free_space_indices, 0, sizeof(saved_ordered_free_space_indices));
28719 }
28720
28721 void gc_heap::trim_free_spaces_indices ()
28722 {
28723     trimmed_free_space_index = -1;
28724     size_t max_count = max_free_space_items - 1;
28725     size_t count = 0;
28726     int i = 0;
28727     for (i = (MAX_NUM_BUCKETS - 1); i >= 0; i--)
28728     {
28729         count += ordered_free_space_indices[i];
28730
28731         if (count >= max_count)
28732         {
28733             break;
28734         }
28735     }
28736
28737     ptrdiff_t extra_free_space_items = count - max_count;
28738
28739     if (extra_free_space_items > 0)
28740     {
28741         ordered_free_space_indices[i] -= extra_free_space_items;
28742         free_space_items = max_count;
28743         trimmed_free_space_index = i;
28744     }
28745     else
28746     {
28747         free_space_items = count;
28748     }
28749
28750     if (i == -1)
28751     {
28752         i = 0;
28753     }
28754
28755     free_space_buckets = MAX_NUM_BUCKETS - i;
28756
28757     for (--i; i >= 0; i--)
28758     {
28759         ordered_free_space_indices[i] = 0;
28760     }
28761
28762     memcpy (saved_ordered_free_space_indices, 
28763             ordered_free_space_indices,
28764             sizeof(ordered_free_space_indices));
28765 }
28766
28767 // We fit as many plugs as we can and update the number of plugs left and the number
28768 // of free spaces left.
28769 BOOL gc_heap::can_fit_in_spaces_p (size_t* ordered_blocks, int small_index, size_t* ordered_spaces, int big_index)
28770 {
28771     assert (small_index <= big_index);
28772     assert (big_index < MAX_NUM_BUCKETS);
28773
28774     size_t small_blocks = ordered_blocks[small_index];
28775
28776     if (small_blocks == 0)
28777     {
28778         return TRUE;
28779     }
28780
28781     size_t big_spaces = ordered_spaces[big_index];
28782
28783     if (big_spaces == 0)
28784     {
28785         return FALSE;
28786     }
28787
28788     dprintf (SEG_REUSE_LOG_1, ("[%d]Fitting %Id 2^%d plugs into %Id 2^%d free spaces", 
28789         heap_number,
28790         small_blocks, (small_index + MIN_INDEX_POWER2),
28791         big_spaces, (big_index + MIN_INDEX_POWER2)));
28792
28793     size_t big_to_small = big_spaces << (big_index - small_index);
28794
28795     ptrdiff_t extra_small_spaces = big_to_small - small_blocks;
28796     dprintf (SEG_REUSE_LOG_1, ("[%d]%d 2^%d spaces can fit %d 2^%d blocks", 
28797         heap_number,
28798         big_spaces, (big_index + MIN_INDEX_POWER2), big_to_small, (small_index + MIN_INDEX_POWER2)));
28799     BOOL can_fit = (extra_small_spaces >= 0);
28800
28801     if (can_fit) 
28802     {
28803         dprintf (SEG_REUSE_LOG_1, ("[%d]Can fit with %d 2^%d extras blocks", 
28804             heap_number,
28805             extra_small_spaces, (small_index + MIN_INDEX_POWER2)));
28806     }
28807
28808     int i = 0;
28809
28810     dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d spaces to 0", heap_number, (big_index + MIN_INDEX_POWER2)));
28811     ordered_spaces[big_index] = 0;
28812     if (extra_small_spaces > 0)
28813     {
28814         dprintf (SEG_REUSE_LOG_1, ("[%d]Setting # of 2^%d blocks to 0", heap_number, (small_index + MIN_INDEX_POWER2)));
28815         ordered_blocks[small_index] = 0;
28816         for (i = small_index; i < big_index; i++)
28817         {
28818             if (extra_small_spaces & 1)
28819             {
28820                 dprintf (SEG_REUSE_LOG_1, ("[%d]Increasing # of 2^%d spaces from %d to %d", 
28821                     heap_number,
28822                     (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + 1)));
28823                 ordered_spaces[i] += 1;
28824             }
28825             extra_small_spaces >>= 1;
28826         }
28827
28828         dprintf (SEG_REUSE_LOG_1, ("[%d]Finally increasing # of 2^%d spaces from %d to %d", 
28829             heap_number,
28830             (i + MIN_INDEX_POWER2), ordered_spaces[i], (ordered_spaces[i] + extra_small_spaces)));
28831         ordered_spaces[i] += extra_small_spaces;
28832     }
28833     else
28834     {
28835         dprintf (SEG_REUSE_LOG_1, ("[%d]Decreasing # of 2^%d blocks from %d to %d", 
28836             heap_number,
28837             (small_index + MIN_INDEX_POWER2), 
28838             ordered_blocks[small_index], 
28839             (ordered_blocks[small_index] - big_to_small)));
28840         ordered_blocks[small_index] -= big_to_small;
28841     }
28842
28843 #ifdef SEG_REUSE_STATS
28844     size_t temp;
28845     dprintf (SEG_REUSE_LOG_1, ("[%d]Plugs became:", heap_number));
28846     dump_buckets (ordered_blocks, MAX_NUM_BUCKETS, &temp);
28847
28848     dprintf (SEG_REUSE_LOG_1, ("[%d]Free spaces became:", heap_number));
28849     dump_buckets (ordered_spaces, MAX_NUM_BUCKETS, &temp);
28850 #endif //SEG_REUSE_STATS
28851
28852     return can_fit;
28853 }
28854
28855 // space_index gets updated to the biggest available space index.
28856 BOOL gc_heap::can_fit_blocks_p (size_t* ordered_blocks, int block_index, size_t* ordered_spaces, int* space_index)
28857 {
28858     assert (*space_index >= block_index);
28859
28860     while (!can_fit_in_spaces_p (ordered_blocks, block_index, ordered_spaces, *space_index))
28861     {
28862         (*space_index)--;
28863         if (*space_index < block_index)
28864         {
28865             return FALSE;
28866         }
28867     }
28868
28869     return TRUE;
28870 }
28871
28872 BOOL gc_heap::can_fit_all_blocks_p (size_t* ordered_blocks, size_t* ordered_spaces, int count)
28873 {
28874 #ifdef FEATURE_STRUCTALIGN
28875     // BARTOKTODO (4841): reenable when can_fit_in_spaces_p takes alignment requirements into account
28876     return FALSE;
28877 #endif // FEATURE_STRUCTALIGN
28878     int space_index = count - 1;
28879     for (int block_index = (count - 1); block_index >= 0; block_index--)
28880     {
28881         if (!can_fit_blocks_p (ordered_blocks, block_index, ordered_spaces, &space_index))
28882         {
28883             return FALSE;
28884         }
28885     }
28886
28887     return TRUE;
28888 }
28889
28890 void gc_heap::build_ordered_free_spaces (heap_segment* seg)
28891 {
28892     assert (bestfit_seg);
28893
28894     //bestfit_seg->add_buckets (MAX_NUM_BUCKETS - free_space_buckets + MIN_INDEX_POWER2, 
28895     //                    ordered_free_space_indices + (MAX_NUM_BUCKETS - free_space_buckets), 
28896     //                    free_space_buckets, 
28897     //                    free_space_items);
28898
28899     bestfit_seg->add_buckets (MIN_INDEX_POWER2, 
28900                         ordered_free_space_indices, 
28901                         MAX_NUM_BUCKETS, 
28902                         free_space_items);
28903
28904     assert (settings.condemned_generation == max_generation);
28905
28906     uint8_t* first_address = heap_segment_mem (seg);
28907     uint8_t* end_address   = heap_segment_reserved (seg);
28908     //look through the pinned plugs for relevant ones.
28909     //Look for the right pinned plug to start from.
28910     reset_pinned_queue_bos();
28911     mark* m = 0;
28912     // See comment in can_expand_into_p why we need (max_generation + 1).
28913     size_t eph_gen_starts = (Align (min_obj_size)) * (max_generation + 1);
28914     BOOL has_fit_gen_starts = FALSE;
28915
28916     while (!pinned_plug_que_empty_p())
28917     {
28918         m = oldest_pin();
28919         if ((pinned_plug (m) >= first_address) && 
28920             (pinned_plug (m) < end_address) &&
28921             (pinned_len (m) >= eph_gen_starts))
28922         {
28923
28924             assert ((pinned_plug (m) - pinned_len (m)) == bestfit_first_pin);
28925             break;
28926         }
28927         else
28928         {
28929             deque_pinned_plug();
28930         }
28931     }
28932
28933     if (!pinned_plug_que_empty_p())
28934     {
28935         bestfit_seg->add ((void*)m, TRUE, TRUE);
28936         deque_pinned_plug();
28937         m = oldest_pin();
28938         has_fit_gen_starts = TRUE;
28939     }
28940
28941     while (!pinned_plug_que_empty_p() &&
28942             ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
28943     {
28944         bestfit_seg->add ((void*)m, TRUE, FALSE);
28945         deque_pinned_plug();
28946         m = oldest_pin();
28947     }
28948
28949     if (commit_end_of_seg)
28950     {
28951         if (!has_fit_gen_starts)
28952         {
28953             assert (bestfit_first_pin == heap_segment_plan_allocated (seg));
28954         }
28955         bestfit_seg->add ((void*)seg, FALSE, (!has_fit_gen_starts));
28956     }
28957
28958 #ifdef _DEBUG
28959     bestfit_seg->check();
28960 #endif //_DEBUG
28961 }
28962
28963 BOOL gc_heap::try_best_fit (BOOL end_of_segment_p)
28964 {
28965     if (!end_of_segment_p)
28966     {
28967         trim_free_spaces_indices ();
28968     }
28969
28970     BOOL can_bestfit = can_fit_all_blocks_p (ordered_plug_indices, 
28971                                              ordered_free_space_indices, 
28972                                              MAX_NUM_BUCKETS);
28973
28974     return can_bestfit;
28975 }
28976
28977 BOOL gc_heap::best_fit (size_t free_space, 
28978                         size_t largest_free_space, 
28979                         size_t additional_space, 
28980                         BOOL* use_additional_space)
28981 {
28982     dprintf (SEG_REUSE_LOG_0, ("gen%d: trying best fit mechanism", settings.condemned_generation));
28983
28984     assert (!additional_space || (additional_space && use_additional_space));
28985     if (use_additional_space)
28986     {
28987         *use_additional_space = FALSE;
28988     }
28989
28990     if (ordered_plug_indices_init == FALSE)
28991     {
28992         total_ephemeral_plugs = 0;
28993         build_ordered_plug_indices();
28994         ordered_plug_indices_init = TRUE;
28995     }
28996     else
28997     {
28998         memcpy (ordered_plug_indices, saved_ordered_plug_indices, sizeof(ordered_plug_indices));
28999     }
29000
29001     if (total_ephemeral_plugs == (END_SPACE_AFTER_GC + Align (min_obj_size)))
29002     {
29003         dprintf (SEG_REUSE_LOG_0, ("No ephemeral plugs to realloc, done"));
29004         size_t empty_eph = (END_SPACE_AFTER_GC + Align (min_obj_size) + (Align (min_obj_size)) * (max_generation + 1));
29005         BOOL can_fit_empty_eph = (largest_free_space >= empty_eph);
29006         if (!can_fit_empty_eph)
29007         {
29008             can_fit_empty_eph = (additional_space >= empty_eph);
29009
29010             if (can_fit_empty_eph)
29011             {
29012                 *use_additional_space = TRUE;
29013             }
29014         }
29015
29016         return can_fit_empty_eph;
29017     }
29018
29019     if ((total_ephemeral_plugs + approximate_new_allocation()) >= (free_space + additional_space))
29020     {
29021         dprintf (SEG_REUSE_LOG_0, ("We won't have enough free space left in this segment after fitting, done"));
29022         return FALSE;
29023     }
29024
29025     if ((free_space + additional_space) == 0)
29026     {
29027         dprintf (SEG_REUSE_LOG_0, ("No free space in this segment, done"));
29028         return FALSE;
29029     }
29030
29031 #ifdef SEG_REUSE_STATS
29032     dprintf (SEG_REUSE_LOG_0, ("Free spaces:"));
29033     size_t total_free_space_power2 = 0;
29034     size_t total_free_space_items = 
29035         dump_buckets (ordered_free_space_indices, 
29036                       MAX_NUM_BUCKETS,
29037                       &total_free_space_power2);
29038     dprintf (SEG_REUSE_LOG_0, ("currently max free spaces is %Id", max_free_space_items));
29039
29040     dprintf (SEG_REUSE_LOG_0, ("Ephemeral plugs: 0x%Ix, free space: 0x%Ix (rounded down to 0x%Ix (%Id%%)), additional free_space: 0x%Ix",
29041                 total_ephemeral_plugs, 
29042                 free_space, 
29043                 total_free_space_power2, 
29044                 (free_space ? (total_free_space_power2 * 100 / free_space) : 0),
29045                 additional_space));
29046
29047     size_t saved_all_free_space_indices[MAX_NUM_BUCKETS];
29048     memcpy (saved_all_free_space_indices, 
29049             ordered_free_space_indices, 
29050             sizeof(saved_all_free_space_indices));
29051
29052 #endif // SEG_REUSE_STATS
29053
29054     if (total_ephemeral_plugs > (free_space + additional_space))
29055     {
29056         return FALSE;
29057     }
29058
29059     use_bestfit = try_best_fit(FALSE);
29060
29061     if (!use_bestfit && additional_space)
29062     {
29063         int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (additional_space));
29064
29065         if (relative_free_space_index != -1)
29066         {
29067             int relative_plug_index = 0;
29068             size_t plugs_to_fit = 0;
29069
29070             for (relative_plug_index = (MAX_NUM_BUCKETS - 1); relative_plug_index >= 0; relative_plug_index--)
29071             {
29072                 plugs_to_fit = ordered_plug_indices[relative_plug_index];
29073                 if (plugs_to_fit != 0)
29074                 {
29075                     break;
29076                 }
29077             }
29078
29079             if ((relative_plug_index > relative_free_space_index) ||
29080                 ((relative_plug_index == relative_free_space_index) &&
29081                 (plugs_to_fit > 1)))
29082             {
29083 #ifdef SEG_REUSE_STATS
29084                 dprintf (SEG_REUSE_LOG_0, ("additional space is 2^%d but we stopped at %d 2^%d plug(s)",
29085                             (relative_free_space_index + MIN_INDEX_POWER2),
29086                             plugs_to_fit,
29087                             (relative_plug_index + MIN_INDEX_POWER2)));
29088 #endif // SEG_REUSE_STATS
29089                 goto adjust;
29090             }
29091             
29092             dprintf (SEG_REUSE_LOG_0, ("Adding end of segment (2^%d)", (relative_free_space_index + MIN_INDEX_POWER2)));
29093             ordered_free_space_indices[relative_free_space_index]++;
29094             use_bestfit = try_best_fit(TRUE);
29095             if (use_bestfit)
29096             {
29097                 free_space_items++;
29098                 // Since we might've trimmed away some of the free spaces we had, we should see
29099                 // if we really need to use end of seg space - if it's the same or smaller than
29100                 // the largest space we trimmed we can just add that one back instead of 
29101                 // using end of seg.
29102                 if (relative_free_space_index > trimmed_free_space_index)
29103                 {
29104                     *use_additional_space = TRUE;
29105                 }
29106                 else 
29107                 {
29108                     // If the addition space is <= than the last trimmed space, we
29109                     // should just use that last trimmed space instead.
29110                     saved_ordered_free_space_indices[trimmed_free_space_index]++;
29111                 }
29112             }
29113         }
29114     }
29115
29116 adjust:
29117
29118     if (!use_bestfit)
29119     {
29120         dprintf (SEG_REUSE_LOG_0, ("couldn't fit..."));
29121
29122 #ifdef SEG_REUSE_STATS
29123         size_t saved_max = max_free_space_items;
29124         BOOL temp_bestfit = FALSE;
29125
29126         dprintf (SEG_REUSE_LOG_0, ("----Starting experiment process----"));
29127         dprintf (SEG_REUSE_LOG_0, ("----Couldn't fit with max free items %Id", max_free_space_items));
29128
29129         // TODO: need to take the end of segment into consideration.
29130         while (max_free_space_items <= total_free_space_items)
29131         {
29132             max_free_space_items += max_free_space_items / 2;
29133             dprintf (SEG_REUSE_LOG_0, ("----Temporarily increasing max free spaces to %Id", max_free_space_items));
29134             memcpy (ordered_free_space_indices, 
29135                     saved_all_free_space_indices,
29136                     sizeof(ordered_free_space_indices));
29137             if (try_best_fit(FALSE))
29138             {
29139                 temp_bestfit = TRUE;
29140                 break;
29141             }
29142         }
29143
29144         if (temp_bestfit)
29145         {
29146             dprintf (SEG_REUSE_LOG_0, ("----With %Id max free spaces we could fit", max_free_space_items));
29147         }
29148         else
29149         {
29150             dprintf (SEG_REUSE_LOG_0, ("----Tried all free spaces and still couldn't fit, lost too much space"));
29151         }
29152
29153         dprintf (SEG_REUSE_LOG_0, ("----Restoring max free spaces to %Id", saved_max));
29154         max_free_space_items = saved_max;
29155 #endif // SEG_REUSE_STATS
29156         if (free_space_items)
29157         {
29158             max_free_space_items = min (MAX_NUM_FREE_SPACES, free_space_items * 2);
29159             max_free_space_items = max (max_free_space_items, MIN_NUM_FREE_SPACES);
29160         }
29161         else
29162         {
29163             max_free_space_items = MAX_NUM_FREE_SPACES;
29164         }
29165     }
29166
29167     dprintf (SEG_REUSE_LOG_0, ("Adjusted number of max free spaces to %Id", max_free_space_items));
29168     dprintf (SEG_REUSE_LOG_0, ("------End of best fitting process------\n"));
29169
29170     return use_bestfit;
29171 }
29172
29173 BOOL gc_heap::process_free_space (heap_segment* seg, 
29174                          size_t free_space,
29175                          size_t min_free_size, 
29176                          size_t min_cont_size,
29177                          size_t* total_free_space,
29178                          size_t* largest_free_space)
29179 {
29180     *total_free_space += free_space;
29181     *largest_free_space = max (*largest_free_space, free_space);
29182
29183 #ifdef SIMPLE_DPRINTF
29184     dprintf (SEG_REUSE_LOG_1, ("free space len: %Ix, total free space: %Ix, largest free space: %Ix", 
29185                 free_space, *total_free_space, *largest_free_space));
29186 #endif //SIMPLE_DPRINTF
29187
29188     if ((*total_free_space >= min_free_size) && (*largest_free_space >= min_cont_size))
29189     {
29190 #ifdef SIMPLE_DPRINTF
29191         dprintf (SEG_REUSE_LOG_0, ("(gen%d)total free: %Ix(min: %Ix), largest free: %Ix(min: %Ix). Found segment %Ix to reuse without bestfit", 
29192             settings.condemned_generation,
29193             *total_free_space, min_free_size, *largest_free_space, min_cont_size,
29194             (size_t)seg));
29195 #else
29196         UNREFERENCED_PARAMETER(seg);
29197 #endif //SIMPLE_DPRINTF
29198         return TRUE;
29199     }
29200
29201     int free_space_index = relative_index_power2_free_space (round_down_power2 (free_space));
29202     if (free_space_index != -1)
29203     {
29204         ordered_free_space_indices[free_space_index]++;
29205     }
29206     return FALSE;
29207 }
29208
29209 BOOL gc_heap::expand_reused_seg_p()
29210 {
29211     BOOL reused_seg = FALSE;
29212     int heap_expand_mechanism = gc_data_per_heap.get_mechanism (gc_heap_expand);
29213     if ((heap_expand_mechanism == expand_reuse_bestfit) || 
29214         (heap_expand_mechanism == expand_reuse_normal))
29215     {
29216         reused_seg = TRUE;
29217     }
29218
29219     return reused_seg;
29220 }
29221
29222 BOOL gc_heap::can_expand_into_p (heap_segment* seg, size_t min_free_size, size_t min_cont_size,
29223                                  allocator* gen_allocator)
29224 {
29225     min_cont_size += END_SPACE_AFTER_GC;
29226     use_bestfit = FALSE;
29227     commit_end_of_seg = FALSE;
29228     bestfit_first_pin = 0;
29229     uint8_t* first_address = heap_segment_mem (seg);
29230     uint8_t* end_address   = heap_segment_reserved (seg);
29231     size_t end_extra_space = end_space_after_gc();
29232
29233     if ((heap_segment_reserved (seg) - end_extra_space) <= heap_segment_plan_allocated (seg))
29234     {
29235         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: can't use segment [%Ix %Ix, has less than %d bytes at the end",
29236                                    first_address, end_address, end_extra_space));
29237         return FALSE;
29238     }
29239
29240     end_address -= end_extra_space;
29241
29242     dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p(gen%d): min free: %Ix, min continuous: %Ix", 
29243         settings.condemned_generation, min_free_size, min_cont_size));
29244     size_t eph_gen_starts = eph_gen_starts_size;
29245
29246     if (settings.condemned_generation == max_generation)
29247     {
29248         size_t free_space = 0;
29249         size_t largest_free_space = free_space;
29250         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen2: testing segment [%Ix %Ix", first_address, end_address));
29251         //Look through the pinned plugs for relevant ones and Look for the right pinned plug to start from. 
29252         //We are going to allocate the generation starts in the 1st free space,
29253         //so start from the first free space that's big enough for gen starts and a min object size.
29254         // If we see a free space that is >= gen starts but < gen starts + min obj size we just don't use it - 
29255         // we could use it by allocating the last generation start a bit bigger but 
29256         // the complexity isn't worth the effort (those plugs are from gen2 
29257         // already anyway).
29258         reset_pinned_queue_bos();
29259         mark* m = 0;
29260         BOOL has_fit_gen_starts = FALSE;
29261
29262         init_ordered_free_space_indices ();
29263         while (!pinned_plug_que_empty_p())
29264         {
29265             m = oldest_pin();
29266             if ((pinned_plug (m) >= first_address) && 
29267                 (pinned_plug (m) < end_address) &&
29268                 (pinned_len (m) >= (eph_gen_starts + Align (min_obj_size))))
29269             {
29270                 break;
29271             }
29272             else
29273             {
29274                 deque_pinned_plug();
29275             }
29276         }
29277
29278         if (!pinned_plug_que_empty_p())
29279         {
29280             bestfit_first_pin = pinned_plug (m) - pinned_len (m);
29281
29282             if (process_free_space (seg, 
29283                                     pinned_len (m) - eph_gen_starts, 
29284                                     min_free_size, min_cont_size, 
29285                                     &free_space, &largest_free_space))
29286             {
29287                 return TRUE;
29288             }
29289
29290             deque_pinned_plug();
29291             m = oldest_pin();
29292             has_fit_gen_starts = TRUE;
29293         }
29294
29295         dprintf (3, ("first pin is %Ix", pinned_plug (m)));
29296
29297         //tally up free space
29298         while (!pinned_plug_que_empty_p() &&
29299                ((pinned_plug (m) >= first_address) && (pinned_plug (m) < end_address)))
29300         {
29301             dprintf (3, ("looking at pin %Ix", pinned_plug (m)));
29302             if (process_free_space (seg, 
29303                                     pinned_len (m), 
29304                                     min_free_size, min_cont_size, 
29305                                     &free_space, &largest_free_space))
29306             {
29307                 return TRUE;
29308             }
29309
29310             deque_pinned_plug();
29311             m = oldest_pin();
29312         }
29313
29314         //try to find space at the end of the segment. 
29315         size_t end_space = (end_address - heap_segment_plan_allocated (seg)); 
29316         size_t additional_space = ((min_free_size > free_space) ? (min_free_size - free_space) : 0); 
29317         dprintf (SEG_REUSE_LOG_0, ("end space: %Ix; additional: %Ix", end_space, additional_space));
29318         if (end_space >= additional_space)
29319         {
29320             BOOL can_fit = TRUE;
29321             commit_end_of_seg = TRUE;
29322
29323             if (largest_free_space < min_cont_size)
29324             {
29325                 if (end_space >= min_cont_size)
29326                 {
29327                     additional_space = max (min_cont_size, additional_space);
29328                     dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg for eph", 
29329                         seg));
29330                 }
29331                 else 
29332                 {
29333                     if (settings.concurrent)
29334                     {
29335                         can_fit = FALSE;
29336                         commit_end_of_seg = FALSE;
29337                     }
29338                     else
29339                     {
29340                         size_t additional_space_bestfit = additional_space;
29341                         if (!has_fit_gen_starts)
29342                         {
29343                             if (additional_space_bestfit < (eph_gen_starts + Align (min_obj_size)))
29344                             {
29345                                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, gen starts not allocated yet and end space is too small: %Id",
29346                                         additional_space_bestfit));
29347                                 return FALSE;
29348                             }
29349
29350                             bestfit_first_pin = heap_segment_plan_allocated (seg);
29351                             additional_space_bestfit -= eph_gen_starts;
29352                         }
29353
29354                         can_fit = best_fit (free_space, 
29355                                             largest_free_space,
29356                                             additional_space_bestfit, 
29357                                             &commit_end_of_seg);
29358
29359                         if (can_fit)
29360                         {
29361                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse with bestfit, %s committing end of seg", 
29362                                 seg, (commit_end_of_seg ? "with" : "without")));
29363                         }
29364                         else
29365                         {
29366                             dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
29367                         }
29368                     }
29369                 }
29370             }
29371             else
29372             {
29373                 dprintf (SEG_REUSE_LOG_0, ("(gen2)Found segment %Ix to reuse without bestfit, with committing end of seg", seg));
29374             }
29375
29376             assert (additional_space <= end_space);
29377             if (commit_end_of_seg)
29378             {
29379                 if (!grow_heap_segment (seg, heap_segment_plan_allocated (seg) + additional_space))
29380                 {
29381                     dprintf (2, ("Couldn't commit end of segment?!"));
29382                     use_bestfit = FALSE;
29383  
29384                     return FALSE;
29385                 }
29386
29387                 if (use_bestfit)
29388                 {
29389                     // We increase the index here because growing heap segment could create a discrepency with 
29390                     // the additional space we used (could be bigger).
29391                     size_t free_space_end_of_seg = 
29392                         heap_segment_committed (seg) - heap_segment_plan_allocated (seg);
29393                     int relative_free_space_index = relative_index_power2_free_space (round_down_power2 (free_space_end_of_seg));
29394                     saved_ordered_free_space_indices[relative_free_space_index]++;
29395                 }
29396             }
29397         
29398             if (use_bestfit)
29399             {
29400                 memcpy (ordered_free_space_indices, 
29401                         saved_ordered_free_space_indices, 
29402                         sizeof(ordered_free_space_indices));
29403                 max_free_space_items = max (MIN_NUM_FREE_SPACES, free_space_items * 3 / 2);
29404                 max_free_space_items = min (MAX_NUM_FREE_SPACES, max_free_space_items);
29405                 dprintf (SEG_REUSE_LOG_0, ("could fit! %Id free spaces, %Id max", free_space_items, max_free_space_items));
29406             }
29407
29408             return can_fit;
29409         }
29410
29411         dprintf (SEG_REUSE_LOG_0, ("(gen2)Couldn't fit, total free space is %Ix", (free_space + end_space)));
29412         return FALSE;
29413     }
29414     else
29415     {
29416         assert (settings.condemned_generation == (max_generation-1));
29417         size_t free_space = (end_address - heap_segment_plan_allocated (seg));
29418         size_t largest_free_space = free_space;
29419         dprintf (SEG_REUSE_LOG_0, ("can_expand_into_p: gen1: testing segment [%Ix %Ix", first_address, end_address));
29420         //find the first free list in range of the current segment
29421         size_t sz_list = gen_allocator->first_bucket_size();
29422         unsigned int a_l_idx = 0;
29423         uint8_t* free_list = 0;
29424         for (; a_l_idx < gen_allocator->number_of_buckets(); a_l_idx++)
29425         {
29426             if ((eph_gen_starts <= sz_list) || (a_l_idx == (gen_allocator->number_of_buckets()-1)))
29427             {
29428                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
29429                 while (free_list)
29430                 {
29431                     if ((free_list >= first_address) && 
29432                         (free_list < end_address) && 
29433                         (unused_array_size (free_list) >= eph_gen_starts))
29434                     {
29435                         goto next;
29436                     }
29437                     else
29438                     {
29439                         free_list = free_list_slot (free_list);
29440                     }
29441                 }
29442             }
29443         }
29444 next:
29445         if (free_list)
29446         {
29447             init_ordered_free_space_indices ();
29448             if (process_free_space (seg, 
29449                                     unused_array_size (free_list) - eph_gen_starts + Align (min_obj_size), 
29450                                     min_free_size, min_cont_size, 
29451                                     &free_space, &largest_free_space))
29452             {
29453                 return TRUE;
29454             }
29455
29456             free_list = free_list_slot (free_list);
29457         }
29458         else
29459         {
29460             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, no free list"));
29461             return FALSE;
29462         }
29463
29464        //tally up free space
29465
29466         while (1)
29467         {
29468             while (free_list)
29469             {
29470                 if ((free_list >= first_address) && (free_list < end_address) &&
29471                     process_free_space (seg, 
29472                                         unused_array_size (free_list), 
29473                                         min_free_size, min_cont_size, 
29474                                         &free_space, &largest_free_space))
29475                 {
29476                     return TRUE;
29477                 }
29478
29479                 free_list = free_list_slot (free_list);
29480             }
29481             a_l_idx++;
29482             if (a_l_idx < gen_allocator->number_of_buckets())
29483             {
29484                 free_list = gen_allocator->alloc_list_head_of (a_l_idx);
29485             }
29486             else
29487                 break;
29488         } 
29489
29490         dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
29491         return FALSE;
29492
29493         /*
29494         BOOL can_fit = best_fit (free_space, 0, NULL);
29495         if (can_fit)
29496         {
29497             dprintf (SEG_REUSE_LOG_0, ("(gen1)Found segment %Ix to reuse with bestfit", seg));
29498         }
29499         else
29500         {
29501             dprintf (SEG_REUSE_LOG_0, ("(gen1)Couldn't fit, total free space is %Ix", free_space));
29502         }
29503
29504         return can_fit;
29505         */
29506     }
29507 }
29508
29509 void gc_heap::realloc_plug (size_t last_plug_size, uint8_t*& last_plug,
29510                             generation* gen, uint8_t* start_address,
29511                             unsigned int& active_new_gen_number,
29512                             uint8_t*& last_pinned_gap, BOOL& leftp,
29513                             BOOL shortened_p
29514 #ifdef SHORT_PLUGS
29515                             , mark* pinned_plug_entry
29516 #endif //SHORT_PLUGS
29517                             )
29518 {
29519     // detect generation boundaries
29520     // make sure that active_new_gen_number is not the youngest generation.
29521     // because the generation_limit wouldn't return the right thing in this case.
29522     if (!use_bestfit)
29523     {
29524         if ((active_new_gen_number > 1) &&
29525             (last_plug >= generation_limit (active_new_gen_number)))
29526         {
29527             assert (last_plug >= start_address);
29528             active_new_gen_number--;
29529             realloc_plan_generation_start (generation_of (active_new_gen_number), gen);
29530             assert (generation_plan_allocation_start (generation_of (active_new_gen_number)));
29531             leftp = FALSE;
29532         }
29533     }
29534
29535     // detect pinned plugs
29536     if (!pinned_plug_que_empty_p() && (last_plug == pinned_plug (oldest_pin())))
29537     {
29538         size_t  entry = deque_pinned_plug();
29539         mark*  m = pinned_plug_of (entry);
29540
29541         size_t saved_pinned_len = pinned_len(m);
29542         pinned_len(m) = last_plug - last_pinned_gap;
29543         //dprintf (3,("Adjusting pinned gap: [%Ix, %Ix[", (size_t)last_pinned_gap, (size_t)last_plug));
29544
29545         if (m->has_post_plug_info())
29546         {
29547             last_plug_size += sizeof (gap_reloc_pair);
29548             dprintf (3, ("ra pinned %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
29549         }
29550
29551         last_pinned_gap = last_plug + last_plug_size;
29552         dprintf (3, ("ra found pin %Ix, len: %Ix->%Ix, last_p: %Ix, last_p_size: %Ix",
29553             pinned_plug (m), saved_pinned_len, pinned_len (m), last_plug, last_plug_size));
29554         leftp = FALSE;
29555
29556         //we are creating a generation fault. set the cards.
29557         {
29558             size_t end_card = card_of (align_on_card (last_plug + last_plug_size));
29559             size_t card = card_of (last_plug);
29560             while (card != end_card)
29561             {
29562                 set_card (card);
29563                 card++;
29564             }
29565         }
29566     }
29567     else if (last_plug >= start_address)
29568     {
29569 #ifdef FEATURE_STRUCTALIGN
29570         int requiredAlignment;
29571         ptrdiff_t pad;
29572         node_aligninfo (last_plug, requiredAlignment, pad);
29573
29574         // from how we previously aligned the plug's destination address,
29575         // compute the actual alignment offset.
29576         uint8_t* reloc_plug = last_plug + node_relocation_distance (last_plug);
29577         ptrdiff_t alignmentOffset = ComputeStructAlignPad(reloc_plug, requiredAlignment, 0);
29578         if (!alignmentOffset)
29579         {
29580             // allocate_in_expanded_heap doesn't expect alignmentOffset to be zero.
29581             alignmentOffset = requiredAlignment;
29582         }
29583
29584         //clear the alignment info because we are reallocating
29585         clear_node_aligninfo (last_plug);
29586 #else // FEATURE_STRUCTALIGN
29587         //clear the realignment flag because we are reallocating
29588         clear_node_realigned (last_plug);
29589 #endif // FEATURE_STRUCTALIGN
29590         BOOL adjacentp = FALSE;
29591         BOOL set_padding_on_saved_p = FALSE;
29592
29593         if (shortened_p)
29594         {
29595             last_plug_size += sizeof (gap_reloc_pair);
29596
29597 #ifdef SHORT_PLUGS
29598             assert (pinned_plug_entry != NULL);
29599             if (last_plug_size <= sizeof (plug_and_gap))
29600             {
29601                 set_padding_on_saved_p = TRUE;
29602             }
29603 #endif //SHORT_PLUGS
29604
29605             dprintf (3, ("ra plug %Ix was shortened, adjusting plug size to %Ix", last_plug, last_plug_size))
29606         }
29607
29608 #ifdef SHORT_PLUGS
29609         clear_padding_in_expand (last_plug, set_padding_on_saved_p, pinned_plug_entry);
29610 #endif //SHORT_PLUGS
29611
29612         uint8_t* new_address = allocate_in_expanded_heap(gen, last_plug_size, adjacentp, last_plug,
29613 #ifdef SHORT_PLUGS
29614                                      set_padding_on_saved_p,
29615                                      pinned_plug_entry,
29616 #endif //SHORT_PLUGS
29617                                      TRUE, active_new_gen_number REQD_ALIGN_AND_OFFSET_ARG);
29618
29619         dprintf (3, ("ra NA: [%Ix, %Ix[: %Ix", new_address, (new_address + last_plug_size), last_plug_size));
29620         assert (new_address);
29621         set_node_relocation_distance (last_plug, new_address - last_plug);
29622 #ifdef FEATURE_STRUCTALIGN
29623         if (leftp && node_alignpad (last_plug) == 0)
29624 #else // FEATURE_STRUCTALIGN
29625         if (leftp && !node_realigned (last_plug))
29626 #endif // FEATURE_STRUCTALIGN
29627         {
29628             // TODO - temporarily disable L optimization because of a bug in it.
29629             //set_node_left (last_plug);
29630         }
29631         dprintf (3,(" Re-allocating %Ix->%Ix len %Id", (size_t)last_plug, (size_t)new_address, last_plug_size));
29632         leftp = adjacentp;
29633     }
29634 }
29635
29636 void gc_heap::realloc_in_brick (uint8_t* tree, uint8_t*& last_plug,
29637                                 uint8_t* start_address,
29638                                 generation* gen,
29639                                 unsigned int& active_new_gen_number,
29640                                 uint8_t*& last_pinned_gap, BOOL& leftp)
29641 {
29642     assert (tree != NULL);
29643     int   left_node = node_left_child (tree);
29644     int   right_node = node_right_child (tree);
29645
29646     dprintf (3, ("ra: tree: %Ix, last_pin_gap: %Ix, last_p: %Ix, L: %d, R: %d", 
29647         tree, last_pinned_gap, last_plug, left_node, right_node));
29648
29649     if (left_node)
29650     {
29651         dprintf (3, ("LN: realloc %Ix(%Ix)", (tree + left_node), last_plug));
29652         realloc_in_brick ((tree + left_node), last_plug, start_address,
29653                           gen, active_new_gen_number, last_pinned_gap,
29654                           leftp);
29655     }
29656
29657     if (last_plug != 0)
29658     {
29659         uint8_t*  plug = tree;
29660
29661         BOOL has_pre_plug_info_p = FALSE;
29662         BOOL has_post_plug_info_p = FALSE;
29663         mark* pinned_plug_entry = get_next_pinned_entry (tree, 
29664                                                          &has_pre_plug_info_p,
29665                                                          &has_post_plug_info_p, 
29666                                                          FALSE);
29667
29668         // We only care about the pre plug info 'cause that's what decides if the last plug is shortened.
29669         // The pinned plugs are handled in realloc_plug.
29670         size_t gap_size = node_gap_size (plug);
29671         uint8_t*   gap = (plug - gap_size);
29672         uint8_t*  last_plug_end = gap;
29673         size_t  last_plug_size = (last_plug_end - last_plug);
29674         // Cannot assert this - a plug could be less than that due to the shortened ones.
29675         //assert (last_plug_size >= Align (min_obj_size));
29676         dprintf (3, ("ra: plug %Ix, gap size: %Ix, last_pin_gap: %Ix, last_p: %Ix, last_p_end: %Ix, shortened: %d",
29677             plug, gap_size, last_pinned_gap, last_plug, last_plug_end, (has_pre_plug_info_p ? 1 : 0)));
29678         realloc_plug (last_plug_size, last_plug, gen, start_address,
29679                       active_new_gen_number, last_pinned_gap,
29680                       leftp, has_pre_plug_info_p
29681 #ifdef SHORT_PLUGS
29682                       , pinned_plug_entry
29683 #endif //SHORT_PLUGS
29684                       );
29685     }
29686
29687     last_plug = tree;
29688
29689     if (right_node)
29690     {
29691         dprintf (3, ("RN: realloc %Ix(%Ix)", (tree + right_node), last_plug));
29692         realloc_in_brick ((tree + right_node), last_plug, start_address,
29693                           gen, active_new_gen_number, last_pinned_gap,
29694                           leftp);
29695     }
29696 }
29697
29698 void
29699 gc_heap::realloc_plugs (generation* consing_gen, heap_segment* seg,
29700                         uint8_t* start_address, uint8_t* end_address,
29701                         unsigned active_new_gen_number)
29702 {
29703     dprintf (3, ("--- Reallocing ---"));
29704
29705     if (use_bestfit)
29706     {
29707         //make sure that every generation has a planned allocation start
29708         int  gen_number = max_generation - 1;
29709         while (gen_number >= 0)
29710         {
29711             generation* gen = generation_of (gen_number);
29712             if (0 == generation_plan_allocation_start (gen))
29713             {
29714                 generation_plan_allocation_start (gen) = 
29715                     bestfit_first_pin + (max_generation - gen_number - 1) * Align (min_obj_size);
29716                 generation_plan_allocation_start_size (gen) = Align (min_obj_size);
29717                 assert (generation_plan_allocation_start (gen));
29718             }
29719             gen_number--;
29720         }
29721     }
29722
29723     uint8_t* first_address = start_address;
29724     //Look for the right pinned plug to start from.
29725     reset_pinned_queue_bos();
29726     uint8_t* planned_ephemeral_seg_end = heap_segment_plan_allocated (seg);
29727     while (!pinned_plug_que_empty_p())
29728     {
29729         mark* m = oldest_pin();
29730         if ((pinned_plug (m) >= planned_ephemeral_seg_end) && (pinned_plug (m) < end_address))
29731         {
29732             if (pinned_plug (m) < first_address)
29733             {
29734                 first_address = pinned_plug (m);
29735             }
29736             break;
29737         }
29738         else
29739             deque_pinned_plug();
29740     }
29741
29742     size_t  current_brick = brick_of (first_address);
29743     size_t  end_brick = brick_of (end_address-1);
29744     uint8_t*  last_plug = 0;
29745
29746     uint8_t* last_pinned_gap = heap_segment_plan_allocated (seg);
29747     BOOL leftp = FALSE;
29748
29749     dprintf (3, ("start addr: %Ix, first addr: %Ix, current oldest pin: %Ix",
29750         start_address, first_address, pinned_plug (oldest_pin())));
29751
29752     while (current_brick <= end_brick)
29753     {
29754         int   brick_entry =  brick_table [ current_brick ];
29755         if (brick_entry >= 0)
29756         {
29757             realloc_in_brick ((brick_address (current_brick) + brick_entry - 1),
29758                               last_plug, start_address, consing_gen,
29759                               active_new_gen_number, last_pinned_gap,
29760                               leftp);
29761         }
29762         current_brick++;
29763     }
29764
29765     if (last_plug != 0)
29766     {
29767         realloc_plug (end_address - last_plug, last_plug, consing_gen,
29768                       start_address,
29769                       active_new_gen_number, last_pinned_gap,
29770                       leftp, FALSE
29771 #ifdef SHORT_PLUGS
29772                       , NULL
29773 #endif //SHORT_PLUGS
29774                       );
29775     }
29776
29777     //Fix the old segment allocated size
29778     assert (last_pinned_gap >= heap_segment_mem (seg));
29779     assert (last_pinned_gap <= heap_segment_committed (seg));
29780     heap_segment_plan_allocated (seg) = last_pinned_gap;
29781 }
29782
29783 void gc_heap::verify_no_pins (uint8_t* start, uint8_t* end)
29784 {
29785 #ifdef VERIFY_HEAP
29786     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
29787     {
29788         BOOL contains_pinned_plugs = FALSE;
29789         size_t mi = 0;
29790         mark* m = 0;
29791         while (mi != mark_stack_tos)
29792         {
29793             m = pinned_plug_of (mi);
29794             if ((pinned_plug (m) >= start) && (pinned_plug (m) < end))
29795             {
29796                 contains_pinned_plugs = TRUE;
29797                 break;
29798             }
29799             else
29800                 mi++;
29801         }
29802
29803         if (contains_pinned_plugs)
29804         {
29805             FATAL_GC_ERROR();
29806         }
29807     }
29808 #endif //VERIFY_HEAP
29809 }
29810
29811 void gc_heap::set_expand_in_full_gc (int condemned_gen_number)
29812 {
29813     if (!should_expand_in_full_gc)
29814     {
29815         if ((condemned_gen_number != max_generation) && 
29816             (settings.pause_mode != pause_low_latency) &&
29817             (settings.pause_mode != pause_sustained_low_latency))
29818         {
29819             should_expand_in_full_gc = TRUE;
29820         }
29821     }
29822 }
29823
29824 void gc_heap::save_ephemeral_generation_starts()
29825 {
29826     for (int ephemeral_generation = 0; ephemeral_generation < max_generation; ephemeral_generation++)
29827     {
29828         saved_ephemeral_plan_start[ephemeral_generation] = 
29829             generation_plan_allocation_start (generation_of (ephemeral_generation));
29830         saved_ephemeral_plan_start_size[ephemeral_generation] = 
29831             generation_plan_allocation_start_size (generation_of (ephemeral_generation));
29832     }
29833 }
29834
29835 generation* gc_heap::expand_heap (int condemned_generation,
29836                                   generation* consing_gen,
29837                                   heap_segment* new_heap_segment)
29838 {
29839     UNREFERENCED_PARAMETER(condemned_generation);
29840     assert (condemned_generation >= (max_generation -1));
29841     unsigned int active_new_gen_number = max_generation; //Set one too high to get generation gap
29842     uint8_t*  start_address = generation_limit (max_generation);
29843     uint8_t*  end_address = heap_segment_allocated (ephemeral_heap_segment);
29844     BOOL should_promote_ephemeral = FALSE;
29845     ptrdiff_t eph_size = total_ephemeral_size;
29846 #ifdef BACKGROUND_GC
29847     dprintf(2,("%s: ---- Heap Expansion ----", (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")));
29848 #endif //BACKGROUND_GC
29849     settings.heap_expansion = TRUE;
29850
29851 #ifdef BACKGROUND_GC
29852     if (cm_in_progress)
29853     {
29854         if (!expanded_in_fgc)
29855         {
29856             expanded_in_fgc = TRUE;
29857         }
29858     }
29859 #endif //BACKGROUND_GC
29860
29861     //reset the elevation state for next time.
29862     dprintf (2, ("Elevation: elevation = el_none"));
29863     if (settings.should_lock_elevation && !expand_reused_seg_p())
29864         settings.should_lock_elevation = FALSE;
29865
29866     heap_segment* new_seg = new_heap_segment;
29867
29868     if (!new_seg)
29869         return consing_gen;
29870
29871     //copy the card and brick tables
29872     if (g_gc_card_table!= card_table)
29873         copy_brick_card_table();
29874
29875     BOOL new_segment_p = (heap_segment_next (new_seg) == 0);
29876     dprintf (2, ("new_segment_p %Ix", (size_t)new_segment_p));
29877
29878     assert (generation_plan_allocation_start (generation_of (max_generation-1)));
29879     assert (generation_plan_allocation_start (generation_of (max_generation-1)) >=
29880             heap_segment_mem (ephemeral_heap_segment));
29881     assert (generation_plan_allocation_start (generation_of (max_generation-1)) <=
29882             heap_segment_committed (ephemeral_heap_segment));
29883
29884     assert (generation_plan_allocation_start (youngest_generation));
29885     assert (generation_plan_allocation_start (youngest_generation) <
29886             heap_segment_plan_allocated (ephemeral_heap_segment));
29887
29888     if (settings.pause_mode == pause_no_gc)
29889     {
29890         // We don't reuse for no gc, so the size used on the new eph seg is eph_size.
29891         if ((size_t)(heap_segment_reserved (new_seg) - heap_segment_mem (new_seg)) < (eph_size + soh_allocation_no_gc))
29892             should_promote_ephemeral = TRUE;
29893     }
29894     else
29895     {
29896         if (!use_bestfit)
29897         {
29898             should_promote_ephemeral = dt_low_ephemeral_space_p (tuning_deciding_promote_ephemeral);
29899         }
29900     }
29901
29902     if (should_promote_ephemeral)
29903     {
29904         ephemeral_promotion = TRUE;
29905         get_gc_data_per_heap()->set_mechanism (gc_heap_expand, expand_new_seg_ep);
29906         dprintf (2, ("promoting ephemeral"));
29907         save_ephemeral_generation_starts();
29908     }
29909     else
29910     {
29911         // commit the new ephemeral segment all at once if it is a new one.
29912         if ((eph_size > 0) && new_segment_p)
29913         {
29914 #ifdef FEATURE_STRUCTALIGN
29915             // The destination may require a larger alignment padding than the source.
29916             // Assume the worst possible alignment padding.
29917             eph_size += ComputeStructAlignPad(heap_segment_mem (new_seg), MAX_STRUCTALIGN, OBJECT_ALIGNMENT_OFFSET);
29918 #endif // FEATURE_STRUCTALIGN
29919 #ifdef RESPECT_LARGE_ALIGNMENT
29920             //Since the generation start can be larger than min_obj_size
29921             //The alignment could be switched. 
29922             eph_size += switch_alignment_size(FALSE);
29923 #endif //RESPECT_LARGE_ALIGNMENT
29924             //Since the generation start can be larger than min_obj_size
29925             //Compare the alignment of the first object in gen1 
29926             if (grow_heap_segment (new_seg, heap_segment_mem (new_seg) + eph_size) == 0)
29927             {
29928                 fgm_result.set_fgm (fgm_commit_eph_segment, eph_size, FALSE);
29929                 return consing_gen;
29930             }
29931             heap_segment_used (new_seg) = heap_segment_committed (new_seg);
29932         }
29933
29934         //Fix the end of the old ephemeral heap segment
29935         heap_segment_plan_allocated (ephemeral_heap_segment) =
29936             generation_plan_allocation_start (generation_of (max_generation-1));
29937
29938         dprintf (3, ("Old ephemeral allocated set to %Ix",
29939                     (size_t)heap_segment_plan_allocated (ephemeral_heap_segment)));
29940     }
29941
29942     if (new_segment_p)
29943     {
29944         // TODO - Is this really necessary? We should think about it.
29945         //initialize the first brick
29946         size_t first_brick = brick_of (heap_segment_mem (new_seg));
29947         set_brick (first_brick,
29948                 heap_segment_mem (new_seg) - brick_address (first_brick));
29949     }
29950
29951     //From this point on, we cannot run out of memory
29952
29953     //reset the allocation of the consing generation back to the end of the
29954     //old ephemeral segment
29955     generation_allocation_limit (consing_gen) =
29956         heap_segment_plan_allocated (ephemeral_heap_segment);
29957     generation_allocation_pointer (consing_gen) = generation_allocation_limit (consing_gen);
29958     generation_allocation_segment (consing_gen) = ephemeral_heap_segment;
29959
29960     //clear the generation gap for all of the ephemeral generations
29961     {
29962         int generation_num = max_generation-1;
29963         while (generation_num >= 0)
29964         {
29965             generation* gen = generation_of (generation_num);
29966             generation_plan_allocation_start (gen) = 0;
29967             generation_num--;
29968         }
29969     }
29970
29971     heap_segment* old_seg = ephemeral_heap_segment;
29972     ephemeral_heap_segment = new_seg;
29973
29974     //Note: the ephemeral segment shouldn't be threaded onto the segment chain
29975     //because the relocation and compact phases shouldn't see it
29976
29977     // set the generation members used by allocate_in_expanded_heap
29978     // and switch to ephemeral generation
29979     consing_gen = ensure_ephemeral_heap_segment (consing_gen);
29980
29981     if (!should_promote_ephemeral)
29982     {
29983         realloc_plugs (consing_gen, old_seg, start_address, end_address,
29984                     active_new_gen_number);
29985     }
29986
29987     if (!use_bestfit)
29988     {
29989         repair_allocation_in_expanded_heap (consing_gen);
29990     }
29991
29992     // assert that the generation gap for all of the ephemeral generations were allocated.
29993 #ifdef _DEBUG
29994     {
29995         int generation_num = max_generation-1;
29996         while (generation_num >= 0)
29997         {
29998             generation* gen = generation_of (generation_num);
29999             assert (generation_plan_allocation_start (gen));
30000             generation_num--;
30001         }
30002     }
30003 #endif // _DEBUG
30004
30005     if (!new_segment_p)
30006     {
30007         dprintf (2, ("Demoting ephemeral segment"));
30008         //demote the entire segment.
30009         settings.demotion = TRUE;
30010         get_gc_data_per_heap()->set_mechanism_bit (gc_demotion_bit);
30011         demotion_low = heap_segment_mem (ephemeral_heap_segment);
30012         demotion_high = heap_segment_reserved (ephemeral_heap_segment);
30013     }
30014     else
30015     {
30016         demotion_low = MAX_PTR;
30017         demotion_high = 0;
30018 #ifndef MULTIPLE_HEAPS
30019         settings.demotion = FALSE;
30020         get_gc_data_per_heap()->clear_mechanism_bit (gc_demotion_bit);
30021 #endif //!MULTIPLE_HEAPS
30022     }
30023     ptrdiff_t eph_size1 = total_ephemeral_size;
30024     MAYBE_UNUSED_VAR(eph_size1);
30025
30026     if (!should_promote_ephemeral && new_segment_p)
30027     {
30028         assert (eph_size1 <= eph_size);
30029     }
30030
30031     if (heap_segment_mem (old_seg) == heap_segment_plan_allocated (old_seg))
30032     {
30033         // This is to catch when we accidently delete a segment that has pins.
30034         verify_no_pins (heap_segment_mem (old_seg), heap_segment_reserved (old_seg));
30035     }
30036
30037     verify_no_pins (heap_segment_plan_allocated (old_seg), heap_segment_reserved(old_seg));
30038
30039     dprintf(2,("---- End of Heap Expansion ----"));
30040     return consing_gen;
30041 }
30042
30043 void gc_heap::set_static_data()
30044 {
30045     static_data* pause_mode_sdata = static_data_table[latency_level];
30046     for (int i = 0; i < NUMBERGENERATIONS; i++)
30047     {
30048         dynamic_data* dd = dynamic_data_of (i);
30049         static_data* sdata = &pause_mode_sdata[i];
30050
30051         dd->sdata = sdata;
30052         dd->min_size = sdata->min_size;
30053
30054         dprintf (GTC_LOG, ("PM: %d, gen%d:  min: %Id, max: %Id, fr_l: %Id, fr_b: %d%%",
30055             settings.pause_mode,i, 
30056             dd->min_size, dd_max_size (dd), 
30057             sdata->fragmentation_limit, (int)(sdata->fragmentation_burden_limit * 100)));
30058     }
30059 }
30060
30061 // Initialize the values that are not const.
30062 void gc_heap::init_static_data()
30063 {
30064     size_t gen0_min_size = get_gen0_min_size();
30065
30066     size_t gen0_max_size =
30067 #ifdef MULTIPLE_HEAPS
30068         max (6*1024*1024, min ( Align(soh_segment_size/2), 200*1024*1024));
30069 #else //MULTIPLE_HEAPS
30070         (gc_can_use_concurrent ?
30071             6*1024*1024 :
30072             max (6*1024*1024,  min ( Align(soh_segment_size/2), 200*1024*1024)));
30073 #endif //MULTIPLE_HEAPS
30074
30075     if (heap_hard_limit)
30076     {
30077         size_t gen0_max_size_seg = soh_segment_size / 4;
30078         dprintf (GTC_LOG, ("limit gen0 max %Id->%Id", gen0_max_size, gen0_max_size_seg));
30079         gen0_max_size = min (gen0_max_size, gen0_max_size_seg);
30080     }
30081
30082     size_t gen0_max_size_config = (size_t)GCConfig::GetGCGen0MaxBudget();
30083
30084     if (gen0_max_size_config)
30085     {
30086         gen0_max_size = min (gen0_max_size, gen0_max_size_config);
30087     }
30088
30089     gen0_max_size = Align (gen0_max_size);
30090
30091     gen0_min_size = min (gen0_min_size, gen0_max_size);
30092
30093     // TODO: gen0_max_size has a 200mb cap; gen1_max_size should also have a cap.
30094     size_t gen1_max_size = (size_t)
30095 #ifdef MULTIPLE_HEAPS
30096         max (6*1024*1024, Align(soh_segment_size/2));
30097 #else //MULTIPLE_HEAPS
30098         (gc_can_use_concurrent ?
30099             6*1024*1024 :
30100             max (6*1024*1024, Align(soh_segment_size/2)));
30101 #endif //MULTIPLE_HEAPS
30102
30103     dprintf (GTC_LOG, ("gen0 min: %Id, max: %Id, gen1 max: %Id",
30104         gen0_min_size, gen0_max_size, gen1_max_size));
30105
30106     for (int i = latency_level_first; i <= latency_level_last; i++)
30107     {
30108         static_data_table[i][0].min_size = gen0_min_size;
30109         static_data_table[i][0].max_size = gen0_max_size;
30110         static_data_table[i][1].max_size = gen1_max_size;
30111     }
30112 }
30113
30114 bool gc_heap::init_dynamic_data()
30115 {
30116     qpf = GCToOSInterface::QueryPerformanceFrequency();
30117
30118     uint32_t now = (uint32_t)GetHighPrecisionTimeStamp();
30119
30120     set_static_data();
30121
30122     for (int i = 0; i <= max_generation+1; i++)
30123     {
30124         dynamic_data* dd = dynamic_data_of (i);
30125         dd->gc_clock = 0;
30126         dd->time_clock = now;
30127         dd->current_size = 0;
30128         dd->promoted_size = 0;
30129         dd->collection_count = 0;
30130         dd->new_allocation = dd->min_size;
30131         dd->gc_new_allocation = dd->new_allocation;
30132         dd->desired_allocation = dd->new_allocation;
30133         dd->fragmentation = 0;
30134     }
30135
30136 #ifdef GC_CONFIG_DRIVEN
30137     if (heap_number == 0)
30138         time_init = now;
30139 #endif //GC_CONFIG_DRIVEN
30140
30141     return true;
30142 }
30143
30144 float gc_heap::surv_to_growth (float cst, float limit, float max_limit)
30145 {
30146     if (cst < ((max_limit - limit ) / (limit * (max_limit-1.0f))))
30147         return ((limit - limit*cst) / (1.0f - (cst * limit)));
30148     else
30149         return max_limit;
30150 }
30151
30152
30153 //if the allocation budget wasn't exhausted, the new budget may be wrong because the survival may 
30154 //not be correct (collection happened too soon). Correct with a linear estimation based on the previous 
30155 //value of the budget 
30156 static size_t linear_allocation_model (float allocation_fraction, size_t new_allocation, 
30157                                        size_t previous_desired_allocation, size_t collection_count)
30158 {
30159     if ((allocation_fraction < 0.95) && (allocation_fraction > 0.0))
30160     {
30161         dprintf (2, ("allocation fraction: %d", (int)(allocation_fraction/100.0)));
30162         new_allocation = (size_t)(allocation_fraction*new_allocation + (1.0-allocation_fraction)*previous_desired_allocation);
30163     }
30164 #if 0 
30165     size_t smoothing = 3; // exponential smoothing factor
30166     if (smoothing  > collection_count)
30167         smoothing  = collection_count;
30168     new_allocation = new_allocation / smoothing + ((previous_desired_allocation / smoothing) * (smoothing-1));
30169 #else
30170     UNREFERENCED_PARAMETER(collection_count);
30171 #endif //0
30172     return new_allocation;
30173 }
30174
30175 size_t gc_heap::desired_new_allocation (dynamic_data* dd,
30176                                         size_t out, int gen_number,
30177                                         int pass)
30178 {
30179     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30180
30181     if (dd_begin_data_size (dd) == 0)
30182     {
30183         size_t new_allocation = dd_min_size (dd);
30184         current_gc_data_per_heap->gen_data[gen_number].new_allocation = new_allocation;        
30185         return new_allocation;
30186     }
30187     else
30188     {
30189         float     cst;
30190         size_t    previous_desired_allocation = dd_desired_allocation (dd);
30191         size_t    current_size = dd_current_size (dd);
30192         float     max_limit = dd_max_limit (dd);
30193         float     limit = dd_limit (dd);
30194         size_t    min_gc_size = dd_min_size (dd);
30195         float     f = 0;
30196         size_t    max_size = dd_max_size (dd);
30197         size_t    new_allocation = 0;
30198         float allocation_fraction = (float) (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)) / (float) (dd_desired_allocation (dd));
30199         if (gen_number >= max_generation)
30200         {
30201             size_t    new_size = 0;
30202
30203             cst = min (1.0f, float (out) / float (dd_begin_data_size (dd)));
30204
30205             f = surv_to_growth (cst, limit, max_limit);
30206             size_t max_growth_size = (size_t)(max_size / f);
30207             if (current_size >= max_growth_size)
30208             {
30209                 new_size = max_size;
30210             }
30211             else
30212             {
30213                 new_size = (size_t) min (max ( (f * current_size), min_gc_size), max_size);
30214             }
30215
30216             assert ((new_size >= current_size) || (new_size == max_size));
30217
30218             if (gen_number == max_generation)
30219             {
30220                 new_allocation  =  max((new_size - current_size), min_gc_size);
30221
30222                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
30223                                                           dd_desired_allocation (dd), dd_collection_count (dd));
30224
30225                 if ((dd_fragmentation (dd) > ((size_t)((f-1)*current_size))))
30226                 {
30227                     //reducing allocation in case of fragmentation
30228                     size_t new_allocation1 = max (min_gc_size,
30229                                                   // CAN OVERFLOW
30230                                                   (size_t)((float)new_allocation * current_size /
30231                                                            ((float)current_size + 2*dd_fragmentation (dd))));
30232                     dprintf (2, ("Reducing max_gen allocation due to fragmentation from %Id to %Id",
30233                                  new_allocation, new_allocation1));
30234                     new_allocation = new_allocation1;
30235                 }
30236             }
30237             else //large object heap
30238             {
30239                 uint32_t memory_load = 0;
30240                 uint64_t available_physical = 0;
30241                 get_memory_info (&memory_load, &available_physical);
30242 #ifdef TRACE_GC
30243                 if (heap_hard_limit)
30244                 {
30245                     size_t loh_allocated = 0;
30246                     size_t loh_committed = committed_size (true, &loh_allocated);
30247                     dprintf (1, ("GC#%Id h%d, GMI: LOH budget, LOH commit %Id (obj %Id, frag %Id), total commit: %Id (recorded: %Id)", 
30248                         (size_t)settings.gc_index, heap_number, 
30249                         loh_committed, loh_allocated,
30250                         dd_fragmentation (dynamic_data_of (max_generation + 1)),
30251                         get_total_committed_size(), (current_total_committed - current_total_committed_bookkeeping)));
30252                 }
30253 #endif //TRACE_GC
30254                 if (heap_number == 0)
30255                     settings.exit_memory_load = memory_load;
30256                 if (available_physical > 1024*1024)
30257                     available_physical -= 1024*1024;
30258
30259                 uint64_t available_free = available_physical + (uint64_t)generation_free_list_space (generation_of (gen_number));
30260                 if (available_free > (uint64_t)MAX_PTR)
30261                 {
30262                     available_free = (uint64_t)MAX_PTR;
30263                 }
30264
30265                 //try to avoid OOM during large object allocation
30266                 new_allocation = max (min(max((new_size - current_size), dd_desired_allocation (dynamic_data_of (max_generation))), 
30267                                           (size_t)available_free), 
30268                                       max ((current_size/4), min_gc_size));
30269
30270                 new_allocation = linear_allocation_model (allocation_fraction, new_allocation,
30271                                                           dd_desired_allocation (dd), dd_collection_count (dd));
30272
30273             }
30274         }
30275         else
30276         {
30277             size_t survivors = out;
30278             cst = float (survivors) / float (dd_begin_data_size (dd));
30279             f = surv_to_growth (cst, limit, max_limit);
30280             new_allocation = (size_t) min (max ((f * (survivors)), min_gc_size), max_size);
30281
30282             new_allocation = linear_allocation_model (allocation_fraction, new_allocation, 
30283                                                       dd_desired_allocation (dd), dd_collection_count (dd));
30284
30285             if (gen_number == 0)
30286             {
30287                 if (pass == 0)
30288                 {
30289
30290                     //printf ("%f, %Id\n", cst, new_allocation);
30291                     size_t free_space = generation_free_list_space (generation_of (gen_number));
30292                     // DTREVIEW - is min_gc_size really a good choice? 
30293                     // on 64-bit this will almost always be true.
30294                     dprintf (GTC_LOG, ("frag: %Id, min: %Id", free_space, min_gc_size));
30295                     if (free_space > min_gc_size)
30296                     {
30297                         settings.gen0_reduction_count = 2;
30298                     }
30299                     else
30300                     {
30301                         if (settings.gen0_reduction_count > 0)
30302                             settings.gen0_reduction_count--;
30303                     }
30304                 }
30305                 if (settings.gen0_reduction_count > 0)
30306                 {
30307                     dprintf (2, ("Reducing new allocation based on fragmentation"));
30308                     new_allocation = min (new_allocation,
30309                                           max (min_gc_size, (max_size/3)));
30310                 }
30311             }
30312         }
30313
30314         size_t new_allocation_ret = 
30315             Align (new_allocation, get_alignment_constant (!(gen_number == (max_generation+1))));
30316         int gen_data_index = gen_number;
30317         gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_data_index]);
30318         gen_data->new_allocation = new_allocation_ret;
30319
30320         dd_surv (dd) = cst;
30321
30322 #ifdef SIMPLE_DPRINTF
30323         dprintf (1, ("h%d g%d surv: %Id current: %Id alloc: %Id (%d%%) f: %d%% new-size: %Id new-alloc: %Id",
30324                     heap_number, gen_number, out, current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd)),
30325                     (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
30326 #else
30327         dprintf (1,("gen: %d in: %Id out: %Id ", gen_number, generation_allocation_size (generation_of (gen_number)), out));
30328         dprintf (1,("current: %Id alloc: %Id ", current_size, (dd_desired_allocation (dd) - dd_gc_new_allocation (dd))));
30329         dprintf (1,(" surv: %d%% f: %d%% new-size: %Id new-alloc: %Id",
30330                     (int)(cst*100), (int)(f*100), current_size + new_allocation, new_allocation));
30331 #endif //SIMPLE_DPRINTF
30332
30333         return new_allocation_ret;
30334     }
30335 }
30336
30337 //returns the planned size of a generation (including free list element)
30338 size_t gc_heap::generation_plan_size (int gen_number)
30339 {
30340     if (0 == gen_number)
30341         return max((heap_segment_plan_allocated (ephemeral_heap_segment) -
30342                     generation_plan_allocation_start (generation_of (gen_number))),
30343                    (int)Align (min_obj_size));
30344     else
30345     {
30346         generation* gen = generation_of (gen_number);
30347         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
30348             return (generation_plan_allocation_start (generation_of (gen_number - 1)) -
30349                     generation_plan_allocation_start (generation_of (gen_number)));
30350         else
30351         {
30352             size_t gensize = 0;
30353             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30354
30355             PREFIX_ASSUME(seg != NULL);
30356
30357             while (seg && (seg != ephemeral_heap_segment))
30358             {
30359                 gensize += heap_segment_plan_allocated (seg) -
30360                            heap_segment_mem (seg);
30361                 seg = heap_segment_next_rw (seg);
30362             }
30363             if (seg)
30364             {
30365                 gensize += (generation_plan_allocation_start (generation_of (gen_number - 1)) -
30366                             heap_segment_mem (ephemeral_heap_segment));
30367             }
30368             return gensize;
30369         }
30370     }
30371
30372 }
30373
30374 //returns the size of a generation (including free list element)
30375 size_t gc_heap::generation_size (int gen_number)
30376 {
30377     if (0 == gen_number)
30378         return max((heap_segment_allocated (ephemeral_heap_segment) -
30379                     generation_allocation_start (generation_of (gen_number))),
30380                    (int)Align (min_obj_size));
30381     else
30382     {
30383         generation* gen = generation_of (gen_number);
30384         if (heap_segment_rw (generation_start_segment (gen)) == ephemeral_heap_segment)
30385             return (generation_allocation_start (generation_of (gen_number - 1)) -
30386                     generation_allocation_start (generation_of (gen_number)));
30387         else
30388         {
30389             size_t gensize = 0;
30390             heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30391
30392             PREFIX_ASSUME(seg != NULL);
30393
30394             while (seg && (seg != ephemeral_heap_segment))
30395             {
30396                 gensize += heap_segment_allocated (seg) -
30397                            heap_segment_mem (seg);
30398                 seg = heap_segment_next_rw (seg);
30399             }
30400             if (seg)
30401             {
30402                 gensize += (generation_allocation_start (generation_of (gen_number - 1)) -
30403                             heap_segment_mem (ephemeral_heap_segment));
30404             }
30405
30406             return gensize;
30407         }
30408     }
30409
30410 }
30411
30412 size_t  gc_heap::compute_in (int gen_number)
30413 {
30414     assert (gen_number != 0);
30415     dynamic_data* dd = dynamic_data_of (gen_number);
30416
30417     size_t in = generation_allocation_size (generation_of (gen_number));
30418
30419     if (gen_number == max_generation && ephemeral_promotion)
30420     {
30421         in = 0;
30422         for (int i = 0; i <= max_generation; i++)
30423         {
30424             dynamic_data* dd = dynamic_data_of (i);
30425             in += dd_survived_size (dd);
30426             if (i != max_generation)
30427             {
30428                 generation_condemned_allocated (generation_of (gen_number)) += dd_survived_size (dd);
30429             }
30430         }
30431     }
30432
30433     dd_gc_new_allocation (dd) -= in;
30434     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30435
30436     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30437     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
30438     gen_data->in = in;
30439
30440     generation_allocation_size (generation_of (gen_number)) = 0;
30441     return in;
30442 }
30443
30444 void  gc_heap::compute_promoted_allocation (int gen_number)
30445 {
30446     compute_in (gen_number);
30447 }
30448
30449 #ifdef BIT64
30450 inline
30451 size_t gc_heap::trim_youngest_desired (uint32_t memory_load,
30452                                        size_t total_new_allocation,
30453                                        size_t total_min_allocation)
30454 {
30455     if (memory_load < MAX_ALLOWED_MEM_LOAD)
30456     {
30457         // If the total of memory load and gen0 budget exceeds 
30458         // our max memory load limit, trim the gen0 budget so the total 
30459         // is the max memory load limit.
30460         size_t remain_memory_load = (MAX_ALLOWED_MEM_LOAD - memory_load) * mem_one_percent;
30461         return min (total_new_allocation, remain_memory_load);
30462     }
30463     else
30464     {
30465         return max (mem_one_percent, total_min_allocation);
30466     }
30467 }
30468
30469 size_t gc_heap::joined_youngest_desired (size_t new_allocation)
30470 {
30471     dprintf (2, ("Entry memory load: %d; gen0 new_alloc: %Id", settings.entry_memory_load, new_allocation));
30472
30473     size_t final_new_allocation = new_allocation;
30474     if (new_allocation > MIN_YOUNGEST_GEN_DESIRED)
30475     {
30476         uint32_t num_heaps = 1;
30477
30478 #ifdef MULTIPLE_HEAPS
30479         num_heaps = gc_heap::n_heaps;
30480 #endif //MULTIPLE_HEAPS
30481
30482         size_t total_new_allocation = new_allocation * num_heaps;
30483         size_t total_min_allocation = MIN_YOUNGEST_GEN_DESIRED * num_heaps;
30484
30485         if ((settings.entry_memory_load >= MAX_ALLOWED_MEM_LOAD) ||
30486             (total_new_allocation > max (youngest_gen_desired_th, total_min_allocation)))
30487         {
30488             uint32_t memory_load = 0;
30489             get_memory_info (&memory_load);
30490             settings.exit_memory_load = memory_load;
30491             dprintf (2, ("Current emory load: %d", memory_load));
30492
30493             size_t final_total = 
30494                 trim_youngest_desired (memory_load, total_new_allocation, total_min_allocation);
30495             size_t max_new_allocation = 
30496 #ifdef MULTIPLE_HEAPS
30497                                          dd_max_size (g_heaps[0]->dynamic_data_of (0));
30498 #else //MULTIPLE_HEAPS
30499                                          dd_max_size (dynamic_data_of (0));
30500 #endif //MULTIPLE_HEAPS
30501
30502             final_new_allocation  = min (Align ((final_total / num_heaps), get_alignment_constant (TRUE)), max_new_allocation);
30503         }
30504     }
30505
30506     if (final_new_allocation < new_allocation)
30507     {
30508         settings.gen0_reduction_count = 2;
30509     }
30510
30511     return final_new_allocation;
30512 }
30513 #endif // BIT64 
30514
30515 inline
30516 gc_history_per_heap* gc_heap::get_gc_data_per_heap()
30517 {
30518 #ifdef BACKGROUND_GC
30519     return (settings.concurrent ? &bgc_data_per_heap : &gc_data_per_heap);
30520 #else
30521     return &gc_data_per_heap;
30522 #endif //BACKGROUND_GC
30523 }
30524
30525 void gc_heap::compute_new_dynamic_data (int gen_number)
30526 {
30527     PREFIX_ASSUME(gen_number >= 0);
30528     PREFIX_ASSUME(gen_number <= max_generation);
30529
30530     dynamic_data* dd = dynamic_data_of (gen_number);
30531     generation*   gen = generation_of (gen_number);
30532     size_t        in = (gen_number==0) ? 0 : compute_in (gen_number);
30533
30534     size_t total_gen_size = generation_size (gen_number);
30535     //keep track of fragmentation
30536     dd_fragmentation (dd) = generation_free_list_space (gen) + generation_free_obj_space (gen);
30537     dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
30538
30539     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30540
30541     size_t out = dd_survived_size (dd);
30542
30543     gc_generation_data* gen_data = &(current_gc_data_per_heap->gen_data[gen_number]);
30544     gen_data->size_after = total_gen_size;
30545     gen_data->free_list_space_after = generation_free_list_space (gen);
30546     gen_data->free_obj_space_after = generation_free_obj_space (gen);
30547
30548     if ((settings.pause_mode == pause_low_latency) && (gen_number <= 1))
30549     {
30550         // When we are in the low latency mode, we can still be
30551         // condemning more than gen1's 'cause of induced GCs.
30552         dd_desired_allocation (dd) = low_latency_alloc;
30553     }
30554     else
30555     {
30556         if (gen_number == 0)
30557         {
30558             //compensate for dead finalizable objects promotion.
30559             //they shoudn't be counted for growth.
30560             size_t final_promoted = 0;
30561             final_promoted = min (promoted_bytes (heap_number), out);
30562             // Prefast: this is clear from above but prefast needs to be told explicitly
30563             PREFIX_ASSUME(final_promoted <= out);
30564
30565             dprintf (2, ("gen: %d final promoted: %Id", gen_number, final_promoted));
30566             dd_freach_previous_promotion (dd) = final_promoted;
30567             size_t lower_bound = desired_new_allocation  (dd, out-final_promoted, gen_number, 0);
30568
30569             if (settings.condemned_generation == 0)
30570             {
30571                 //there is no noise.
30572                 dd_desired_allocation (dd) = lower_bound;
30573             }
30574             else
30575             {
30576                 size_t higher_bound = desired_new_allocation (dd, out, gen_number, 1);
30577
30578                 // <TODO>This assert was causing AppDomains\unload\test1n\test1nrun.bat to fail</TODO>
30579                 //assert ( lower_bound <= higher_bound);
30580
30581                 //discount the noise. Change the desired allocation
30582                 //only if the previous value is outside of the range.
30583                 if (dd_desired_allocation (dd) < lower_bound)
30584                 {
30585                     dd_desired_allocation (dd) = lower_bound;
30586                 }
30587                 else if (dd_desired_allocation (dd) > higher_bound)
30588                 {
30589                     dd_desired_allocation (dd) = higher_bound;
30590                 }
30591 #if defined (BIT64) && !defined (MULTIPLE_HEAPS)
30592                 dd_desired_allocation (dd) = joined_youngest_desired (dd_desired_allocation (dd));
30593 #endif // BIT64 && !MULTIPLE_HEAPS
30594                 trim_youngest_desired_low_memory();
30595                 dprintf (2, ("final gen0 new_alloc: %Id", dd_desired_allocation (dd)));
30596             }
30597         }
30598         else
30599         {
30600             dd_desired_allocation (dd) = desired_new_allocation (dd, out, gen_number, 0);
30601         }
30602     }
30603
30604     gen_data->pinned_surv = dd_pinned_survived_size (dd);
30605     gen_data->npinned_surv = dd_survived_size (dd) - dd_pinned_survived_size (dd);
30606
30607     dd_gc_new_allocation (dd) = dd_desired_allocation (dd);
30608     dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30609
30610     //update counter
30611     dd_promoted_size (dd) = out;
30612     if (gen_number == max_generation)
30613     {
30614         dd = dynamic_data_of (max_generation+1);
30615         total_gen_size = generation_size (max_generation + 1);
30616         dd_fragmentation (dd) = generation_free_list_space (large_object_generation) + 
30617                                 generation_free_obj_space (large_object_generation);
30618         dd_current_size (dd) = total_gen_size - dd_fragmentation (dd);
30619         dd_survived_size (dd) = dd_current_size (dd);
30620         in = 0;
30621         out = dd_current_size (dd);
30622         dd_desired_allocation (dd) = desired_new_allocation (dd, out, max_generation+1, 0);
30623         dd_gc_new_allocation (dd) = Align (dd_desired_allocation (dd),
30624                                            get_alignment_constant (FALSE));
30625         dd_new_allocation (dd) = dd_gc_new_allocation (dd);
30626
30627         gen_data = &(current_gc_data_per_heap->gen_data[max_generation+1]);
30628         gen_data->size_after = total_gen_size;
30629         gen_data->free_list_space_after = generation_free_list_space (large_object_generation);
30630         gen_data->free_obj_space_after = generation_free_obj_space (large_object_generation);
30631         gen_data->npinned_surv = out;
30632 #ifdef BACKGROUND_GC
30633         end_loh_size = total_gen_size;
30634 #endif //BACKGROUND_GC
30635         //update counter
30636         dd_promoted_size (dd) = out;
30637     }
30638 }
30639
30640 void gc_heap::trim_youngest_desired_low_memory()
30641 {
30642     if (g_low_memory_status)
30643     {
30644         size_t committed_mem = 0;
30645         heap_segment* seg = generation_start_segment (generation_of (max_generation));
30646         while (seg)
30647         {
30648             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30649             seg = heap_segment_next (seg);
30650         }
30651         seg = generation_start_segment (generation_of (max_generation + 1));
30652         while (seg)
30653         {
30654             committed_mem += heap_segment_committed (seg) - heap_segment_mem (seg);
30655             seg = heap_segment_next (seg);
30656         }
30657
30658         dynamic_data* dd = dynamic_data_of (0);
30659         size_t current = dd_desired_allocation (dd);
30660         size_t candidate = max (Align ((committed_mem / 10), get_alignment_constant(FALSE)), dd_min_size (dd));
30661
30662         dd_desired_allocation (dd) = min (current, candidate);
30663     }
30664 }
30665
30666 void gc_heap::decommit_ephemeral_segment_pages()
30667 {
30668     if (settings.concurrent)
30669     {
30670         return;
30671     }
30672
30673     size_t slack_space = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30674
30675     dynamic_data* dd = dynamic_data_of (0);
30676
30677 #ifndef MULTIPLE_HEAPS
30678     size_t extra_space = (g_low_memory_status ? 0 : (512 * 1024));
30679     size_t decommit_timeout = (g_low_memory_status ? 0 : GC_EPHEMERAL_DECOMMIT_TIMEOUT);
30680     size_t ephemeral_elapsed = dd_time_clock(dd) - gc_last_ephemeral_decommit_time;
30681
30682     if (dd_desired_allocation (dd) > gc_gen0_desired_high)
30683     {
30684         gc_gen0_desired_high = dd_desired_allocation (dd) + extra_space;
30685     }
30686
30687     if (ephemeral_elapsed >= decommit_timeout)
30688     {
30689         slack_space = min (slack_space, gc_gen0_desired_high);
30690
30691         gc_last_ephemeral_decommit_time = dd_time_clock(dd);
30692         gc_gen0_desired_high = 0;
30693     }
30694 #endif //!MULTIPLE_HEAPS
30695
30696     if (settings.condemned_generation >= (max_generation-1))
30697     {
30698         size_t new_slack_space = 
30699 #ifdef BIT64
30700                     max(min(min(soh_segment_size/32, dd_max_size(dd)), (generation_size (max_generation) / 10)), dd_desired_allocation(dd));
30701 #else
30702 #ifdef FEATURE_CORECLR
30703                     dd_desired_allocation (dd);
30704 #else
30705                     dd_max_size (dd);
30706 #endif //FEATURE_CORECLR                                    
30707 #endif // BIT64
30708
30709         slack_space = min (slack_space, new_slack_space);
30710     }
30711
30712     decommit_heap_segment_pages (ephemeral_heap_segment, slack_space);    
30713
30714     gc_history_per_heap* current_gc_data_per_heap = get_gc_data_per_heap();
30715     current_gc_data_per_heap->extra_gen0_committed = heap_segment_committed (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment);
30716 }
30717
30718 //This is meant to be called by decide_on_compacting.
30719
30720 size_t gc_heap::generation_fragmentation (generation* gen,
30721                                           generation* consing_gen,
30722                                           uint8_t* end)
30723 {
30724     size_t frag;
30725     uint8_t* alloc = generation_allocation_pointer (consing_gen);
30726     // If the allocation pointer has reached the ephemeral segment
30727     // fine, otherwise the whole ephemeral segment is considered
30728     // fragmentation
30729     if (in_range_for_segment (alloc, ephemeral_heap_segment))
30730         {
30731             if (alloc <= heap_segment_allocated(ephemeral_heap_segment))
30732                 frag = end - alloc;
30733             else
30734             {
30735                 // case when no survivors, allocated set to beginning
30736                 frag = 0;
30737             }
30738             dprintf (3, ("ephemeral frag: %Id", frag));
30739         }
30740     else
30741         frag = (heap_segment_allocated (ephemeral_heap_segment) -
30742                 heap_segment_mem (ephemeral_heap_segment));
30743     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
30744
30745     PREFIX_ASSUME(seg != NULL);
30746
30747     while (seg != ephemeral_heap_segment)
30748     {
30749         frag += (heap_segment_allocated (seg) -
30750                  heap_segment_plan_allocated (seg));
30751         dprintf (3, ("seg: %Ix, frag: %Id", (size_t)seg,
30752                      (heap_segment_allocated (seg) -
30753                       heap_segment_plan_allocated (seg))));
30754
30755         seg = heap_segment_next_rw (seg);
30756         assert (seg);
30757     }
30758     dprintf (3, ("frag: %Id discounting pinned plugs", frag));
30759     //add the length of the dequeued plug free space
30760     size_t bos = 0;
30761     while (bos < mark_stack_bos)
30762     {
30763         frag += (pinned_len (pinned_plug_of (bos)));
30764         bos++;
30765     }
30766
30767     return frag;
30768 }
30769
30770 // for SOH this returns the total sizes of the generation and its 
30771 // younger generation(s).
30772 // for LOH this returns just LOH size.
30773 size_t gc_heap::generation_sizes (generation* gen)
30774 {
30775     size_t result = 0;
30776     if (generation_start_segment (gen ) == ephemeral_heap_segment)
30777         result = (heap_segment_allocated (ephemeral_heap_segment) -
30778                   generation_allocation_start (gen));
30779     else
30780     {
30781         heap_segment* seg = heap_segment_in_range (generation_start_segment (gen));
30782
30783         PREFIX_ASSUME(seg != NULL);
30784
30785         while (seg)
30786         {
30787             result += (heap_segment_allocated (seg) -
30788                        heap_segment_mem (seg));
30789             seg = heap_segment_next_in_range (seg);
30790         }
30791     }
30792
30793     return result;
30794 }
30795
30796 size_t gc_heap::estimated_reclaim (int gen_number)
30797 {
30798     dynamic_data* dd = dynamic_data_of (gen_number);
30799     size_t gen_allocated = (dd_desired_allocation (dd) - dd_new_allocation (dd));
30800     size_t gen_total_size = gen_allocated + dd_current_size (dd);
30801     size_t est_gen_surv = (size_t)((float) (gen_total_size) * dd_surv (dd));
30802     size_t est_gen_free = gen_total_size - est_gen_surv + dd_fragmentation (dd);
30803
30804     dprintf (GTC_LOG, ("h%d gen%d total size: %Id, est dead space: %Id (s: %d, allocated: %Id), frag: %Id",
30805                 heap_number, gen_number,
30806                 gen_total_size,
30807                 est_gen_free, 
30808                 (int)(dd_surv (dd) * 100),
30809                 gen_allocated,
30810                 dd_fragmentation (dd)));
30811
30812     return est_gen_free;
30813 }
30814
30815 BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
30816                                     size_t fragmentation,
30817                                     BOOL& should_expand)
30818 {
30819     BOOL should_compact = FALSE;
30820     should_expand = FALSE;
30821     generation*   gen = generation_of (condemned_gen_number);
30822     dynamic_data* dd = dynamic_data_of (condemned_gen_number);
30823     size_t gen_sizes     = generation_sizes(gen);
30824     float  fragmentation_burden = ( ((0 == fragmentation) || (0 == gen_sizes)) ? (0.0f) :
30825                                     (float (fragmentation) / gen_sizes) );
30826
30827     dprintf (GTC_LOG, ("h%d g%d fragmentation: %Id (%d%%)", 
30828         heap_number, settings.condemned_generation, 
30829         fragmentation, (int)(fragmentation_burden * 100.0)));
30830
30831 #ifdef STRESS_HEAP
30832     // for pure GC stress runs we need compaction, for GC stress "mix"
30833     // we need to ensure a better mix of compacting and sweeping collections
30834     if (GCStress<cfg_any>::IsEnabled() && !settings.concurrent
30835         && !g_pConfig->IsGCStressMix())
30836         should_compact = TRUE;
30837
30838 #ifdef GC_STATS
30839     // in GC stress "mix" mode, for stress induced collections make sure we 
30840     // keep sweeps and compactions relatively balanced. do not (yet) force sweeps
30841     // against the GC's determination, as it may lead to premature OOMs.
30842     if (g_pConfig->IsGCStressMix() && settings.stress_induced)
30843     {
30844         int compactions = g_GCStatistics.cntCompactFGC+g_GCStatistics.cntCompactNGC;
30845         int sweeps = g_GCStatistics.cntFGC + g_GCStatistics.cntNGC - compactions;
30846         if (compactions < sweeps / 10)
30847         {
30848             should_compact = TRUE;
30849         }
30850     }
30851 #endif // GC_STATS
30852 #endif //STRESS_HEAP
30853
30854     if (GCConfig::GetForceCompact())
30855         should_compact = TRUE;
30856
30857     if ((condemned_gen_number == max_generation) && last_gc_before_oom)
30858     {
30859         should_compact = TRUE;
30860         last_gc_before_oom = FALSE;
30861         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_last_gc);
30862     }
30863
30864     if (settings.reason == reason_induced_compacting)
30865     {
30866         dprintf (2, ("induced compacting GC"));
30867         should_compact = TRUE;
30868         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_induced_compacting);
30869     }
30870
30871     if (settings.reason == reason_pm_full_gc)
30872     {
30873         assert (condemned_gen_number == max_generation);
30874         if (heap_number == 0)
30875         {
30876             dprintf (GTC_LOG, ("PM doing compacting full GC after a gen1"));
30877         }
30878         should_compact = TRUE;
30879     }
30880
30881     dprintf (2, ("Fragmentation: %d Fragmentation burden %d%%",
30882                 fragmentation, (int) (100*fragmentation_burden)));
30883
30884     if (provisional_mode_triggered && (condemned_gen_number == (max_generation - 1)))
30885     {
30886         dprintf (GTC_LOG, ("gen1 in PM always compact"));
30887         should_compact = TRUE;
30888     }
30889
30890     if (!should_compact)
30891     {
30892         if (dt_low_ephemeral_space_p (tuning_deciding_compaction))
30893         {
30894             dprintf(GTC_LOG, ("compacting due to low ephemeral"));
30895             should_compact = TRUE;
30896             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_low_ephemeral);
30897         }
30898     }
30899
30900     if (should_compact)
30901     {
30902         if ((condemned_gen_number >= (max_generation - 1)))
30903         {
30904             if (dt_low_ephemeral_space_p (tuning_deciding_expansion))
30905             {
30906                 dprintf (GTC_LOG,("Not enough space for all ephemeral generations with compaction"));
30907                 should_expand = TRUE;
30908             }
30909         }
30910     }
30911
30912 #ifdef BIT64
30913     BOOL high_memory = FALSE;
30914 #endif // BIT64
30915
30916     if (!should_compact)
30917     {
30918         // We are not putting this in dt_high_frag_p because it's not exactly
30919         // high fragmentation - it's just enough planned fragmentation for us to 
30920         // want to compact. Also the "fragmentation" we are talking about here
30921         // is different from anywhere else.
30922         BOOL frag_exceeded = ((fragmentation >= dd_fragmentation_limit (dd)) &&
30923                                 (fragmentation_burden >= dd_fragmentation_burden_limit (dd)));
30924
30925         if (frag_exceeded)
30926         {
30927 #ifdef BACKGROUND_GC
30928             // do not force compaction if this was a stress-induced GC
30929             IN_STRESS_HEAP(if (!settings.stress_induced))
30930             {
30931 #endif // BACKGROUND_GC
30932             assert (settings.concurrent == FALSE);
30933             should_compact = TRUE;
30934             get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_frag);
30935 #ifdef BACKGROUND_GC
30936             }
30937 #endif // BACKGROUND_GC
30938         }
30939
30940 #ifdef BIT64
30941         // check for high memory situation
30942         if(!should_compact)
30943         {
30944             uint32_t num_heaps = 1;
30945 #ifdef MULTIPLE_HEAPS
30946             num_heaps = gc_heap::n_heaps;
30947 #endif // MULTIPLE_HEAPS
30948             
30949             ptrdiff_t reclaim_space = generation_size(max_generation) - generation_plan_size(max_generation);
30950
30951             if((settings.entry_memory_load >= high_memory_load_th) && (settings.entry_memory_load < v_high_memory_load_th))
30952             {
30953                 if(reclaim_space > (int64_t)(min_high_fragmentation_threshold (entry_available_physical_mem, num_heaps)))
30954                 {
30955                     dprintf(GTC_LOG,("compacting due to fragmentation in high memory"));
30956                     should_compact = TRUE;
30957                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_high_mem_frag);
30958                 }
30959                 high_memory = TRUE;
30960             }
30961             else if(settings.entry_memory_load >= v_high_memory_load_th)
30962             {
30963                 if(reclaim_space > (ptrdiff_t)(min_reclaim_fragmentation_threshold (num_heaps)))
30964                 {
30965                     dprintf(GTC_LOG,("compacting due to fragmentation in very high memory"));
30966                     should_compact = TRUE;
30967                     get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_vhigh_mem_frag);
30968                 }
30969                 high_memory = TRUE;
30970             }
30971         }
30972 #endif // BIT64
30973     }
30974
30975     // The purpose of calling ensure_gap_allocation here is to make sure
30976     // that we actually are able to commit the memory to allocate generation
30977     // starts.
30978     if ((should_compact == FALSE) &&
30979         (ensure_gap_allocation (condemned_gen_number) == FALSE))
30980     {
30981         should_compact = TRUE;
30982         get_gc_data_per_heap()->set_mechanism (gc_heap_compact, compact_no_gaps);
30983     }
30984
30985     if (settings.condemned_generation == max_generation)
30986     {
30987         //check the progress
30988         if (
30989 #ifdef BIT64
30990             (high_memory && !should_compact) ||
30991 #endif // BIT64
30992             (generation_plan_allocation_start (generation_of (max_generation - 1)) >= 
30993                 generation_allocation_start (generation_of (max_generation - 1))))
30994         {
30995             dprintf (1, ("gen1 start %Ix->%Ix, gen2 size %Id->%Id, lock elevation",
30996                     generation_allocation_start (generation_of (max_generation - 1)),
30997                     generation_plan_allocation_start (generation_of (max_generation - 1)),
30998                      generation_size (max_generation),
30999                      generation_plan_size (max_generation)));
31000             //no progress -> lock
31001             settings.should_lock_elevation = TRUE;
31002         }
31003     }
31004
31005     if (settings.pause_mode == pause_no_gc)
31006     {
31007         should_compact = TRUE;
31008         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_plan_allocated (ephemeral_heap_segment))
31009             < soh_allocation_no_gc)
31010         {
31011             should_expand = TRUE;
31012         }
31013     }
31014
31015     dprintf (2, ("will %s(%s)", (should_compact ? "compact" : "sweep"), (should_expand ? "ex" : "")));
31016     return should_compact;
31017 }
31018
31019 size_t align_lower_good_size_allocation (size_t size)
31020 {
31021     return (size/64)*64;
31022 }
31023
31024 size_t gc_heap::approximate_new_allocation()
31025 {
31026     dynamic_data* dd0 = dynamic_data_of (0);
31027     return max (2*dd_min_size (dd0), ((dd_desired_allocation (dd0)*2)/3));
31028 }
31029
31030 BOOL gc_heap::sufficient_space_end_seg (uint8_t* start, uint8_t* seg_end, size_t end_space_required, gc_tuning_point tp)
31031 {
31032     BOOL can_fit = FALSE;
31033     size_t end_seg_space = (size_t)(seg_end - start);
31034     if (end_seg_space > end_space_required)
31035     {
31036         // If hard limit is specified, and if we attributed all that's left in commit to the ephemeral seg
31037         // so we treat that as segment end, do we have enough space.
31038         if (heap_hard_limit)
31039         {
31040             size_t left_in_commit = heap_hard_limit - current_total_committed;
31041             int num_heaps = 1;
31042 #ifdef MULTIPLE_HEAPS
31043             num_heaps = n_heaps;
31044 #endif //MULTIPLE_HEAPS
31045             left_in_commit /= num_heaps;
31046             if (left_in_commit > end_space_required)
31047             {
31048                 can_fit = TRUE;
31049             }
31050
31051             dprintf (2, ("h%d end seg %Id, but only %Id left in HARD LIMIT commit, required: %Id %s on eph (%d)",
31052                 heap_number, end_seg_space, 
31053                 left_in_commit, end_space_required, 
31054                 (can_fit ? "ok" : "short"), (int)tp));
31055         }
31056         else
31057             can_fit = TRUE;
31058     }
31059
31060     return can_fit;
31061 }
31062
31063 // After we did a GC we expect to have at least this 
31064 // much space at the end of the segment to satisfy
31065 // a reasonable amount of allocation requests.
31066 size_t gc_heap::end_space_after_gc()
31067 {
31068     return max ((dd_min_size (dynamic_data_of (0))/2), (END_SPACE_AFTER_GC + Align (min_obj_size)));
31069 }
31070
31071 BOOL gc_heap::ephemeral_gen_fit_p (gc_tuning_point tp)
31072 {
31073     uint8_t* start = 0;
31074
31075     if ((tp == tuning_deciding_condemned_gen) ||
31076         (tp == tuning_deciding_compaction))
31077     {
31078         start = (settings.concurrent ? alloc_allocated : heap_segment_allocated (ephemeral_heap_segment));
31079         if (settings.concurrent)
31080         {
31081             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (alloc_allocated)", 
31082                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
31083         }
31084         else
31085         {
31086             dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment (allocated)", 
31087                 (size_t)(heap_segment_reserved (ephemeral_heap_segment) - heap_segment_allocated (ephemeral_heap_segment))));
31088         }
31089     }
31090     else if (tp == tuning_deciding_expansion)
31091     {
31092         start = heap_segment_plan_allocated (ephemeral_heap_segment);
31093         dprintf (GTC_LOG, ("%Id left at the end of ephemeral segment based on plan", 
31094             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - start)));
31095     }
31096     else
31097     {
31098         assert (tp == tuning_deciding_full_gc);
31099         dprintf (GTC_LOG, ("FGC: %Id left at the end of ephemeral segment (alloc_allocated)", 
31100             (size_t)(heap_segment_reserved (ephemeral_heap_segment) - alloc_allocated)));
31101         start = alloc_allocated;
31102     }
31103     
31104     if (start == 0) // empty ephemeral generations
31105     {
31106         assert (tp == tuning_deciding_expansion);
31107         // if there are no survivors in the ephemeral segment, 
31108         // this should be the beginning of ephemeral segment.
31109         start = generation_allocation_pointer (generation_of (max_generation));
31110         assert (start == heap_segment_mem (ephemeral_heap_segment));
31111     }
31112
31113     if (tp == tuning_deciding_expansion)
31114     {
31115         assert (settings.condemned_generation >= (max_generation-1));
31116         size_t gen0size = approximate_new_allocation();
31117         size_t eph_size = gen0size;
31118         size_t gen_min_sizes = 0;
31119
31120         for (int j = 1; j <= max_generation-1; j++)
31121         {
31122             gen_min_sizes += 2*dd_min_size (dynamic_data_of(j));
31123         }
31124
31125         eph_size += gen_min_sizes;
31126
31127         dprintf (3, ("h%d deciding on expansion, need %Id (gen0: %Id, 2*min: %Id)", 
31128             heap_number, gen0size, gen_min_sizes, eph_size));
31129         
31130         // We must find room for one large object and enough room for gen0size
31131         if ((size_t)(heap_segment_reserved (ephemeral_heap_segment) - start) > eph_size)
31132         {
31133             dprintf (3, ("Enough room before end of segment"));
31134             return TRUE;
31135         }
31136         else
31137         {
31138             size_t room = align_lower_good_size_allocation
31139                 (heap_segment_reserved (ephemeral_heap_segment) - start);
31140             size_t end_seg = room;
31141
31142             //look at the plug free space
31143             size_t largest_alloc = END_SPACE_AFTER_GC + Align (min_obj_size);
31144             bool large_chunk_found = FALSE;
31145             size_t bos = 0;
31146             uint8_t* gen0start = generation_plan_allocation_start (youngest_generation);
31147             dprintf (3, ("ephemeral_gen_fit_p: gen0 plan start: %Ix", (size_t)gen0start));
31148             if (gen0start == 0)
31149                 return FALSE;
31150             dprintf (3, ("ephemeral_gen_fit_p: room before free list search %Id, needed: %Id",
31151                          room, gen0size));
31152             while ((bos < mark_stack_bos) &&
31153                    !((room >= gen0size) && large_chunk_found))
31154             {
31155                 uint8_t* plug = pinned_plug (pinned_plug_of (bos));
31156                 if (in_range_for_segment (plug, ephemeral_heap_segment))
31157                 {
31158                     if (plug >= gen0start)
31159                     {
31160                         size_t chunk = align_lower_good_size_allocation (pinned_len (pinned_plug_of (bos)));
31161                         room += chunk;
31162                         if (!large_chunk_found)
31163                         {
31164                             large_chunk_found = (chunk >= largest_alloc);
31165                         }
31166                         dprintf (3, ("ephemeral_gen_fit_p: room now %Id, large chunk: %Id",
31167                                      room, large_chunk_found));
31168                     }
31169                 }
31170                 bos++;
31171             }
31172
31173             if (room >= gen0size)
31174             {
31175                 if (large_chunk_found)
31176                 {
31177                     sufficient_gen0_space_p = TRUE;
31178
31179                     dprintf (3, ("Enough room"));
31180                     return TRUE;
31181                 }
31182                 else
31183                 {
31184                     // now we need to find largest_alloc at the end of the segment.
31185                     if (end_seg >= end_space_after_gc())
31186                     {
31187                         dprintf (3, ("Enough room (may need end of seg)"));
31188                         return TRUE;
31189                     }
31190                 }
31191             }
31192
31193             dprintf (3, ("Not enough room"));
31194                 return FALSE;
31195         }
31196     }
31197     else
31198     {
31199         size_t end_space = 0;
31200         dynamic_data* dd = dynamic_data_of (0);
31201         if ((tp == tuning_deciding_condemned_gen) ||
31202             (tp == tuning_deciding_full_gc))
31203         {
31204             end_space = max (2*dd_min_size (dd), end_space_after_gc());
31205         }
31206         else
31207         {
31208             assert (tp == tuning_deciding_compaction);
31209             end_space = approximate_new_allocation();
31210         }
31211
31212         BOOL can_fit = sufficient_space_end_seg (start, heap_segment_reserved (ephemeral_heap_segment), end_space, tp);
31213
31214         return can_fit;
31215     }
31216 }
31217
31218 CObjectHeader* gc_heap::allocate_large_object (size_t jsize, int64_t& alloc_bytes)
31219 {
31220     //create a new alloc context because gen3context is shared.
31221     alloc_context acontext;
31222     acontext.alloc_ptr = 0;
31223     acontext.alloc_limit = 0;
31224     acontext.alloc_bytes = 0;
31225 #ifdef MULTIPLE_HEAPS
31226     acontext.set_alloc_heap(vm_heap);
31227 #endif //MULTIPLE_HEAPS
31228
31229 #if BIT64
31230     size_t maxObjectSize = (INT64_MAX - 7 - Align(min_obj_size));
31231 #else
31232     size_t maxObjectSize = (INT32_MAX - 7 - Align(min_obj_size));
31233 #endif
31234
31235     if (jsize >= maxObjectSize)
31236     {
31237         if (GCConfig::GetBreakOnOOM())
31238         {
31239             GCToOSInterface::DebugBreak();
31240         }
31241         return NULL;
31242     }
31243
31244     size_t size = AlignQword (jsize);
31245     int align_const = get_alignment_constant (FALSE);
31246 #ifdef FEATURE_LOH_COMPACTION
31247     size_t pad = Align (loh_padding_obj_size, align_const);
31248 #else
31249     size_t pad = 0;
31250 #endif //FEATURE_LOH_COMPACTION
31251
31252     assert (size >= Align (min_obj_size, align_const));
31253 #ifdef _MSC_VER
31254 #pragma inline_depth(0)
31255 #endif //_MSC_VER
31256     if (! allocate_more_space (&acontext, (size + pad), max_generation+1))
31257     {
31258         return 0;
31259     }
31260
31261 #ifdef _MSC_VER
31262 #pragma inline_depth(20)
31263 #endif //_MSC_VER
31264
31265 #ifdef MARK_ARRAY
31266     uint8_t* current_lowest_address = lowest_address;
31267     uint8_t* current_highest_address = highest_address;
31268 #ifdef BACKGROUND_GC
31269     if (recursive_gc_sync::background_running_p())
31270     {
31271         current_lowest_address = background_saved_lowest_address;
31272         current_highest_address = background_saved_highest_address;
31273     }
31274 #endif //BACKGROUND_GC
31275 #endif // MARK_ARRAY
31276
31277 #ifdef FEATURE_LOH_COMPACTION
31278     // The GC allocator made a free object already in this alloc context and
31279     // adjusted the alloc_ptr accordingly.
31280 #endif //FEATURE_LOH_COMPACTION
31281
31282     uint8_t*  result = acontext.alloc_ptr;
31283
31284     assert ((size_t)(acontext.alloc_limit - acontext.alloc_ptr) == size);
31285     alloc_bytes += size;
31286
31287     CObjectHeader* obj = (CObjectHeader*)result;
31288
31289 #ifdef MARK_ARRAY
31290     if (recursive_gc_sync::background_running_p())
31291     {
31292         if ((result < current_highest_address) && (result >= current_lowest_address))
31293         {
31294             dprintf (3, ("Clearing mark bit at address %Ix",
31295                      (size_t)(&mark_array [mark_word_of (result)])));
31296
31297             mark_array_clear_marked (result);
31298         }
31299 #ifdef BACKGROUND_GC
31300         //the object has to cover one full mark uint32_t
31301         assert (size > mark_word_size);
31302         if (current_c_gc_state != c_gc_state_free)
31303         {
31304             dprintf (3, ("Concurrent allocation of a large object %Ix",
31305                         (size_t)obj));
31306             //mark the new block specially so we know it is a new object
31307             if ((result < current_highest_address) && (result >= current_lowest_address))
31308             {
31309                 dprintf (3, ("Setting mark bit at address %Ix",
31310                             (size_t)(&mark_array [mark_word_of (result)])));
31311     
31312                 mark_array_set_marked (result);
31313             }
31314         }
31315 #endif //BACKGROUND_GC
31316     }
31317 #endif //MARK_ARRAY
31318
31319     assert (obj != 0);
31320     assert ((size_t)obj == Align ((size_t)obj, align_const));
31321
31322     return obj;
31323 }
31324
31325 void reset_memory (uint8_t* o, size_t sizeo)
31326 {
31327     if (sizeo > 128 * 1024)
31328     {
31329         // We cannot reset the memory for the useful part of a free object.
31330         size_t size_to_skip = min_free_list - plug_skew;
31331
31332         size_t page_start = align_on_page ((size_t)(o + size_to_skip));
31333         size_t size = align_lower_page ((size_t)o + sizeo - size_to_skip - plug_skew) - page_start;
31334         // Note we need to compensate for an OS bug here. This bug would cause the MEM_RESET to fail
31335         // on write watched memory.
31336         if (reset_mm_p)
31337         {
31338 #ifdef MULTIPLE_HEAPS
31339             bool unlock_p = true;
31340 #else
31341             // We don't do unlock because there could be many processes using workstation GC and it's
31342             // bad perf to have many threads doing unlock at the same time.
31343             bool unlock_p = false;
31344 #endif //MULTIPLE_HEAPS
31345
31346             reset_mm_p = GCToOSInterface::VirtualReset((void*)page_start, size, unlock_p);
31347         }
31348     }
31349 }
31350
31351 void gc_heap::reset_large_object (uint8_t* o)
31352 {
31353     // If it's a large object, allow the O/S to discard the backing store for these pages.
31354     reset_memory (o, size(o));
31355 }
31356
31357 BOOL gc_heap::large_object_marked (uint8_t* o, BOOL clearp)
31358 {
31359     BOOL m = FALSE;
31360     // It shouldn't be necessary to do these comparisons because this is only used for blocking
31361     // GCs and LOH segments cannot be out of range.
31362     if ((o >= lowest_address) && (o < highest_address))
31363     {
31364         if (marked (o))
31365         {
31366             if (clearp)
31367             {
31368                 clear_marked (o);
31369                 if (pinned (o))
31370                     clear_pinned(o);
31371             }
31372             m = TRUE;
31373         }
31374         else
31375             m = FALSE;
31376     }
31377     else
31378         m = TRUE;
31379     return m;
31380 }
31381
31382 void gc_heap::walk_survivors_relocation (void* profiling_context, record_surv_fn fn)
31383 {
31384     // Now walk the portion of memory that is actually being relocated.
31385     walk_relocation (profiling_context, fn);
31386
31387 #ifdef FEATURE_LOH_COMPACTION
31388     if (loh_compacted_p)
31389     {
31390         walk_relocation_for_loh (profiling_context, fn);
31391     }
31392 #endif //FEATURE_LOH_COMPACTION
31393 }
31394
31395 void gc_heap::walk_survivors_for_loh (void* profiling_context, record_surv_fn fn)
31396 {
31397     generation* gen        = large_object_generation;
31398     heap_segment* seg      = heap_segment_rw (generation_start_segment (gen));;
31399
31400     PREFIX_ASSUME(seg != NULL);
31401
31402     uint8_t* o                = generation_allocation_start (gen);
31403     uint8_t* plug_end         = o;
31404     uint8_t* plug_start       = o;
31405
31406     while (1)
31407     {
31408         if (o >= heap_segment_allocated (seg))
31409         {
31410             seg = heap_segment_next (seg);
31411             if (seg == 0)
31412                 break;
31413             else
31414                 o = heap_segment_mem (seg);
31415         }
31416         if (large_object_marked(o, FALSE))
31417         {
31418             plug_start = o;
31419
31420             BOOL m = TRUE;
31421             while (m)
31422             {
31423                 o = o + AlignQword (size (o));
31424                 if (o >= heap_segment_allocated (seg))
31425                 {
31426                     break;
31427                 }
31428                 m = large_object_marked (o, FALSE);
31429             }
31430
31431             plug_end = o;
31432
31433             fn (plug_start, plug_end, 0, profiling_context, false, false);
31434         }
31435         else
31436         {
31437             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
31438             {
31439                 o = o + AlignQword (size (o));
31440             }
31441         }
31442     }
31443 }
31444
31445 #ifdef BACKGROUND_GC
31446
31447 BOOL gc_heap::background_object_marked (uint8_t* o, BOOL clearp)
31448 {
31449     BOOL m = FALSE;
31450     if ((o >= background_saved_lowest_address) && (o < background_saved_highest_address))
31451     {
31452         if (mark_array_marked (o))
31453         {
31454             if (clearp)
31455             {
31456                 mark_array_clear_marked (o);
31457                 //dprintf (3, ("mark array bit for object %Ix is cleared", o));
31458                 dprintf (3, ("CM: %Ix", o));
31459             }
31460             m = TRUE;
31461         }
31462         else
31463             m = FALSE;
31464     }
31465     else
31466         m = TRUE;
31467
31468     dprintf (3, ("o %Ix(%d) %s", o, size(o), (m ? "was bm" : "was NOT bm")));
31469     return m;
31470 }
31471
31472 void gc_heap::background_delay_delete_loh_segments()
31473 {
31474     generation* gen = large_object_generation;
31475     heap_segment* seg = heap_segment_rw (generation_start_segment (large_object_generation));
31476     heap_segment* prev_seg = 0;
31477
31478     while (seg)
31479     {
31480         heap_segment* next_seg = heap_segment_next (seg);
31481         if (seg->flags & heap_segment_flags_loh_delete)
31482         {
31483             dprintf (3, ("deleting %Ix-%Ix-%Ix", (size_t)seg, heap_segment_allocated (seg), heap_segment_reserved (seg)));
31484             delete_heap_segment (seg, (GCConfig::GetRetainVM() != 0));
31485             heap_segment_next (prev_seg) = next_seg;
31486         }
31487         else
31488         {
31489             prev_seg = seg;
31490         }
31491
31492         seg = next_seg;
31493     }
31494 }
31495
31496 uint8_t* gc_heap::background_next_end (heap_segment* seg, BOOL large_objects_p)
31497 {
31498     return
31499         (large_objects_p ? heap_segment_allocated (seg) : heap_segment_background_allocated (seg));
31500 }
31501
31502 void gc_heap::set_mem_verify (uint8_t* start, uint8_t* end, uint8_t b)
31503 {
31504 #ifdef VERIFY_HEAP
31505     if (end > start)
31506     {
31507         if ((GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC) &&
31508            !(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_NO_MEM_FILL))
31509         {
31510             dprintf (3, ("setting mem to %c [%Ix, [%Ix", b, start, end));
31511             memset (start, b, (end - start));
31512         }
31513     }
31514 #endif //VERIFY_HEAP
31515 }
31516
31517 void gc_heap::generation_delete_heap_segment (generation* gen, 
31518                                               heap_segment* seg,
31519                                               heap_segment* prev_seg,
31520                                               heap_segment* next_seg)
31521 {
31522     dprintf (3, ("bgc sweep: deleting seg %Ix", seg));
31523     if (gen == large_object_generation)
31524     {
31525         dprintf (3, ("Preparing empty large segment %Ix for deletion", (size_t)seg));
31526
31527         // We cannot thread segs in here onto freeable_large_heap_segment because 
31528         // grow_brick_card_tables could be committing mark array which needs to read 
31529         // the seg list. So we delay it till next time we suspend EE.
31530         seg->flags |= heap_segment_flags_loh_delete;
31531         // Since we will be decommitting the seg, we need to prevent heap verification
31532         // to verify this segment.
31533         heap_segment_allocated (seg) = heap_segment_mem (seg);
31534     }
31535     else
31536     {
31537         if (seg == ephemeral_heap_segment)
31538         {
31539             FATAL_GC_ERROR();
31540         }
31541
31542         heap_segment_next (next_seg) = prev_seg;
31543
31544         dprintf (3, ("Preparing empty small segment %Ix for deletion", (size_t)seg));
31545         heap_segment_next (seg) = freeable_small_heap_segment;
31546         freeable_small_heap_segment = seg;
31547     }
31548
31549     decommit_heap_segment (seg);
31550     seg->flags |= heap_segment_flags_decommitted;
31551
31552     set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
31553 }
31554
31555 void gc_heap::process_background_segment_end (heap_segment* seg, 
31556                                           generation* gen,
31557                                           uint8_t* last_plug_end,
31558                                           heap_segment* start_seg,
31559                                           BOOL* delete_p)
31560 {
31561     *delete_p = FALSE;
31562     uint8_t* allocated = heap_segment_allocated (seg);
31563     uint8_t* background_allocated = heap_segment_background_allocated (seg);
31564     BOOL loh_p = heap_segment_loh_p (seg);
31565
31566     dprintf (3, ("Processing end of background segment [%Ix, %Ix[(%Ix[)", 
31567                 (size_t)heap_segment_mem (seg), background_allocated, allocated));
31568
31569     if (!loh_p && (allocated != background_allocated))
31570     {
31571         assert (gen != large_object_generation);
31572
31573         dprintf (3, ("Make a free object before newly promoted objects [%Ix, %Ix[", 
31574                     (size_t)last_plug_end, background_allocated));
31575         thread_gap (last_plug_end, background_allocated - last_plug_end, generation_of (max_generation));
31576
31577
31578         fix_brick_to_highest (last_plug_end, background_allocated);
31579
31580         // When we allowed fgc's during going through gaps, we could have erased the brick
31581         // that corresponds to bgc_allocated 'cause we had to update the brick there, 
31582         // recover it here.
31583         fix_brick_to_highest (background_allocated, background_allocated);
31584     }
31585     else
31586     {
31587         // by default, if allocated == background_allocated, it can't
31588         // be the ephemeral segment.
31589         if (seg == ephemeral_heap_segment)
31590         {
31591             FATAL_GC_ERROR();
31592         }
31593
31594         if (allocated == heap_segment_mem (seg))
31595         {
31596             // this can happen with LOH segments when multiple threads
31597             // allocate new segments and not all of them were needed to
31598             // satisfy allocation requests.
31599             assert (gen == large_object_generation);
31600         }
31601
31602         if (last_plug_end == heap_segment_mem (seg))
31603         {
31604             dprintf (3, ("Segment allocated is %Ix (beginning of this seg) - %s be deleted",
31605                         (size_t)allocated, (*delete_p ? "should" : "should not")));
31606
31607             if (seg != start_seg)
31608             {
31609                 *delete_p = TRUE;
31610             }
31611         }
31612         else
31613         {
31614             dprintf (3, ("Trimming seg to %Ix[", (size_t)last_plug_end));
31615             heap_segment_allocated (seg) = last_plug_end;
31616             set_mem_verify (heap_segment_allocated (seg) - plug_skew, heap_segment_used (seg), 0xbb);
31617
31618             decommit_heap_segment_pages (seg, 0);
31619         }
31620     }
31621
31622     dprintf (3, ("verifying seg %Ix's mark array was completely cleared", seg));
31623     bgc_verify_mark_array_cleared (seg);
31624 }
31625
31626 void gc_heap::process_n_background_segments (heap_segment* seg, 
31627                                              heap_segment* prev_seg,
31628                                              generation* gen)
31629 {
31630     assert (gen != large_object_generation);
31631
31632     while (seg)
31633     {
31634         dprintf (2, ("processing seg %Ix (not seen by bgc mark)", seg));
31635         heap_segment* next_seg = heap_segment_next (seg);
31636
31637         if (heap_segment_read_only_p (seg))
31638         {
31639             prev_seg = seg;
31640         }
31641         else
31642         {
31643             if (heap_segment_allocated (seg) == heap_segment_mem (seg))
31644             {
31645                 // This can happen - if we have a LOH segment where nothing survived
31646                 // or a SOH segment allocated by a gen1 GC when BGC was going where 
31647                 // nothing survived last time we did a gen1 GC.
31648                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
31649             }
31650             else
31651             {
31652                 prev_seg = seg;
31653             }
31654         }
31655
31656         verify_soh_segment_list();
31657         seg = next_seg;
31658     }
31659 }
31660
31661 inline
31662 BOOL gc_heap::fgc_should_consider_object (uint8_t* o,
31663                                           heap_segment* seg,
31664                                           BOOL consider_bgc_mark_p, 
31665                                           BOOL check_current_sweep_p, 
31666                                           BOOL check_saved_sweep_p)
31667 {
31668     // the logic for this function must be kept in sync with the analogous function
31669     // in ToolBox\SOS\Strike\gc.cpp
31670
31671     // TRUE means we don't need to check the bgc mark bit
31672     // FALSE means we do.
31673     BOOL no_bgc_mark_p = FALSE;
31674
31675     if (consider_bgc_mark_p)
31676     {
31677         if (check_current_sweep_p && (o < current_sweep_pos))
31678         {
31679             dprintf (3, ("no bgc mark - o: %Ix < cs: %Ix", o, current_sweep_pos));
31680             no_bgc_mark_p = TRUE;
31681         }
31682
31683         if (!no_bgc_mark_p)
31684         {
31685             if(check_saved_sweep_p && (o >= saved_sweep_ephemeral_start))
31686             {
31687                 dprintf (3, ("no bgc mark - o: %Ix >= ss: %Ix", o, saved_sweep_ephemeral_start));
31688                 no_bgc_mark_p = TRUE;
31689             }
31690
31691             if (!check_saved_sweep_p)
31692             {
31693                 uint8_t* background_allocated = heap_segment_background_allocated (seg);
31694                 // if this was the saved ephemeral segment, check_saved_sweep_p 
31695                 // would've been true.
31696                 assert (heap_segment_background_allocated (seg) != saved_sweep_ephemeral_start);
31697                 // background_allocated could be 0 for the new segments acquired during bgc
31698                 // sweep and we still want no_bgc_mark_p to be true.
31699                 if (o >= background_allocated)
31700                 {
31701                     dprintf (3, ("no bgc mark - o: %Ix >= ba: %Ix", o, background_allocated));
31702                     no_bgc_mark_p = TRUE;
31703                 }
31704             }
31705         }
31706     }
31707     else
31708     {
31709         no_bgc_mark_p = TRUE;
31710     }
31711
31712     dprintf (3, ("bgc mark %Ix: %s (bm: %s)", o, (no_bgc_mark_p ? "no" : "yes"), (background_object_marked (o, FALSE) ? "yes" : "no")));
31713     return (no_bgc_mark_p ? TRUE : background_object_marked (o, FALSE));
31714 }
31715
31716 // consider_bgc_mark_p tells you if you need to care about the bgc mark bit at all
31717 // if it's TRUE, check_current_sweep_p tells you if you should consider the
31718 // current sweep position or not.
31719 void gc_heap::should_check_bgc_mark (heap_segment* seg, 
31720                                      BOOL* consider_bgc_mark_p, 
31721                                      BOOL* check_current_sweep_p,
31722                                      BOOL* check_saved_sweep_p)
31723 {
31724     // the logic for this function must be kept in sync with the analogous function
31725     // in ToolBox\SOS\Strike\gc.cpp
31726     *consider_bgc_mark_p = FALSE;
31727     *check_current_sweep_p = FALSE;
31728     *check_saved_sweep_p = FALSE;
31729
31730     if (current_c_gc_state == c_gc_state_planning)
31731     {
31732         // We are doing the current_sweep_pos comparison here because we have yet to 
31733         // turn on the swept flag for the segment but in_range_for_segment will return
31734         // FALSE if the address is the same as reserved.
31735         if ((seg->flags & heap_segment_flags_swept) || (current_sweep_pos == heap_segment_reserved (seg)))
31736         {
31737             dprintf (3, ("seg %Ix is already swept by bgc", seg));
31738         }
31739         else
31740         {
31741             *consider_bgc_mark_p = TRUE;
31742
31743             dprintf (3, ("seg %Ix hasn't been swept by bgc", seg));
31744
31745             if (seg == saved_sweep_ephemeral_seg)
31746             {
31747                 dprintf (3, ("seg %Ix is the saved ephemeral seg", seg));
31748                 *check_saved_sweep_p = TRUE;
31749             }
31750
31751             if (in_range_for_segment (current_sweep_pos, seg))
31752             {
31753                 dprintf (3, ("current sweep pos is %Ix and within seg %Ix", 
31754                               current_sweep_pos, seg));
31755                 *check_current_sweep_p = TRUE;
31756             }
31757         }
31758     }
31759 }
31760
31761 void gc_heap::background_ephemeral_sweep()
31762 {
31763     dprintf (3, ("bgc ephemeral sweep"));
31764
31765     int align_const = get_alignment_constant (TRUE);
31766
31767     saved_sweep_ephemeral_seg = ephemeral_heap_segment;
31768     saved_sweep_ephemeral_start = generation_allocation_start (generation_of (max_generation - 1));
31769
31770     // Since we don't want to interfere with gen0 allocation while we are threading gen0 free list,
31771     // we thread onto a list first then publish it when we are done.
31772     allocator youngest_free_list;
31773     size_t youngest_free_list_space = 0;
31774     size_t youngest_free_obj_space = 0;
31775
31776     youngest_free_list.clear();
31777
31778     for (int i = 0; i <= (max_generation - 1); i++)
31779     {
31780         generation* gen_to_reset = generation_of (i);
31781         assert (generation_free_list_space (gen_to_reset) == 0);
31782         // Can only assert free_list_space is 0, not free_obj_space as the allocator could have added 
31783         // something there.
31784     }
31785
31786     for (int i = (max_generation - 1); i >= 0; i--)
31787     {
31788         generation* current_gen = generation_of (i);
31789         uint8_t* o = generation_allocation_start (current_gen);
31790         //Skip the generation gap object
31791         o = o + Align(size (o), align_const);
31792         uint8_t* end = ((i > 0) ?
31793                      generation_allocation_start (generation_of (i - 1)) : 
31794                      heap_segment_allocated (ephemeral_heap_segment));
31795
31796         uint8_t* plug_end = o;
31797         uint8_t* plug_start = o;
31798         BOOL marked_p = FALSE;
31799
31800         while (o < end)
31801         {
31802             marked_p = background_object_marked (o, TRUE);
31803             if (marked_p)
31804             {
31805                 plug_start = o;
31806                 size_t plug_size = plug_start - plug_end;
31807
31808                 if (i >= 1)
31809                 {
31810                     thread_gap (plug_end, plug_size, current_gen);
31811                 }
31812                 else
31813                 {
31814                     if (plug_size > 0)
31815                     {
31816                         make_unused_array (plug_end, plug_size);
31817                         if (plug_size >= min_free_list)
31818                         {
31819                             youngest_free_list_space += plug_size;
31820                             youngest_free_list.thread_item (plug_end, plug_size);
31821                         }
31822                         else
31823                         {
31824                             youngest_free_obj_space += plug_size;
31825                         }
31826                     }
31827                 }
31828
31829                 fix_brick_to_highest (plug_end, plug_start);
31830                 fix_brick_to_highest (plug_start, plug_start);
31831
31832                 BOOL m = TRUE;
31833                 while (m)
31834                 {
31835                     o = o + Align (size (o), align_const);
31836                     if (o >= end)
31837                     {
31838                         break;
31839                     }
31840
31841                     m = background_object_marked (o, TRUE);
31842                 }
31843                 plug_end = o;
31844                 dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
31845             }
31846             else
31847             {
31848                 while ((o < end) && !background_object_marked (o, FALSE))
31849                 {
31850                     o = o + Align (size (o), align_const);
31851                 }
31852             }
31853         }
31854
31855         if (plug_end != end)
31856         {
31857             if (i >= 1)
31858             {
31859                 thread_gap (plug_end, end - plug_end, current_gen);
31860                 fix_brick_to_highest (plug_end, end);
31861             }
31862             else
31863             {
31864                 heap_segment_allocated (ephemeral_heap_segment) = plug_end;
31865                 // the following line is temporary.
31866                 heap_segment_saved_bg_allocated (ephemeral_heap_segment) = plug_end;
31867 #ifdef VERIFY_HEAP
31868                 if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
31869                 {
31870                     make_unused_array (plug_end, (end - plug_end));
31871                 }
31872 #endif //VERIFY_HEAP
31873             }
31874         }
31875
31876         dd_fragmentation (dynamic_data_of (i)) = 
31877             generation_free_list_space (current_gen) + generation_free_obj_space (current_gen);
31878     }
31879
31880     generation* youngest_gen = generation_of (0);
31881     generation_free_list_space (youngest_gen) = youngest_free_list_space;
31882     generation_free_obj_space (youngest_gen) = youngest_free_obj_space;
31883     dd_fragmentation (dynamic_data_of (0)) = youngest_free_list_space + youngest_free_obj_space;
31884     generation_allocator (youngest_gen)->copy_with_no_repair (&youngest_free_list);
31885 }
31886
31887 void gc_heap::background_sweep()
31888 {
31889     generation* gen         = generation_of (max_generation);
31890     dynamic_data* dd        = dynamic_data_of (max_generation);
31891     // For SOH segments we go backwards.
31892     heap_segment* start_seg = ephemeral_heap_segment;
31893     PREFIX_ASSUME(start_seg != NULL);
31894     heap_segment* fseg      = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
31895     heap_segment* seg       = start_seg;
31896     uint8_t* o                 = heap_segment_mem (seg);
31897
31898     heap_segment* prev_seg = heap_segment_next (seg);
31899     int align_const        = get_alignment_constant (TRUE);
31900     if (seg == fseg)
31901     {
31902         assert (o == generation_allocation_start (generation_of (max_generation)));
31903         o = o + Align(size (o), align_const);
31904     }
31905
31906     uint8_t* plug_end      = o;
31907     uint8_t* plug_start    = o;
31908     next_sweep_obj         = o;
31909     current_sweep_pos      = o;
31910
31911     //uint8_t* end              = background_next_end (seg, (gen == large_object_generation));
31912     uint8_t* end              = heap_segment_background_allocated (seg);
31913     BOOL delete_p          = FALSE;
31914
31915     //concurrent_print_time_delta ("finished with mark and start with sweep");
31916     concurrent_print_time_delta ("Sw");
31917     dprintf (2, ("---- (GC%d)Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
31918
31919     //block concurrent allocation for large objects
31920     dprintf (3, ("lh state: planning"));
31921     if (gc_lh_block_event.IsValid())
31922     {
31923         gc_lh_block_event.Reset();
31924     }
31925
31926     for (int i = 0; i <= (max_generation + 1); i++)
31927     {
31928         generation* gen_to_reset = generation_of (i);
31929         generation_allocator (gen_to_reset)->clear();
31930         generation_free_list_space (gen_to_reset) = 0;
31931         generation_free_obj_space (gen_to_reset) = 0;
31932         generation_free_list_allocated (gen_to_reset) = 0;
31933         generation_end_seg_allocated (gen_to_reset) = 0;
31934         generation_condemned_allocated (gen_to_reset) = 0; 
31935         //reset the allocation so foreground gc can allocate into older generation
31936         generation_allocation_pointer (gen_to_reset)= 0;
31937         generation_allocation_limit (gen_to_reset) = 0;
31938         generation_allocation_segment (gen_to_reset) = heap_segment_rw (generation_start_segment (gen_to_reset));
31939     }
31940
31941     FIRE_EVENT(BGC2ndNonConEnd);
31942
31943     loh_alloc_thread_count = 0;
31944     current_bgc_state = bgc_sweep_soh;
31945     verify_soh_segment_list();
31946
31947 #ifdef FEATURE_BASICFREEZE
31948     if ((generation_start_segment (gen) != ephemeral_heap_segment) &&
31949         ro_segments_in_range)
31950     {
31951         sweep_ro_segments (generation_start_segment (gen));
31952     }
31953 #endif // FEATURE_BASICFREEZE
31954
31955     //TODO BACKGROUND_GC: can we move this to where we switch to the LOH?
31956     if (current_c_gc_state != c_gc_state_planning)
31957     {
31958         current_c_gc_state = c_gc_state_planning;
31959     }
31960
31961     concurrent_print_time_delta ("Swe");
31962
31963     heap_segment* loh_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation + 1)));
31964     PREFIX_ASSUME(loh_seg  != NULL);
31965     while (loh_seg )
31966     {
31967         loh_seg->flags &= ~heap_segment_flags_swept;
31968         heap_segment_background_allocated (loh_seg) = heap_segment_allocated (loh_seg);
31969         loh_seg = heap_segment_next_rw (loh_seg);
31970     }
31971
31972 #ifdef MULTIPLE_HEAPS
31973     bgc_t_join.join(this, gc_join_restart_ee);
31974     if (bgc_t_join.joined())
31975 #endif //MULTIPLE_HEAPS 
31976     {
31977 #ifdef MULTIPLE_HEAPS
31978         dprintf(2, ("Starting BGC threads for resuming EE"));
31979         bgc_t_join.restart();
31980 #endif //MULTIPLE_HEAPS
31981     }
31982
31983     if (heap_number == 0)
31984     {
31985         restart_EE ();
31986     }
31987
31988     FIRE_EVENT(BGC2ndConBegin);
31989
31990     background_ephemeral_sweep();
31991
31992     concurrent_print_time_delta ("Swe eph");
31993
31994 #ifdef MULTIPLE_HEAPS
31995     bgc_t_join.join(this, gc_join_after_ephemeral_sweep);
31996     if (bgc_t_join.joined())
31997 #endif //MULTIPLE_HEAPS
31998     {
31999 #ifdef FEATURE_EVENT_TRACE
32000         bgc_heap_walk_for_etw_p = GCEventStatus::IsEnabled(GCEventProvider_Default, 
32001                                                            GCEventKeyword_GCHeapSurvivalAndMovement, 
32002                                                            GCEventLevel_Information);
32003 #endif //FEATURE_EVENT_TRACE
32004
32005         leave_spin_lock (&gc_lock);
32006
32007 #ifdef MULTIPLE_HEAPS
32008         dprintf(2, ("Starting BGC threads for BGC sweeping"));
32009         bgc_t_join.restart();
32010 #endif //MULTIPLE_HEAPS
32011     }
32012
32013     disable_preemptive (true);
32014
32015     dprintf (2, ("bgs: sweeping gen2 objects"));
32016     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
32017                     (size_t)heap_segment_mem (seg),
32018                     (size_t)heap_segment_allocated (seg),
32019                     (size_t)heap_segment_background_allocated (seg)));
32020
32021     int num_objs = 256;
32022     int current_num_objs = 0;
32023     heap_segment* next_seg = 0;
32024
32025     while (1)
32026     {
32027         if (o >= end)
32028         {
32029             if (gen == large_object_generation)
32030             {
32031                 next_seg = heap_segment_next (seg);
32032             }
32033             else
32034             {
32035                 next_seg = heap_segment_prev (fseg, seg);
32036             }
32037
32038             delete_p = FALSE;
32039
32040             if (!heap_segment_read_only_p (seg))
32041             {
32042                 if (gen == large_object_generation)
32043                 {
32044                     // we can treat all LOH segments as in the bgc domain
32045                     // regardless of whether we saw in bgc mark or not
32046                     // because we don't allow LOH allocations during bgc
32047                     // sweep anyway - the LOH segments can't change.
32048                     process_background_segment_end (seg, gen, plug_end, 
32049                                                     start_seg, &delete_p);
32050                 }
32051                 else
32052                 {
32053                     assert (heap_segment_background_allocated (seg) != 0);
32054                     process_background_segment_end (seg, gen, plug_end, 
32055                                                     start_seg, &delete_p);
32056
32057                     assert (next_seg || !delete_p);
32058                 }
32059             }
32060
32061             if (delete_p)
32062             {
32063                 generation_delete_heap_segment (gen, seg, prev_seg, next_seg);
32064             }
32065             else
32066             {
32067                 prev_seg = seg;
32068                 dprintf (2, ("seg %Ix has been swept", seg));
32069                 seg->flags |= heap_segment_flags_swept;
32070             }
32071
32072             verify_soh_segment_list();
32073
32074             seg = next_seg;
32075
32076             dprintf (GTC_LOG, ("seg: %Ix, next_seg: %Ix, prev_seg: %Ix", seg, next_seg, prev_seg));
32077             
32078             if (seg == 0)
32079             {
32080                 generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
32081
32082                 PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
32083
32084                 if (gen != large_object_generation)
32085                 {
32086                     dprintf (2, ("bgs: sweeping gen3 objects"));
32087                     concurrent_print_time_delta ("Swe SOH");
32088                     FIRE_EVENT(BGC1stSweepEnd, 0);
32089
32090                     enter_spin_lock (&more_space_lock_loh);
32091                     add_saved_spinlock_info (true, me_acquire, mt_bgc_loh_sweep);
32092
32093                     concurrent_print_time_delta ("Swe LOH took msl");
32094
32095                     // We wait till all allocating threads are completely done.
32096                     int spin_count = yp_spin_count_unit;
32097                     while (loh_alloc_thread_count)
32098                     {
32099                         spin_and_switch (spin_count, (loh_alloc_thread_count == 0));
32100                     }
32101
32102                     current_bgc_state = bgc_sweep_loh;
32103                     gen = generation_of (max_generation+1);
32104                     start_seg = heap_segment_rw (generation_start_segment (gen));
32105
32106                     PREFIX_ASSUME(start_seg != NULL);
32107
32108                     seg = start_seg;
32109                     prev_seg = 0;
32110                     o = generation_allocation_start (gen);
32111                     assert (method_table (o) == g_gc_pFreeObjectMethodTable);
32112                     align_const = get_alignment_constant (FALSE);
32113                     o = o + Align(size (o), align_const);
32114                     plug_end = o;
32115                     end = heap_segment_allocated (seg);
32116                     dprintf (2, ("sweeping gen3 objects"));
32117                     generation_free_obj_space (gen) = 0;
32118                     generation_allocator (gen)->clear();
32119                     generation_free_list_space (gen) = 0;
32120
32121                     dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
32122                                     (size_t)heap_segment_mem (seg),
32123                                     (size_t)heap_segment_allocated (seg),
32124                                     (size_t)heap_segment_background_allocated (seg)));
32125                 }
32126                 else
32127                     break;
32128             }
32129             else
32130             {
32131                 o = heap_segment_mem (seg);
32132                 if (seg == fseg)
32133                 {
32134                     assert (gen != large_object_generation);
32135                     assert (o == generation_allocation_start (generation_of (max_generation)));
32136                     align_const = get_alignment_constant (TRUE);
32137                     o = o + Align(size (o), align_const);
32138                 }
32139
32140                 plug_end = o;
32141                 current_sweep_pos = o;
32142                 next_sweep_obj = o;
32143                 
32144                 allow_fgc();
32145                 end = background_next_end (seg, (gen == large_object_generation));
32146                 dprintf (2, ("bgs: seg: %Ix, [%Ix, %Ix[%Ix", (size_t)seg,
32147                                 (size_t)heap_segment_mem (seg),
32148                                 (size_t)heap_segment_allocated (seg),
32149                                 (size_t)heap_segment_background_allocated (seg)));
32150             }
32151         }
32152
32153         if ((o < end) && background_object_marked (o, TRUE))
32154         {
32155             plug_start = o;
32156             if (gen == large_object_generation)
32157             {
32158                 dprintf (2, ("loh fr: [%Ix-%Ix[(%Id)", plug_end, plug_start, plug_start-plug_end));
32159             }
32160
32161             thread_gap (plug_end, plug_start-plug_end, gen);
32162             if (gen != large_object_generation)
32163             {
32164                 add_gen_free (max_generation, plug_start-plug_end);
32165                 fix_brick_to_highest (plug_end, plug_start);
32166                 // we need to fix the brick for the next plug here 'cause an FGC can
32167                 // happen and can't read a stale brick.
32168                 fix_brick_to_highest (plug_start, plug_start);
32169             }
32170
32171             BOOL m = TRUE;
32172
32173             while (m)
32174             {
32175                 next_sweep_obj = o + Align(size (o), align_const);
32176                 current_num_objs++;
32177                 if (current_num_objs >= num_objs)
32178                 {
32179                     current_sweep_pos = next_sweep_obj;
32180
32181                     allow_fgc();
32182                     current_num_objs = 0;
32183                 }
32184
32185                 o = next_sweep_obj;
32186                 if (o >= end)
32187                 {
32188                     break;
32189                 }
32190
32191                 m = background_object_marked (o, TRUE);
32192             }
32193             plug_end = o;
32194             if (gen != large_object_generation)
32195             {
32196                 add_gen_plug (max_generation, plug_end-plug_start);
32197                 dd_survived_size (dd) += (plug_end - plug_start);
32198             }
32199             dprintf (3, ("bgs: plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
32200         }
32201         else
32202         {
32203             while ((o < end) && !background_object_marked (o, FALSE))
32204             {
32205                 next_sweep_obj = o + Align(size (o), align_const);;
32206                 current_num_objs++;
32207                 if (current_num_objs >= num_objs)
32208                 {
32209                     current_sweep_pos = plug_end;
32210                     dprintf (1234, ("f: swept till %Ix", current_sweep_pos));
32211                     allow_fgc();
32212                     current_num_objs = 0;
32213                 }
32214
32215                 o = next_sweep_obj;
32216             }
32217         }
32218     }
32219
32220     size_t total_loh_size = generation_size (max_generation + 1);
32221     size_t total_soh_size = generation_sizes (generation_of (max_generation));
32222
32223     dprintf (GTC_LOG, ("h%d: S: loh: %Id, soh: %Id", heap_number, total_loh_size, total_soh_size));
32224
32225     dprintf (GTC_LOG, ("end of bgc sweep: gen2 FL: %Id, FO: %Id", 
32226         generation_free_list_space (generation_of (max_generation)),
32227         generation_free_obj_space (generation_of (max_generation))));
32228     dprintf (GTC_LOG, ("h%d: end of bgc sweep: gen3 FL: %Id, FO: %Id", 
32229         heap_number,
32230         generation_free_list_space (generation_of (max_generation + 1)),
32231         generation_free_obj_space (generation_of (max_generation + 1))));
32232
32233     FIRE_EVENT(BGC2ndConEnd);
32234     concurrent_print_time_delta ("background sweep");
32235     
32236     heap_segment* reset_seg = heap_segment_rw (generation_start_segment (generation_of (max_generation)));
32237     PREFIX_ASSUME(reset_seg != NULL);
32238
32239     while (reset_seg)
32240     {
32241         heap_segment_saved_bg_allocated (reset_seg) = heap_segment_background_allocated (reset_seg);
32242         heap_segment_background_allocated (reset_seg) = 0;
32243         reset_seg = heap_segment_next_rw (reset_seg);
32244     }
32245
32246     generation* loh_gen = generation_of (max_generation + 1);
32247     generation_allocation_segment (loh_gen) = heap_segment_rw (generation_start_segment (loh_gen));
32248
32249     // We calculate dynamic data here because if we wait till we signal the lh event, 
32250     // the allocation thread can change the fragmentation and we may read an intermediate
32251     // value (which can be greater than the generation size). Plus by that time it won't 
32252     // be accurate.
32253     compute_new_dynamic_data (max_generation);
32254
32255     enable_preemptive ();
32256
32257 #ifdef MULTIPLE_HEAPS
32258     bgc_t_join.join(this, gc_join_set_state_free);
32259     if (bgc_t_join.joined())
32260 #endif //MULTIPLE_HEAPS
32261     {
32262         // TODO: We are using this join just to set the state. Should
32263         // look into eliminating it - check to make sure things that use 
32264         // this state can live with per heap state like should_check_bgc_mark.
32265         current_c_gc_state = c_gc_state_free;
32266
32267 #ifdef MULTIPLE_HEAPS
32268         dprintf(2, ("Starting BGC threads after background sweep phase"));
32269         bgc_t_join.restart();
32270 #endif //MULTIPLE_HEAPS
32271     }
32272
32273     disable_preemptive (true);
32274
32275     if (gc_lh_block_event.IsValid())
32276     {
32277         gc_lh_block_event.Set();
32278     }
32279
32280     add_saved_spinlock_info (true, me_release, mt_bgc_loh_sweep);
32281     leave_spin_lock (&more_space_lock_loh);
32282
32283     //dprintf (GTC_LOG, ("---- (GC%d)End Background Sweep Phase ----", VolatileLoad(&settings.gc_index)));
32284     dprintf (GTC_LOG, ("---- (GC%d)ESw ----", VolatileLoad(&settings.gc_index)));
32285 }
32286 #endif //BACKGROUND_GC
32287
32288 void gc_heap::sweep_large_objects ()
32289 {
32290     //this min value is for the sake of the dynamic tuning.
32291     //so we know that we are not starting even if we have no
32292     //survivors.
32293     generation* gen        = large_object_generation;
32294     heap_segment* start_seg = heap_segment_rw (generation_start_segment (gen));
32295
32296     PREFIX_ASSUME(start_seg != NULL);
32297
32298     heap_segment* seg      = start_seg;
32299     heap_segment* prev_seg = 0;
32300     uint8_t* o             = generation_allocation_start (gen);
32301     int align_const        = get_alignment_constant (FALSE);
32302
32303     //Skip the generation gap object
32304     o = o + Align(size (o), align_const);
32305
32306     uint8_t* plug_end         = o;
32307     uint8_t* plug_start       = o;
32308
32309     generation_allocator (gen)->clear();
32310     generation_free_list_space (gen) = 0;
32311     generation_free_obj_space (gen) = 0;
32312
32313
32314     dprintf (3, ("sweeping large objects"));
32315     dprintf (3, ("seg: %Ix, [%Ix, %Ix[, starting from %Ix", 
32316                  (size_t)seg,
32317                  (size_t)heap_segment_mem (seg),
32318                  (size_t)heap_segment_allocated (seg),
32319                  o));
32320
32321     while (1)
32322     {
32323         if (o >= heap_segment_allocated (seg))
32324         {
32325             heap_segment* next_seg = heap_segment_next (seg);
32326             //delete the empty segment if not the only one
32327             if ((plug_end == heap_segment_mem (seg)) &&
32328                 (seg != start_seg) && !heap_segment_read_only_p (seg))
32329             {
32330                 //prepare for deletion
32331                 dprintf (3, ("Preparing empty large segment %Ix", (size_t)seg));
32332                 assert (prev_seg);
32333                 heap_segment_next (prev_seg) = next_seg;
32334                 heap_segment_next (seg) = freeable_large_heap_segment;
32335                 freeable_large_heap_segment = seg;
32336             }
32337             else
32338             {
32339                 if (!heap_segment_read_only_p (seg))
32340                 {
32341                     dprintf (3, ("Trimming seg to %Ix[", (size_t)plug_end));
32342                     heap_segment_allocated (seg) = plug_end;
32343                     decommit_heap_segment_pages (seg, 0);
32344                 }
32345                 prev_seg = seg;
32346             }
32347             seg = next_seg;
32348             if (seg == 0)
32349                 break;
32350             else
32351             {
32352                 o = heap_segment_mem (seg);
32353                 plug_end = o;
32354                 dprintf (3, ("seg: %Ix, [%Ix, %Ix[", (size_t)seg,
32355                              (size_t)heap_segment_mem (seg),
32356                              (size_t)heap_segment_allocated (seg)));
32357             }
32358         }
32359         if (large_object_marked(o, TRUE))
32360         {
32361             plug_start = o;
32362             //everything between plug_end and plug_start is free
32363             thread_gap (plug_end, plug_start-plug_end, gen);
32364
32365             BOOL m = TRUE;
32366             while (m)
32367             {
32368                 o = o + AlignQword (size (o));
32369                 if (o >= heap_segment_allocated (seg))
32370                 {
32371                     break;
32372                 }
32373                 m = large_object_marked (o, TRUE);
32374             }
32375             plug_end = o;
32376             dprintf (3, ("plug [%Ix, %Ix[", (size_t)plug_start, (size_t)plug_end));
32377         }
32378         else
32379         {
32380             while (o < heap_segment_allocated (seg) && !large_object_marked(o, FALSE))
32381             {
32382                 o = o + AlignQword (size (o));
32383             }
32384         }
32385     }
32386
32387     generation_allocation_segment (gen) = heap_segment_rw (generation_start_segment (gen));
32388
32389     PREFIX_ASSUME(generation_allocation_segment(gen) != NULL);
32390 }
32391
32392 void gc_heap::relocate_in_large_objects ()
32393 {
32394     relocate_args args;
32395     args.low = gc_low;
32396     args.high = gc_high;
32397     args.last_plug = 0;
32398
32399     generation* gen = large_object_generation;
32400
32401     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
32402
32403     PREFIX_ASSUME(seg != NULL);
32404
32405     uint8_t* o = generation_allocation_start (gen);
32406
32407     while (1)
32408     {
32409         if (o >= heap_segment_allocated (seg))
32410         {
32411             seg = heap_segment_next_rw (seg);
32412             if (seg == 0)
32413                 break;
32414             else
32415             {
32416                 o = heap_segment_mem (seg);
32417             }
32418         }
32419         while (o < heap_segment_allocated (seg))
32420         {
32421             check_class_object_demotion (o);
32422             if (contain_pointers (o))
32423             {
32424                 dprintf(3, ("Relocating through large object %Ix", (size_t)o));
32425                 go_through_object_nostart (method_table (o), o, size(o), pval,
32426                         {
32427                             reloc_survivor_helper (pval);
32428                         });
32429             }
32430             o = o + AlignQword (size (o));
32431         }
32432     }
32433 }
32434
32435 void gc_heap::mark_through_cards_for_large_objects (card_fn fn,
32436                                                     BOOL relocating)
32437 {
32438     uint8_t*      low               = gc_low;
32439     size_t        end_card          = 0;
32440     generation*   oldest_gen        = generation_of (max_generation+1);
32441     heap_segment* seg               = heap_segment_rw (generation_start_segment (oldest_gen));
32442
32443     PREFIX_ASSUME(seg != NULL);
32444
32445     uint8_t*      beg               = generation_allocation_start (oldest_gen);
32446     uint8_t*      end               = heap_segment_allocated (seg);
32447
32448     size_t  cg_pointers_found = 0;
32449
32450     size_t  card_word_end = (card_of (align_on_card_word (end)) /
32451                              card_word_width);
32452
32453     size_t      n_eph             = 0;
32454     size_t      n_gen             = 0;
32455     size_t      n_card_set        = 0;
32456     uint8_t*    next_boundary = (relocating ?
32457                               generation_plan_allocation_start (generation_of (max_generation -1)) :
32458                               ephemeral_low);
32459
32460     uint8_t*    nhigh         = (relocating ?
32461                               heap_segment_plan_allocated (ephemeral_heap_segment) :
32462                               ephemeral_high);
32463
32464     BOOL          foundp            = FALSE;
32465     uint8_t*      start_address     = 0;
32466     uint8_t*      limit             = 0;
32467     size_t        card              = card_of (beg);
32468     uint8_t*      o                 = beg;
32469 #ifdef BACKGROUND_GC
32470     BOOL consider_bgc_mark_p        = FALSE;
32471     BOOL check_current_sweep_p      = FALSE;
32472     BOOL check_saved_sweep_p        = FALSE;
32473     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32474 #endif //BACKGROUND_GC
32475
32476     size_t total_cards_cleared = 0;
32477
32478     //dprintf(3,( "scanning large objects from %Ix to %Ix", (size_t)beg, (size_t)end));
32479     dprintf(3, ("CMl: %Ix->%Ix", (size_t)beg, (size_t)end));
32480     while (1)
32481     {
32482         if ((o < end) && (card_of(o) > card))
32483         {
32484             dprintf (3, ("Found %Id cg pointers", cg_pointers_found));
32485             if (cg_pointers_found == 0)
32486             {
32487                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card), (size_t)o));
32488                 clear_cards (card, card_of((uint8_t*)o));
32489                 total_cards_cleared += (card_of((uint8_t*)o) - card);
32490             }
32491             n_eph +=cg_pointers_found;
32492             cg_pointers_found = 0;
32493             card = card_of ((uint8_t*)o);
32494         }
32495         if ((o < end) &&(card >= end_card))
32496         {
32497             foundp = find_card (card_table, card, card_word_end, end_card);
32498             if (foundp)
32499             {
32500                 n_card_set+= end_card - card;
32501                 start_address = max (beg, card_address (card));
32502             }
32503             limit = min (end, card_address (end_card));
32504         }
32505         if ((!foundp) || (o >= end) || (card_address (card) >= end))
32506         {
32507             if ((foundp) && (cg_pointers_found == 0))
32508             {
32509                 dprintf(3,(" Clearing cards [%Ix, %Ix[ ", (size_t)card_address(card),
32510                            (size_t)card_address(card+1)));
32511                 clear_cards (card, card+1);
32512                 total_cards_cleared += 1;
32513             }
32514             n_eph +=cg_pointers_found;
32515             cg_pointers_found = 0;
32516             if ((seg = heap_segment_next_rw (seg)) != 0)
32517             {
32518 #ifdef BACKGROUND_GC
32519                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
32520 #endif //BACKGROUND_GC
32521                 beg = heap_segment_mem (seg);
32522                 end = compute_next_end (seg, low);
32523                 card_word_end = card_of (align_on_card_word (end)) / card_word_width;
32524                 card = card_of (beg);
32525                 o  = beg;
32526                 end_card = 0;
32527                 continue;
32528             }
32529             else
32530             {
32531                 break;
32532             }
32533         }
32534
32535         assert (card_set_p (card));
32536         {
32537             dprintf(3,("card %Ix: o: %Ix, l: %Ix[ ",
32538                        card, (size_t)o, (size_t)limit));
32539
32540             assert (Align (size (o)) >= Align (min_obj_size));
32541             size_t s = size (o);
32542             uint8_t* next_o =  o + AlignQword (s);
32543             Prefetch (next_o);
32544
32545             while (o < limit)
32546             {
32547                 s = size (o);
32548                 assert (Align (s) >= Align (min_obj_size));
32549                 next_o =  o + AlignQword (s);
32550                 Prefetch (next_o);
32551
32552                 dprintf (4, ("|%Ix|", (size_t)o));
32553                 if (next_o < start_address)
32554                 {
32555                     goto end_object;
32556                 }
32557
32558 #ifdef BACKGROUND_GC
32559                 if (!fgc_should_consider_object (o, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p))
32560                 {
32561                     goto end_object;
32562                 }
32563 #endif //BACKGROUND_GC
32564
32565 #ifdef COLLECTIBLE_CLASS
32566                 if (is_collectible(o))
32567                 {
32568                     BOOL passed_end_card_p = FALSE;
32569
32570                     if (card_of (o) > card)
32571                     {
32572                         passed_end_card_p = card_transition (o, end, card_word_end,
32573                             cg_pointers_found, 
32574                             n_eph, n_card_set,
32575                             card, end_card,
32576                             foundp, start_address,
32577                             limit, total_cards_cleared);
32578                     }
32579
32580                     if ((!passed_end_card_p || foundp) && (card_of (o) == card))
32581                     {
32582                         // card is valid and it covers the head of the object
32583                         if (fn == &gc_heap::relocate_address)
32584                         {
32585                             keep_card_live (o, n_gen, cg_pointers_found);
32586                         }
32587                         else
32588                         {
32589                             uint8_t* class_obj = get_class_object (o);
32590                             mark_through_cards_helper (&class_obj, n_gen,
32591                                                     cg_pointers_found, fn,
32592                                                     nhigh, next_boundary);
32593                         }
32594                     }
32595
32596                     if (passed_end_card_p)
32597                     {
32598                         if (foundp && (card_address (card) < next_o))
32599                         {
32600                             goto go_through_refs;
32601                         }
32602                         else 
32603                         {
32604                             goto end_object;
32605                         }
32606                     }
32607                 }
32608
32609 go_through_refs:
32610 #endif //COLLECTIBLE_CLASS
32611
32612                 if (contain_pointers (o))
32613                 {
32614                     dprintf(3,("Going through %Ix", (size_t)o));
32615
32616                     go_through_object (method_table(o), o, s, poo,
32617                                        start_address, use_start, (o + s),
32618                        {
32619                            if (card_of ((uint8_t*)poo) > card)
32620                            {
32621                                 BOOL passed_end_card_p  = card_transition ((uint8_t*)poo, end,
32622                                         card_word_end,
32623                                         cg_pointers_found, 
32624                                         n_eph, n_card_set,
32625                                         card, end_card,
32626                                         foundp, start_address,
32627                                         limit, total_cards_cleared);
32628
32629                                 if (passed_end_card_p)
32630                                 {
32631                                     if (foundp && (card_address (card) < next_o))
32632                                     {
32633                                         //new_start();
32634                                         {
32635                                             if (ppstop <= (uint8_t**)start_address)
32636                                             {break;}
32637                                             else if (poo < (uint8_t**)start_address)
32638                                             {poo = (uint8_t**)start_address;}
32639                                         }
32640                                     }
32641                                     else
32642                                     {
32643                                         goto end_object;
32644                                     }
32645                                 }
32646                             }
32647
32648                            mark_through_cards_helper (poo, n_gen,
32649                                                       cg_pointers_found, fn,
32650                                                       nhigh, next_boundary);
32651                        }
32652                         );
32653                 }
32654
32655             end_object:
32656                 o = next_o;
32657             }
32658
32659         }
32660     }
32661
32662     // compute the efficiency ratio of the card table
32663     if (!relocating)
32664     {
32665         generation_skip_ratio = min (((n_eph > 800) ?
32666                                       (int)(((float)n_gen / (float)n_eph) * 100) : 100),
32667                                      generation_skip_ratio);
32668
32669         dprintf (3, ("Mloh: cross: %Id, useful: %Id, cards cleared: %Id, cards set: %Id, ratio: %d", 
32670              n_eph, n_gen, total_cards_cleared, n_card_set, generation_skip_ratio));
32671     }
32672     else
32673     {
32674         dprintf (3, ("R: Mloh: cross: %Id, useful: %Id, cards set: %Id, ratio: %d", 
32675              n_eph, n_gen, n_card_set, generation_skip_ratio));
32676     }
32677 }
32678
32679 void gc_heap::descr_segment (heap_segment* seg )
32680 {
32681 #ifdef TRACE_GC
32682     uint8_t*  x = heap_segment_mem (seg);
32683     while (x < heap_segment_allocated (seg))
32684     {
32685         dprintf(2, ( "%Ix: %d ", (size_t)x, size (x)));
32686         x = x + Align(size (x));
32687     }
32688 #else // TRACE_GC
32689     UNREFERENCED_PARAMETER(seg);
32690 #endif // TRACE_GC
32691 }
32692
32693 void gc_heap::descr_generations_to_profiler (gen_walk_fn fn, void *context)
32694 {
32695 #ifdef MULTIPLE_HEAPS
32696     int n_heaps = g_theGCHeap->GetNumberOfHeaps ();
32697     for (int i = 0; i < n_heaps; i++)
32698     {
32699         gc_heap* hp = GCHeap::GetHeap(i)->pGenGCHeap;
32700 #else //MULTIPLE_HEAPS
32701     {
32702         gc_heap* hp = NULL;
32703 #ifdef _PREFAST_
32704         // prefix complains about us dereferencing hp in wks build even though we only access static members
32705         // this way. not sure how to shut it up except for this ugly workaround:
32706         PREFIX_ASSUME(hp != NULL);
32707 #endif // _PREFAST_
32708 #endif //MULTIPLE_HEAPS
32709
32710         int curr_gen_number0 = max_generation+1;
32711         while (curr_gen_number0 >= 0)
32712         {
32713             generation* gen = hp->generation_of (curr_gen_number0);
32714             heap_segment* seg = generation_start_segment (gen);
32715             while (seg && (seg != hp->ephemeral_heap_segment))
32716             {
32717                 assert (curr_gen_number0 > 0);
32718
32719                 // report bounds from heap_segment_mem (seg) to
32720                 // heap_segment_allocated (seg);
32721                 // for generation # curr_gen_number0
32722                 // for heap # heap_no
32723
32724                 fn(context, curr_gen_number0, heap_segment_mem (seg),
32725                                               heap_segment_allocated (seg),
32726                                               curr_gen_number0 == max_generation+1 ? heap_segment_reserved (seg) : heap_segment_allocated (seg));
32727
32728                 seg = heap_segment_next (seg);
32729             }
32730             if (seg)
32731             {
32732                 assert (seg == hp->ephemeral_heap_segment);
32733                 assert (curr_gen_number0 <= max_generation);
32734                 //
32735                 if (curr_gen_number0 == max_generation)
32736                 {
32737                     if (heap_segment_mem (seg) < generation_allocation_start (hp->generation_of (max_generation-1)))
32738                     {
32739                         // report bounds from heap_segment_mem (seg) to
32740                         // generation_allocation_start (generation_of (max_generation-1))
32741                         // for heap # heap_number
32742
32743                         fn(context, curr_gen_number0, heap_segment_mem (seg),
32744                                                       generation_allocation_start (hp->generation_of (max_generation-1)),
32745                                                       generation_allocation_start (hp->generation_of (max_generation-1)) );
32746                     }
32747                 }
32748                 else if (curr_gen_number0 != 0)
32749                 {
32750                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32751                     // to generation_allocation_start (generation_of (curr_gen_number0-1))
32752                     // for heap # heap_number
32753
32754                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32755                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)),
32756                                                   generation_allocation_start (hp->generation_of (curr_gen_number0-1)));
32757                 }
32758                 else
32759                 {
32760                     //report bounds from generation_allocation_start (generation_of (curr_gen_number0))
32761                     // to heap_segment_allocated (ephemeral_heap_segment);
32762                     // for heap # heap_number
32763
32764                     fn(context, curr_gen_number0, generation_allocation_start (hp->generation_of (curr_gen_number0)),
32765                                                   heap_segment_allocated (hp->ephemeral_heap_segment),
32766                                                   heap_segment_reserved (hp->ephemeral_heap_segment) );
32767                 }
32768             }
32769             curr_gen_number0--;
32770         }
32771     }
32772 }
32773
32774 #ifdef TRACE_GC
32775 // Note that when logging is on it can take a long time to go through the free items.
32776 void gc_heap::print_free_list (int gen, heap_segment* seg)
32777 {
32778     UNREFERENCED_PARAMETER(gen);
32779     UNREFERENCED_PARAMETER(seg);
32780 /*
32781     if (settings.concurrent == FALSE)
32782     {
32783         uint8_t* seg_start = heap_segment_mem (seg);
32784         uint8_t* seg_end = heap_segment_allocated (seg);
32785
32786         dprintf (3, ("Free list in seg %Ix:", seg_start));
32787
32788         size_t total_free_item = 0;
32789
32790         allocator* gen_allocator = generation_allocator (generation_of (gen));
32791         for (unsigned int b = 0; b < gen_allocator->number_of_buckets(); b++)
32792         {
32793             uint8_t* fo = gen_allocator->alloc_list_head_of (b);
32794             while (fo)
32795             {
32796                 if (fo >= seg_start && fo < seg_end)
32797                 {
32798                     total_free_item++;
32799
32800                     size_t free_item_len = size(fo);
32801
32802                     dprintf (3, ("[%Ix, %Ix[:%Id",
32803                                  (size_t)fo,
32804                                  (size_t)(fo + free_item_len),
32805                                  free_item_len));
32806                 }
32807
32808                 fo = free_list_slot (fo);
32809             }
32810         }
32811
32812         dprintf (3, ("total %Id free items", total_free_item));
32813     }
32814 */
32815 }
32816 #endif //TRACE_GC
32817
32818 void gc_heap::descr_generations (BOOL begin_gc_p)
32819 {
32820     UNREFERENCED_PARAMETER(begin_gc_p);
32821 #ifdef STRESS_LOG
32822     if (StressLog::StressLogOn(LF_GC, LL_INFO10))
32823     {
32824         gc_heap* hp = 0;
32825 #ifdef MULTIPLE_HEAPS
32826         hp= this;
32827 #endif //MULTIPLE_HEAPS
32828
32829         STRESS_LOG1(LF_GC, LL_INFO10, "GC Heap %p\n", hp);
32830         for (int n = max_generation; n >= 0; --n)
32831         {
32832             STRESS_LOG4(LF_GC, LL_INFO10, "    Generation %d [%p, %p] cur = %p\n",
32833                     n,
32834                     generation_allocation_start(generation_of(n)),
32835                     generation_allocation_limit(generation_of(n)),
32836                     generation_allocation_pointer(generation_of(n)));
32837
32838             heap_segment* seg = generation_start_segment(generation_of(n));
32839             while (seg)
32840             {
32841                 STRESS_LOG4(LF_GC, LL_INFO10, "        Segment mem %p alloc = %p used %p committed %p\n",
32842                         heap_segment_mem(seg),
32843                         heap_segment_allocated(seg),
32844                         heap_segment_used(seg),
32845                         heap_segment_committed(seg));
32846                 seg = heap_segment_next(seg);
32847             }
32848         }
32849     }
32850 #endif  // STRESS_LOG
32851
32852 #ifdef TRACE_GC
32853     dprintf (2, ("lowest_address: %Ix highest_address: %Ix",
32854              (size_t) lowest_address, (size_t) highest_address));
32855 #ifdef BACKGROUND_GC
32856     dprintf (2, ("bgc lowest_address: %Ix bgc highest_address: %Ix",
32857              (size_t) background_saved_lowest_address, (size_t) background_saved_highest_address));
32858 #endif //BACKGROUND_GC
32859
32860     if (heap_number == 0)
32861     {
32862         dprintf (1, ("total heap size: %Id, commit size: %Id", get_total_heap_size(), get_total_committed_size()));
32863     }
32864
32865     int curr_gen_number = max_generation+1;
32866     while (curr_gen_number >= 0)
32867     {
32868         size_t total_gen_size = generation_size (curr_gen_number);
32869 #ifdef SIMPLE_DPRINTF
32870         dprintf (GTC_LOG, ("[%s][g%d]gen %d:, size: %Id, frag: %Id(L: %Id, O: %Id), f: %d%% %s %s %s",
32871                       (begin_gc_p ? "BEG" : "END"),
32872                       settings.condemned_generation,
32873                       curr_gen_number,
32874                       total_gen_size,
32875                       dd_fragmentation (dynamic_data_of (curr_gen_number)),
32876                       generation_free_list_space (generation_of (curr_gen_number)),
32877                       generation_free_obj_space (generation_of (curr_gen_number)),
32878                       (total_gen_size ? 
32879                         (int)(((double)dd_fragmentation (dynamic_data_of (curr_gen_number)) / (double)total_gen_size) * 100) :
32880                         0),
32881                       (begin_gc_p ? ("") : (settings.compaction ? "(compact)" : "(sweep)")),
32882                       (settings.heap_expansion ? "(EX)" : " "),
32883                       (settings.promotion ? "Promotion" : "NoPromotion")));
32884 #else
32885         dprintf (2, ( "Generation %d: gap size: %d, generation size: %Id, fragmentation: %Id",
32886                       curr_gen_number,
32887                       size (generation_allocation_start (generation_of (curr_gen_number))),
32888                       total_gen_size,
32889                       dd_fragmentation (dynamic_data_of (curr_gen_number))));
32890 #endif //SIMPLE_DPRINTF
32891
32892         generation* gen = generation_of (curr_gen_number);
32893         heap_segment* seg = generation_start_segment (gen);
32894         while (seg && (seg != ephemeral_heap_segment))
32895         {
32896             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[-%Ix[ (%Id) (%Id)",
32897                         curr_gen_number,
32898                         (size_t)heap_segment_mem (seg),
32899                         (size_t)heap_segment_allocated (seg),
32900                         (size_t)heap_segment_committed (seg),
32901                         (size_t)(heap_segment_allocated (seg) - heap_segment_mem (seg)),
32902                         (size_t)(heap_segment_committed (seg) - heap_segment_allocated (seg))));
32903             print_free_list (curr_gen_number, seg);
32904             seg = heap_segment_next (seg);
32905         }
32906         if (seg && (seg != generation_start_segment (gen)))
32907         {
32908             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32909                          curr_gen_number,
32910                          (size_t)heap_segment_mem (seg),
32911                          (size_t)generation_allocation_start (generation_of (curr_gen_number-1))));
32912             print_free_list (curr_gen_number, seg);
32913
32914         }
32915         else if (seg)
32916         {
32917             dprintf (GTC_LOG, ("g%d: [%Ix %Ix[",
32918                          curr_gen_number,
32919                          (size_t)generation_allocation_start (generation_of (curr_gen_number)),
32920                          (size_t)(((curr_gen_number == 0)) ?
32921                                   (heap_segment_allocated
32922                                    (generation_start_segment
32923                                     (generation_of (curr_gen_number)))) :
32924                                   (generation_allocation_start
32925                                    (generation_of (curr_gen_number - 1))))
32926                          ));
32927             print_free_list (curr_gen_number, seg);
32928         }
32929         curr_gen_number--;
32930     }
32931
32932 #endif //TRACE_GC
32933 }
32934
32935 #undef TRACE_GC
32936
32937 //#define TRACE_GC
32938
32939 //-----------------------------------------------------------------------------
32940 //
32941 //                                  VM Specific support
32942 //
32943 //-----------------------------------------------------------------------------
32944
32945
32946 #ifdef TRACE_GC
32947
32948  unsigned int PromotedObjectCount  = 0;
32949  unsigned int CreatedObjectCount       = 0;
32950  unsigned int AllocDuration            = 0;
32951  unsigned int AllocCount               = 0;
32952  unsigned int AllocBigCount            = 0;
32953  unsigned int AllocSmallCount      = 0;
32954  unsigned int AllocStart             = 0;
32955 #endif //TRACE_GC
32956
32957 //Static member variables.
32958 VOLATILE(BOOL)    GCHeap::GcInProgress            = FALSE;
32959 //GCTODO
32960 //CMCSafeLock*      GCHeap::fGcLock;
32961 GCEvent            *GCHeap::WaitForGCEvent         = NULL;
32962 //GCTODO
32963 #ifdef TRACE_GC
32964 unsigned int       GCHeap::GcDuration;
32965 #endif //TRACE_GC
32966 unsigned            GCHeap::GcCondemnedGeneration   = 0;
32967 size_t              GCHeap::totalSurvivedSize       = 0;
32968 #ifdef FEATURE_PREMORTEM_FINALIZATION
32969 CFinalize*          GCHeap::m_Finalize              = 0;
32970 BOOL                GCHeap::GcCollectClasses        = FALSE;
32971 VOLATILE(int32_t)      GCHeap::m_GCFLock               = 0;
32972
32973 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
32974 #ifdef STRESS_HEAP
32975 #ifdef BACKGROUND_GC
32976 int                 GCHeap::gc_stress_fgcs_in_bgc   = 0;
32977 #endif // BACKGROUND_GC
32978 #ifndef MULTIPLE_HEAPS
32979 OBJECTHANDLE        GCHeap::m_StressObjs[NUM_HEAP_STRESS_OBJS];
32980 int                 GCHeap::m_CurStressObj          = 0;
32981 #endif // !MULTIPLE_HEAPS
32982 #endif // STRESS_HEAP
32983 #endif // FEATURE_REDHAWK
32984
32985 #endif //FEATURE_PREMORTEM_FINALIZATION
32986
32987 class NoGCRegionLockHolder
32988 {
32989 public:
32990     NoGCRegionLockHolder()
32991     {
32992         enter_spin_lock_noinstru(&g_no_gc_lock);
32993     }
32994
32995     ~NoGCRegionLockHolder()
32996     {
32997         leave_spin_lock_noinstru(&g_no_gc_lock);
32998     }
32999 };
33000
33001 // An explanation of locking for finalization:
33002 //
33003 // Multiple threads allocate objects.  During the allocation, they are serialized by
33004 // the AllocLock above.  But they release that lock before they register the object
33005 // for finalization.  That's because there is much contention for the alloc lock, but
33006 // finalization is presumed to be a rare case.
33007 //
33008 // So registering an object for finalization must be protected by the FinalizeLock.
33009 //
33010 // There is another logical queue that involves finalization.  When objects registered
33011 // for finalization become unreachable, they are moved from the "registered" queue to
33012 // the "unreachable" queue.  Note that this only happens inside a GC, so no other
33013 // threads can be manipulating either queue at that time.  Once the GC is over and
33014 // threads are resumed, the Finalizer thread will dequeue objects from the "unreachable"
33015 // queue and call their finalizers.  This dequeue operation is also protected with
33016 // the finalize lock.
33017 //
33018 // At first, this seems unnecessary.  Only one thread is ever enqueuing or dequeuing
33019 // on the unreachable queue (either the GC thread during a GC or the finalizer thread
33020 // when a GC is not in progress).  The reason we share a lock with threads enqueuing
33021 // on the "registered" queue is that the "registered" and "unreachable" queues are
33022 // interrelated.
33023 //
33024 // They are actually two regions of a longer list, which can only grow at one end.
33025 // So to enqueue an object to the "registered" list, you actually rotate an unreachable
33026 // object at the boundary between the logical queues, out to the other end of the
33027 // unreachable queue -- where all growing takes place.  Then you move the boundary
33028 // pointer so that the gap we created at the boundary is now on the "registered"
33029 // side rather than the "unreachable" side.  Now the object can be placed into the
33030 // "registered" side at that point.  This is much more efficient than doing moves
33031 // of arbitrarily long regions, but it causes the two queues to require a shared lock.
33032 //
33033 // Notice that Enter/LeaveFinalizeLock is not a GC-aware spin lock.  Instead, it relies
33034 // on the fact that the lock will only be taken for a brief period and that it will
33035 // never provoke or allow a GC while the lock is held.  This is critical.  If the
33036 // FinalizeLock used enter_spin_lock (and thus sometimes enters preemptive mode to
33037 // allow a GC), then the Alloc client would have to GC protect a finalizable object
33038 // to protect against that eventuality.  That is too slow!
33039
33040
33041
33042 BOOL IsValidObject99(uint8_t *pObject)
33043 {
33044 #ifdef VERIFY_HEAP
33045     if (!((CObjectHeader*)pObject)->IsFree())
33046         ((CObjectHeader *) pObject)->Validate();
33047 #endif //VERIFY_HEAP
33048     return(TRUE);
33049 }
33050
33051 #ifdef BACKGROUND_GC 
33052 BOOL gc_heap::bgc_mark_array_range (heap_segment* seg, 
33053                                     BOOL whole_seg_p,
33054                                     uint8_t** range_beg,
33055                                     uint8_t** range_end)
33056 {
33057     uint8_t* seg_start = heap_segment_mem (seg);
33058     uint8_t* seg_end = (whole_seg_p ? heap_segment_reserved (seg) : align_on_mark_word (heap_segment_allocated (seg)));
33059
33060     if ((seg_start < background_saved_highest_address) &&
33061         (seg_end > background_saved_lowest_address))
33062     {
33063         *range_beg = max (seg_start, background_saved_lowest_address);
33064         *range_end = min (seg_end, background_saved_highest_address);
33065         return TRUE;
33066     }
33067     else
33068     {
33069         return FALSE;
33070     }
33071 }
33072
33073 void gc_heap::bgc_verify_mark_array_cleared (heap_segment* seg)
33074 {
33075 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
33076     if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
33077     {
33078         uint8_t* range_beg = 0;
33079         uint8_t* range_end = 0;
33080
33081         if (bgc_mark_array_range (seg, TRUE, &range_beg, &range_end))
33082         {
33083             size_t  markw = mark_word_of (range_beg);
33084             size_t  markw_end = mark_word_of (range_end);
33085             while (markw < markw_end)
33086             {
33087                 if (mark_array [markw])
33088                 {
33089                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
33090                                     markw, mark_array [markw], mark_word_address (markw)));
33091                     FATAL_GC_ERROR();
33092                 }
33093                 markw++;
33094             }
33095             uint8_t* p = mark_word_address (markw_end);
33096             while (p < range_end)
33097             {
33098                 assert (!(mark_array_marked (p)));
33099                 p++;
33100             }
33101         }
33102     }
33103 #endif //VERIFY_HEAP && MARK_ARRAY
33104 }
33105
33106 void gc_heap::verify_mark_bits_cleared (uint8_t* obj, size_t s)
33107 {
33108 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
33109     size_t start_mark_bit = mark_bit_of (obj) + 1;
33110     size_t end_mark_bit = mark_bit_of (obj + s);
33111     unsigned int startbit = mark_bit_bit (start_mark_bit);
33112     unsigned int endbit = mark_bit_bit (end_mark_bit);
33113     size_t startwrd = mark_bit_word (start_mark_bit);
33114     size_t endwrd = mark_bit_word (end_mark_bit);
33115     unsigned int result = 0;
33116
33117     unsigned int firstwrd = ~(lowbits (~0, startbit));
33118     unsigned int lastwrd = ~(highbits (~0, endbit));
33119
33120     if (startwrd == endwrd)
33121     {
33122         unsigned int wrd = firstwrd & lastwrd;
33123         result = mark_array[startwrd] & wrd;
33124         if (result)
33125         {
33126             FATAL_GC_ERROR();
33127         }
33128         return;
33129     }
33130
33131     // verify the first mark word is cleared.
33132     if (startbit)
33133     {
33134         result = mark_array[startwrd] & firstwrd;
33135         if (result)
33136         {
33137             FATAL_GC_ERROR();
33138         }
33139         startwrd++;
33140     }
33141
33142     for (size_t wrdtmp = startwrd; wrdtmp < endwrd; wrdtmp++)
33143     {
33144         result = mark_array[wrdtmp];
33145         if (result)
33146         {
33147             FATAL_GC_ERROR();
33148         }
33149     }
33150
33151     // set the last mark word.
33152     if (endbit)
33153     {
33154         result = mark_array[endwrd] & lastwrd;
33155         if (result)
33156         {
33157             FATAL_GC_ERROR();
33158         }
33159     }
33160 #endif //VERIFY_HEAP && MARK_ARRAY
33161 }
33162
33163 void gc_heap::clear_all_mark_array()
33164 {
33165 #ifdef MARK_ARRAY
33166     //size_t num_dwords_written = 0;
33167     //size_t begin_time = GetHighPrecisionTimeStamp();
33168
33169     generation* gen = generation_of (max_generation);
33170     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33171     
33172     while (1)
33173     {
33174         if (seg == 0)
33175         {
33176             if (gen != large_object_generation)
33177             {
33178                 gen = generation_of (max_generation+1);
33179                 seg = heap_segment_rw (generation_start_segment (gen));
33180             }
33181             else
33182             {
33183                 break;
33184             }
33185         }
33186
33187         uint8_t* range_beg = 0;
33188         uint8_t* range_end = 0;
33189
33190         if (bgc_mark_array_range (seg, (seg == ephemeral_heap_segment), &range_beg, &range_end))
33191         { 
33192             size_t markw = mark_word_of (range_beg);
33193             size_t markw_end = mark_word_of (range_end);
33194             size_t size_total = (markw_end - markw) * sizeof (uint32_t);
33195             //num_dwords_written = markw_end - markw;
33196             size_t size = 0;
33197             size_t size_left = 0;
33198
33199             assert (((size_t)&mark_array[markw] & (sizeof(PTR_PTR)-1)) == 0);
33200
33201             if ((size_total & (sizeof(PTR_PTR) - 1)) != 0)
33202             {
33203                 size = (size_total & ~(sizeof(PTR_PTR) - 1));
33204                 size_left = size_total - size;
33205                 assert ((size_left & (sizeof (uint32_t) - 1)) == 0);
33206             }
33207             else
33208             {
33209                 size = size_total;
33210             }
33211
33212             memclr ((uint8_t*)&mark_array[markw], size);
33213
33214             if (size_left != 0)
33215             {
33216                 uint32_t* markw_to_clear = &mark_array[markw + size / sizeof (uint32_t)];
33217                 for (size_t i = 0; i < (size_left / sizeof (uint32_t)); i++)
33218                 {
33219                     *markw_to_clear = 0;
33220                     markw_to_clear++;
33221                 }
33222             }
33223         }
33224
33225         seg = heap_segment_next_rw (seg);
33226     }
33227
33228     //size_t end_time = GetHighPrecisionTimeStamp() - begin_time; 
33229
33230     //printf ("took %Id ms to clear %Id bytes\n", end_time, num_dwords_written*sizeof(uint32_t));
33231
33232 #endif //MARK_ARRAY
33233 }
33234
33235 #endif //BACKGROUND_GC 
33236
33237 void gc_heap::verify_mark_array_cleared (heap_segment* seg)
33238 {
33239 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
33240     assert (card_table == g_gc_card_table);
33241     size_t  markw = mark_word_of (heap_segment_mem (seg));
33242     size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
33243
33244     while (markw < markw_end)
33245     {
33246         if (mark_array [markw])
33247         {
33248             dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
33249                             markw, mark_array [markw], mark_word_address (markw)));
33250             FATAL_GC_ERROR();
33251         }
33252         markw++;
33253     }
33254 #endif //VERIFY_HEAP && MARK_ARRAY
33255 }
33256
33257 void gc_heap::verify_mark_array_cleared ()
33258 {
33259 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
33260     if (recursive_gc_sync::background_running_p() && GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
33261     {
33262         generation* gen = generation_of (max_generation);
33263         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33264         
33265         while (1)
33266         {
33267             if (seg == 0)
33268             {
33269                 if (gen != large_object_generation)
33270                 {
33271                     gen = generation_of (max_generation+1);
33272                     seg = heap_segment_rw (generation_start_segment (gen));
33273                 }
33274                 else
33275                 {
33276                     break;
33277                 }
33278             }
33279
33280             bgc_verify_mark_array_cleared (seg);
33281             seg = heap_segment_next_rw (seg);
33282         }
33283     }
33284 #endif //VERIFY_HEAP && MARK_ARRAY
33285 }
33286
33287 void gc_heap::verify_seg_end_mark_array_cleared()
33288 {
33289 #if defined (VERIFY_HEAP) && defined (MARK_ARRAY)
33290     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
33291     {
33292         generation* gen = generation_of (max_generation);
33293         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33294         
33295         while (1)
33296         {
33297             if (seg == 0)
33298             {
33299                 if (gen != large_object_generation)
33300                 {
33301                     gen = generation_of (max_generation+1);
33302                     seg = heap_segment_rw (generation_start_segment (gen));
33303                 }
33304                 else
33305                 {
33306                     break;
33307                 }
33308             }
33309
33310             // We already cleared all mark array bits for ephemeral generations
33311             // at the beginning of bgc sweep
33312             uint8_t* from = ((seg == ephemeral_heap_segment) ?
33313                           generation_allocation_start (generation_of (max_generation - 1)) :
33314                           heap_segment_allocated (seg));
33315             size_t  markw = mark_word_of (align_on_mark_word (from));
33316             size_t  markw_end = mark_word_of (heap_segment_reserved (seg));
33317
33318             while (from < mark_word_address (markw))
33319             {
33320                 if (is_mark_bit_set (from))
33321                 {
33322                     dprintf (3, ("mark bit for %Ix was not cleared", from));
33323                     FATAL_GC_ERROR();
33324                 }
33325
33326                 from += mark_bit_pitch;
33327             }
33328
33329             while (markw < markw_end)
33330             {
33331                 if (mark_array [markw])
33332                 {
33333                     dprintf  (3, ("The mark bits at 0x%Ix:0x%Ix(addr: 0x%Ix) were not cleared", 
33334                                     markw, mark_array [markw], mark_word_address (markw)));
33335                     FATAL_GC_ERROR();
33336                 }
33337                 markw++;
33338             }
33339             seg = heap_segment_next_rw (seg);
33340         }
33341     }
33342 #endif //VERIFY_HEAP && MARK_ARRAY
33343 }
33344
33345 // This function is called to make sure we don't mess up the segment list
33346 // in SOH. It's called by:
33347 // 1) begin and end of ephemeral GCs
33348 // 2) during bgc sweep when we switch segments.
33349 void gc_heap::verify_soh_segment_list()
33350 {
33351 #ifdef VERIFY_HEAP
33352     if (GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_GC)
33353     {
33354         generation* gen = generation_of (max_generation);
33355         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33356         heap_segment* last_seg = 0;
33357         while (seg)
33358         {
33359             last_seg = seg;
33360             seg = heap_segment_next_rw (seg);
33361         }
33362         if (last_seg != ephemeral_heap_segment)
33363         {
33364             FATAL_GC_ERROR();
33365         }
33366     }
33367 #endif //VERIFY_HEAP
33368 }
33369
33370 // This function can be called at any foreground GCs or blocking GCs. For background GCs,
33371 // it can be called at the end of the final marking; and at any point during background
33372 // sweep.
33373 // NOTE - to be able to call this function during background sweep, we need to temporarily 
33374 // NOT clear the mark array bits as we go.
33375 void gc_heap::verify_partial ()
33376 {
33377 #ifdef BACKGROUND_GC
33378     //printf ("GC#%d: Verifying loh during sweep\n", settings.gc_index);
33379     //generation* gen = large_object_generation;
33380     generation* gen = generation_of (max_generation);
33381     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
33382     int align_const = get_alignment_constant (gen != large_object_generation);
33383
33384     uint8_t* o = 0;
33385     uint8_t* end = 0;
33386     size_t s = 0;
33387
33388     // Different ways to fail.
33389     BOOL mark_missed_p = FALSE;
33390     BOOL bad_ref_p = FALSE;
33391     BOOL free_ref_p = FALSE;
33392
33393     while (1)
33394     {
33395         if (seg == 0)
33396         {
33397             if (gen != large_object_generation)
33398             {
33399                 //switch to LOH
33400                 gen = large_object_generation;
33401                 align_const = get_alignment_constant (gen != large_object_generation);
33402                 seg = heap_segment_rw (generation_start_segment (gen));
33403                 continue;
33404             }
33405             else
33406             {
33407                 break;
33408             }
33409         }
33410
33411         o = heap_segment_mem (seg);
33412         end  = heap_segment_allocated (seg);
33413         //printf ("validating [%Ix-[%Ix\n", o, end);
33414         while (o < end)
33415         {
33416             s = size (o);
33417
33418             BOOL marked_p = background_object_marked (o, FALSE);
33419
33420             if (marked_p)
33421             {
33422                 go_through_object_cl (method_table (o), o, s, oo,
33423                     {
33424                         if (*oo)
33425                         {
33426                             //dprintf (3, ("VOM: verifying member %Ix in obj %Ix", (size_t)*oo, o));
33427                             MethodTable *pMT = method_table (*oo);
33428
33429                             if (pMT == g_gc_pFreeObjectMethodTable)
33430                             {
33431                                 free_ref_p = TRUE;
33432                                 FATAL_GC_ERROR();
33433                             }
33434
33435                             if (!pMT->SanityCheck()) 
33436                             {
33437                                 bad_ref_p = TRUE;
33438                                 dprintf (3, ("Bad member of %Ix %Ix",
33439                                             (size_t)oo, (size_t)*oo));
33440                                 FATAL_GC_ERROR();
33441                             }
33442
33443                             if (current_bgc_state == bgc_final_marking)
33444                             {
33445                                 if (marked_p && !background_object_marked (*oo, FALSE))
33446                                 {
33447                                     mark_missed_p = TRUE;
33448                                     FATAL_GC_ERROR();
33449                                 }
33450                             }
33451                         }
33452                     }
33453                                     );
33454             }
33455
33456             o = o + Align(s, align_const);
33457         }
33458         seg = heap_segment_next_rw (seg);
33459     }
33460
33461     //printf ("didn't find any large object large enough...\n");
33462     //printf ("finished verifying loh\n");
33463 #endif //BACKGROUND_GC 
33464 }
33465
33466 #ifdef VERIFY_HEAP
33467
33468 void 
33469 gc_heap::verify_free_lists ()
33470 {
33471     for (int gen_num = 0; gen_num <= max_generation+1; gen_num++)
33472     {
33473         dprintf (3, ("Verifying free list for gen:%d", gen_num));
33474         allocator* gen_alloc = generation_allocator (generation_of (gen_num));
33475         size_t sz = gen_alloc->first_bucket_size();
33476         bool verify_undo_slot = (gen_num != 0) && (gen_num != max_generation+1) && !gen_alloc->discard_if_no_fit_p();
33477
33478         for (unsigned int a_l_number = 0; a_l_number < gen_alloc->number_of_buckets(); a_l_number++)
33479         {
33480             uint8_t* free_list = gen_alloc->alloc_list_head_of (a_l_number);
33481             uint8_t* prev = 0;
33482             while (free_list)
33483             {
33484                 if (!((CObjectHeader*)free_list)->IsFree())
33485                 {
33486                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't a free object)",
33487                                  (size_t)free_list));
33488                     FATAL_GC_ERROR();
33489                 }
33490                 if (((a_l_number < (gen_alloc->number_of_buckets()-1))&& (unused_array_size (free_list) >= sz))
33491                     || ((a_l_number != 0) && (unused_array_size (free_list) < sz/2)))
33492                 {
33493                     dprintf (3, ("Verifiying Heap: curr free list item %Ix isn't in the right bucket",
33494                                  (size_t)free_list));
33495                     FATAL_GC_ERROR();
33496                 }
33497                 if (verify_undo_slot && (free_list_undo (free_list) != UNDO_EMPTY))
33498                 {
33499                     dprintf (3, ("Verifiying Heap: curr free list item %Ix has non empty undo slot",
33500                                  (size_t)free_list));
33501                     FATAL_GC_ERROR();
33502                 }
33503                 if ((gen_num != max_generation+1)&&(object_gennum (free_list)!= gen_num))
33504                 {
33505                     dprintf (3, ("Verifiying Heap: curr free list item %Ix is in the wrong generation free list",
33506                                  (size_t)free_list));
33507                     FATAL_GC_ERROR();
33508                 }
33509                     
33510                 prev = free_list;
33511                 free_list = free_list_slot (free_list);
33512             }
33513             //verify the sanity of the tail 
33514             uint8_t* tail = gen_alloc->alloc_list_tail_of (a_l_number);
33515             if (!((tail == 0) || (tail == prev)))
33516             {
33517                 dprintf (3, ("Verifying Heap: tail of free list is not correct"));
33518                 FATAL_GC_ERROR();
33519             }
33520             if (tail == 0)
33521             {
33522                 uint8_t* head = gen_alloc->alloc_list_head_of (a_l_number);
33523                 if ((head != 0) && (free_list_slot (head) != 0))
33524                 {
33525                     dprintf (3, ("Verifying Heap: tail of free list is not correct"));
33526                     FATAL_GC_ERROR();
33527                 }
33528             }
33529
33530             sz *=2;
33531         }
33532     }
33533 }
33534
33535 void
33536 gc_heap::verify_heap (BOOL begin_gc_p)
33537 {
33538     int             heap_verify_level = static_cast<int>(GCConfig::GetHeapVerifyLevel());
33539     size_t          last_valid_brick = 0;
33540     BOOL            bCurrentBrickInvalid = FALSE;
33541     BOOL            large_brick_p = TRUE;
33542     size_t          curr_brick = 0;
33543     size_t          prev_brick = (size_t)-1;
33544     int             curr_gen_num = max_generation+1;    
33545     heap_segment*   seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num ) ));
33546
33547     PREFIX_ASSUME(seg != NULL);
33548
33549     uint8_t*        curr_object = heap_segment_mem (seg);
33550     uint8_t*        prev_object = 0;
33551     uint8_t*        begin_youngest = generation_allocation_start(generation_of(0));
33552     uint8_t*        end_youngest = heap_segment_allocated (ephemeral_heap_segment);
33553     uint8_t*        next_boundary = generation_allocation_start (generation_of (max_generation - 1));
33554     int             align_const = get_alignment_constant (FALSE);
33555     size_t          total_objects_verified = 0;
33556     size_t          total_objects_verified_deep = 0;
33557
33558 #ifdef BACKGROUND_GC
33559     BOOL consider_bgc_mark_p    = FALSE;
33560     BOOL check_current_sweep_p  = FALSE;
33561     BOOL check_saved_sweep_p    = FALSE;
33562     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33563 #endif //BACKGROUND_GC
33564
33565 #ifdef MULTIPLE_HEAPS
33566     t_join* current_join = &gc_t_join;
33567 #ifdef BACKGROUND_GC
33568     if (settings.concurrent && (bgc_thread_id.IsCurrentThread()))
33569     {
33570         // We always call verify_heap on entry of GC on the SVR GC threads.
33571         current_join = &bgc_t_join;
33572     }
33573 #endif //BACKGROUND_GC
33574 #endif //MULTIPLE_HEAPS
33575
33576     UNREFERENCED_PARAMETER(begin_gc_p);
33577 #ifdef BACKGROUND_GC 
33578     dprintf (2,("[%s]GC#%d(%s): Verifying heap - begin", 
33579         (begin_gc_p ? "BEG" : "END"),
33580         VolatileLoad(&settings.gc_index), 
33581         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
33582 #else
33583     dprintf (2,("[%s]GC#%d: Verifying heap - begin", 
33584                 (begin_gc_p ? "BEG" : "END"), VolatileLoad(&settings.gc_index)));
33585 #endif //BACKGROUND_GC 
33586
33587 #ifndef MULTIPLE_HEAPS
33588     if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
33589         (ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
33590     {
33591         FATAL_GC_ERROR();
33592     }
33593 #endif //MULTIPLE_HEAPS
33594
33595 #ifdef BACKGROUND_GC
33596     //don't touch the memory because the program is allocating from it.
33597     if (!settings.concurrent)
33598 #endif //BACKGROUND_GC
33599     {
33600         if (!(heap_verify_level & GCConfig::HEAPVERIFY_NO_MEM_FILL))
33601         {
33602             //uninit the unused portions of segments.
33603             generation* gen1 = large_object_generation;
33604             heap_segment* seg1 = heap_segment_rw (generation_start_segment (gen1));
33605             PREFIX_ASSUME(seg1 != NULL);
33606
33607             while (1)
33608             {
33609                 if (seg1)
33610                 {
33611                     uint8_t* clear_start = heap_segment_allocated (seg1) - plug_skew;
33612                     if (heap_segment_used (seg1) > clear_start)
33613                     {
33614                         dprintf (3, ("setting end of seg %Ix: [%Ix-[%Ix to 0xaa", 
33615                                     heap_segment_mem (seg1),
33616                                     clear_start ,
33617                                     heap_segment_used (seg1)));
33618                         memset (heap_segment_allocated (seg1) - plug_skew, 0xaa,
33619                             (heap_segment_used (seg1) - clear_start));
33620                     }
33621                     seg1 = heap_segment_next_rw (seg1);
33622                 }
33623                 else
33624                 {
33625                     if (gen1 == large_object_generation)
33626                     {
33627                         gen1 = generation_of (max_generation);
33628                         seg1 = heap_segment_rw (generation_start_segment (gen1));
33629                         PREFIX_ASSUME(seg1 != NULL);
33630                     }
33631                     else
33632                     {
33633                         break;
33634                     }
33635                 }
33636             }
33637         }
33638     }
33639
33640 #ifdef MULTIPLE_HEAPS
33641     current_join->join(this, gc_join_verify_copy_table);
33642     if (current_join->joined())
33643     {
33644         // in concurrent GC, new segment could be allocated when GC is working so the card brick table might not be updated at this point
33645         for (int i = 0; i < n_heaps; i++)
33646         {
33647             //copy the card and brick tables
33648             if (g_gc_card_table != g_heaps[i]->card_table)
33649             {
33650                 g_heaps[i]->copy_brick_card_table();
33651             }
33652         }
33653
33654         current_join->restart();
33655     }
33656 #else
33657         if (g_gc_card_table != card_table)
33658             copy_brick_card_table();
33659 #endif //MULTIPLE_HEAPS
33660
33661     //verify that the generation structures makes sense
33662     {
33663         generation* gen = generation_of (max_generation);
33664
33665         assert (generation_allocation_start (gen) ==
33666                 heap_segment_mem (heap_segment_rw (generation_start_segment (gen))));
33667         int gen_num = max_generation-1;
33668         generation* prev_gen = gen;
33669         while (gen_num >= 0)
33670         {
33671             gen = generation_of (gen_num);
33672             assert (generation_allocation_segment (gen) == ephemeral_heap_segment);
33673             assert (generation_allocation_start (gen) >= heap_segment_mem (ephemeral_heap_segment));
33674             assert (generation_allocation_start (gen) < heap_segment_allocated (ephemeral_heap_segment));
33675
33676             if (generation_start_segment (prev_gen ) ==
33677                 generation_start_segment (gen))
33678             {
33679                 assert (generation_allocation_start (prev_gen) <
33680                         generation_allocation_start (gen));
33681             }
33682             prev_gen = gen;
33683             gen_num--;
33684         }
33685     }
33686
33687     while (1)
33688     {
33689         // Handle segment transitions
33690         if (curr_object >= heap_segment_allocated (seg))
33691         {
33692             if (curr_object > heap_segment_allocated(seg))
33693             {
33694                 dprintf (3, ("Verifiying Heap: curr_object: %Ix > heap_segment_allocated (seg: %Ix)",
33695                         (size_t)curr_object, (size_t)seg));
33696                 FATAL_GC_ERROR();
33697             }
33698             seg = heap_segment_next_in_range (seg);
33699             if (seg)
33700             {
33701 #ifdef BACKGROUND_GC
33702                 should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33703 #endif //BACKGROUND_GC
33704                 curr_object = heap_segment_mem(seg);
33705                 prev_object = 0;
33706                 continue;
33707             }
33708             else
33709             {
33710                 if (curr_gen_num == (max_generation+1))
33711                 {
33712                     curr_gen_num--;
33713                     seg = heap_segment_in_range (generation_start_segment (generation_of (curr_gen_num)));
33714
33715                     PREFIX_ASSUME(seg != NULL);
33716
33717 #ifdef BACKGROUND_GC
33718                     should_check_bgc_mark (seg, &consider_bgc_mark_p, &check_current_sweep_p, &check_saved_sweep_p);
33719 #endif //BACKGROUND_GC
33720                     curr_object = heap_segment_mem (seg);
33721                     prev_object = 0;
33722                     large_brick_p = FALSE;
33723                     align_const = get_alignment_constant (TRUE);
33724                 }
33725                 else
33726                     break;  // Done Verifying Heap -- no more segments
33727             }
33728         }
33729
33730         // Are we at the end of the youngest_generation?
33731         if (seg == ephemeral_heap_segment)
33732         {
33733             if (curr_object >= end_youngest)
33734             {
33735                 // prev_object length is too long if we hit this int3
33736                 if (curr_object > end_youngest)
33737                 {
33738                     dprintf (3, ("Verifiying Heap: curr_object: %Ix > end_youngest: %Ix",
33739                             (size_t)curr_object, (size_t)end_youngest));
33740                     FATAL_GC_ERROR();
33741                 }
33742                 break;
33743             }
33744             
33745             if ((curr_object >= next_boundary) && (curr_gen_num > 0))
33746             {
33747                 curr_gen_num--;
33748                 if (curr_gen_num > 0)
33749                 {
33750                     next_boundary = generation_allocation_start (generation_of (curr_gen_num - 1));
33751                 }
33752             }
33753         }
33754
33755          //if (is_mark_set (curr_object))
33756          //{
33757          //        printf ("curr_object: %Ix is marked!",(size_t)curr_object);
33758          //        FATAL_GC_ERROR();
33759          //}
33760
33761         size_t s = size (curr_object);
33762         dprintf (3, ("o: %Ix, s: %d", (size_t)curr_object, s));
33763         if (s == 0)
33764         {
33765             dprintf (3, ("Verifying Heap: size of current object %Ix == 0", curr_object));
33766             FATAL_GC_ERROR();
33767         }
33768
33769         // If object is not in the youngest generation, then lets
33770         // verify that the brick table is correct....
33771         if (((seg != ephemeral_heap_segment) ||
33772              (brick_of(curr_object) < brick_of(begin_youngest))))
33773         {
33774             curr_brick = brick_of(curr_object);
33775
33776             // Brick Table Verification...
33777             //
33778             // On brick transition
33779             //     if brick is negative
33780             //          verify that brick indirects to previous valid brick
33781             //     else
33782             //          set current brick invalid flag to be flipped if we
33783             //          encounter an object at the correct place
33784             //
33785             if (curr_brick != prev_brick)
33786             {
33787                 // If the last brick we were examining had positive
33788                 // entry but we never found the matching object, then
33789                 // we have a problem
33790                 // If prev_brick was the last one of the segment
33791                 // it's ok for it to be invalid because it is never looked at
33792                 if (bCurrentBrickInvalid &&
33793                     (curr_brick != brick_of (heap_segment_mem (seg))) &&
33794                     !heap_segment_read_only_p (seg))
33795                 {
33796                     dprintf (3, ("curr brick %Ix invalid", curr_brick));
33797                     FATAL_GC_ERROR();
33798                 }
33799
33800                 if (large_brick_p)
33801                 {
33802                     //large objects verify the table only if they are in
33803                     //range.
33804                     if ((heap_segment_reserved (seg) <= highest_address) &&
33805                         (heap_segment_mem (seg) >= lowest_address) &&
33806                         brick_table [curr_brick] != 0)
33807                     {
33808                         dprintf (3, ("curr_brick %Ix for large object %Ix not set to -32768",
33809                                 curr_brick, (size_t)curr_object));
33810                         FATAL_GC_ERROR();
33811                     }
33812                     else
33813                     {
33814                         bCurrentBrickInvalid = FALSE;
33815                     }
33816                 }
33817                 else
33818                 {
33819                     // If the current brick contains a negative value make sure
33820                     // that the indirection terminates at the last  valid brick
33821                     if (brick_table [curr_brick] <= 0)
33822                     {
33823                         if (brick_table [curr_brick] == 0)
33824                         {
33825                             dprintf(3, ("curr_brick %Ix for object %Ix set to 0",
33826                                     curr_brick, (size_t)curr_object));
33827                             FATAL_GC_ERROR();
33828                         }
33829                         ptrdiff_t i = curr_brick;
33830                         while ((i >= ((ptrdiff_t) brick_of (heap_segment_mem (seg)))) &&
33831                                (brick_table[i] < 0))
33832                         {
33833                             i = i + brick_table[i];
33834                         }
33835                         if (i <  ((ptrdiff_t)(brick_of (heap_segment_mem (seg))) - 1))
33836                         {
33837                             dprintf (3, ("ptrdiff i: %Ix < brick_of (heap_segment_mem (seg)):%Ix - 1. curr_brick: %Ix",
33838                                     i, brick_of (heap_segment_mem (seg)),
33839                                     curr_brick));
33840                             FATAL_GC_ERROR();
33841                         }
33842                         // if (i != last_valid_brick)
33843                         //  FATAL_GC_ERROR();
33844                         bCurrentBrickInvalid = FALSE;
33845                     }
33846                     else if (!heap_segment_read_only_p (seg))
33847                     {
33848                         bCurrentBrickInvalid = TRUE;
33849                     }
33850                 }
33851             }
33852
33853             if (bCurrentBrickInvalid)
33854             {
33855                 if (curr_object == (brick_address(curr_brick) + brick_table[curr_brick] - 1))
33856                 {
33857                     bCurrentBrickInvalid = FALSE;
33858                     last_valid_brick = curr_brick;
33859                 }
33860             }
33861         }
33862
33863         if (*((uint8_t**)curr_object) != (uint8_t *) g_gc_pFreeObjectMethodTable)
33864         {
33865 #ifdef FEATURE_LOH_COMPACTION
33866             if ((curr_gen_num == (max_generation+1)) && (prev_object != 0))
33867             {
33868                 assert (method_table (prev_object) == g_gc_pFreeObjectMethodTable);
33869             }
33870 #endif //FEATURE_LOH_COMPACTION
33871
33872             total_objects_verified++;
33873
33874             BOOL can_verify_deep = TRUE;
33875 #ifdef BACKGROUND_GC
33876             can_verify_deep = fgc_should_consider_object (curr_object, seg, consider_bgc_mark_p, check_current_sweep_p, check_saved_sweep_p);
33877 #endif //BACKGROUND_GC
33878
33879             BOOL deep_verify_obj = can_verify_deep;
33880             if ((heap_verify_level & GCConfig::HEAPVERIFY_DEEP_ON_COMPACT) && !settings.compaction)
33881                 deep_verify_obj = FALSE;
33882
33883             ((CObjectHeader*)curr_object)->ValidateHeap((Object*)curr_object, deep_verify_obj);
33884
33885             if (can_verify_deep)
33886             {
33887                 if (curr_gen_num > 0)
33888                 {
33889                     BOOL need_card_p = FALSE;
33890                     if (contain_pointers_or_collectible (curr_object))
33891                     {
33892                         dprintf (4, ("curr_object: %Ix", (size_t)curr_object));
33893                         size_t crd = card_of (curr_object);
33894                         BOOL found_card_p = card_set_p (crd);
33895
33896 #ifdef COLLECTIBLE_CLASS
33897                         if (is_collectible(curr_object))
33898                         {
33899                             uint8_t* class_obj = get_class_object (curr_object);
33900                             if ((class_obj < ephemeral_high) && (class_obj >= next_boundary))
33901                             {
33902                                 if (!found_card_p)
33903                                 {
33904                                     dprintf (3, ("Card not set, curr_object = [%Ix:%Ix pointing to class object %Ix",
33905                                                 card_of (curr_object), (size_t)curr_object, class_obj));
33906
33907                                     FATAL_GC_ERROR();
33908                                 }
33909                             }
33910                         }
33911 #endif //COLLECTIBLE_CLASS
33912
33913                         if (contain_pointers(curr_object))
33914                         {
33915                             go_through_object_nostart
33916                                 (method_table(curr_object), curr_object, s, oo,
33917                                 {
33918                                     if ((crd != card_of ((uint8_t*)oo)) && !found_card_p)
33919                                     {
33920                                         crd = card_of ((uint8_t*)oo);
33921                                         found_card_p = card_set_p (crd);
33922                                         need_card_p = FALSE;
33923                                     }
33924                                     if ((*oo < ephemeral_high) && (*oo >= next_boundary))
33925                                     {
33926                                         need_card_p = TRUE;
33927                                     }
33928
33929                                 if (need_card_p && !found_card_p)
33930                                 {
33931
33932                                         dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33933                                                     card_of (curr_object), (size_t)curr_object,
33934                                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33935                                         FATAL_GC_ERROR();
33936                                     }
33937                                 }
33938                                     );
33939                         }
33940                         if (need_card_p && !found_card_p)
33941                         {
33942                             dprintf (3, ("Card not set, curr_object = [%Ix:%Ix, %Ix:%Ix[",
33943                                     card_of (curr_object), (size_t)curr_object,
33944                                     card_of (curr_object+Align(s, align_const)), (size_t)curr_object+Align(s, align_const)));
33945                             FATAL_GC_ERROR();
33946                         }
33947                     }
33948                 }
33949                 total_objects_verified_deep++;
33950             }
33951         }
33952
33953         prev_object = curr_object;
33954         prev_brick = curr_brick;
33955         curr_object = curr_object + Align(s, align_const);
33956         if (curr_object < prev_object)
33957         {
33958             dprintf (3, ("overflow because of a bad object size: %Ix size %Ix", prev_object, s));
33959             FATAL_GC_ERROR();
33960         }
33961     }
33962
33963 #ifdef BACKGROUND_GC
33964     dprintf (2, ("(%s)(%s)(%s) total_objects_verified is %Id, total_objects_verified_deep is %Id", 
33965                  (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p () ? "FGC" : "NGC")),
33966                  (begin_gc_p ? "BEG" : "END"),
33967                  ((current_c_gc_state == c_gc_state_planning) ? "in plan" : "not in plan"),
33968                  total_objects_verified, total_objects_verified_deep));
33969     if (current_c_gc_state != c_gc_state_planning)
33970     {
33971         assert (total_objects_verified == total_objects_verified_deep);
33972     }
33973 #endif //BACKGROUND_GC
33974     
33975     verify_free_lists();
33976
33977 #ifdef FEATURE_PREMORTEM_FINALIZATION
33978     finalize_queue->CheckFinalizerObjects();
33979 #endif // FEATURE_PREMORTEM_FINALIZATION
33980
33981     {
33982         // to be consistent with handle table APIs pass a ScanContext*
33983         // to provide the heap number.  the SC isn't complete though so
33984         // limit its scope to handle table verification.
33985         ScanContext sc;
33986         sc.thread_number = heap_number;
33987         GCScan::VerifyHandleTable(max_generation, max_generation, &sc);
33988     }
33989
33990 #ifdef MULTIPLE_HEAPS
33991     current_join->join(this, gc_join_verify_objects_done);
33992     if (current_join->joined())
33993 #endif //MULTIPLE_HEAPS
33994     {
33995         GCToEEInterface::VerifySyncTableEntry();
33996 #ifdef MULTIPLE_HEAPS
33997         current_join->restart();
33998 #endif //MULTIPLE_HEAPS
33999     }
34000
34001 #ifdef BACKGROUND_GC 
34002     if (!settings.concurrent)
34003     {
34004         if (current_c_gc_state == c_gc_state_planning)
34005         {
34006             // temporarily commenting this out 'cause an FGC
34007             // could be triggered before we sweep ephemeral.
34008             //verify_seg_end_mark_array_cleared();
34009         }
34010     }
34011
34012     if (settings.concurrent)
34013     {
34014         verify_mark_array_cleared();
34015     }
34016     dprintf (2,("GC%d(%s): Verifying heap - end", 
34017         VolatileLoad(&settings.gc_index), 
34018         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC"))));
34019 #else
34020     dprintf (2,("GC#d: Verifying heap - end", VolatileLoad(&settings.gc_index)));
34021 #endif //BACKGROUND_GC 
34022 }
34023
34024 #endif  //VERIFY_HEAP
34025
34026
34027 void GCHeap::ValidateObjectMember (Object* obj)
34028 {
34029 #ifdef VERIFY_HEAP
34030     size_t s = size (obj);
34031     uint8_t* o = (uint8_t*)obj;
34032
34033     go_through_object_cl (method_table (obj), o, s, oo,
34034                                 {
34035                                     uint8_t* child_o = *oo;
34036                                     if (child_o)
34037                                     {
34038                                         dprintf (3, ("VOM: m: %Ix obj %Ix", (size_t)child_o, o));
34039                                         MethodTable *pMT = method_table (child_o);
34040                                         assert(pMT);
34041                                         if (!pMT->SanityCheck()) {
34042                                             dprintf (3, ("Bad member of %Ix %Ix",
34043                                                         (size_t)oo, (size_t)child_o));
34044                                             FATAL_GC_ERROR();
34045                                         }
34046                                     }
34047                                 } );
34048 #endif // VERIFY_HEAP
34049 }
34050
34051 void DestructObject (CObjectHeader* hdr)
34052 {
34053     UNREFERENCED_PARAMETER(hdr); // compiler bug? -- this *is*, indeed, referenced
34054     hdr->~CObjectHeader();
34055 }
34056
34057 HRESULT GCHeap::Shutdown ()
34058 {
34059     deleteGCShadow();
34060
34061     GCScan::GcRuntimeStructuresValid (FALSE);
34062
34063     // Cannot assert this, since we use SuspendEE as the mechanism to quiesce all
34064     // threads except the one performing the shutdown.
34065     // ASSERT( !GcInProgress );
34066
34067     // Guard against any more GC occurring and against any threads blocking
34068     // for GC to complete when the GC heap is gone.  This fixes a race condition
34069     // where a thread in GC is destroyed as part of process destruction and
34070     // the remaining threads block for GC complete.
34071
34072     //GCTODO
34073     //EnterAllocLock();
34074     //Enter();
34075     //EnterFinalizeLock();
34076     //SetGCDone();
34077
34078     // during shutdown lot of threads are suspended
34079     // on this even, we don't want to wake them up just yet
34080     //CloseHandle (WaitForGCEvent);
34081
34082     //find out if the global card table hasn't been used yet
34083     uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))];
34084     if (card_table_refcount (ct) == 0)
34085     {
34086         destroy_card_table (ct);
34087         g_gc_card_table = nullptr;
34088
34089 #ifdef FEATURE_MANUALLY_MANAGED_CARD_BUNDLES
34090         g_gc_card_bundle_table = nullptr;
34091 #endif
34092 #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
34093         SoftwareWriteWatch::StaticClose();
34094 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
34095     }
34096
34097     //destroy all segments on the standby list
34098     while(gc_heap::segment_standby_list != 0)
34099     {
34100         heap_segment* next_seg = heap_segment_next (gc_heap::segment_standby_list);
34101 #ifdef MULTIPLE_HEAPS
34102         (gc_heap::g_heaps[0])->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
34103 #else //MULTIPLE_HEAPS
34104         pGenGCHeap->delete_heap_segment (gc_heap::segment_standby_list, FALSE);
34105 #endif //MULTIPLE_HEAPS
34106         gc_heap::segment_standby_list = next_seg;
34107     }
34108
34109
34110 #ifdef MULTIPLE_HEAPS
34111
34112     for (int i = 0; i < gc_heap::n_heaps; i ++)
34113     {
34114         delete gc_heap::g_heaps[i]->vm_heap;
34115         //destroy pure GC stuff
34116         gc_heap::destroy_gc_heap (gc_heap::g_heaps[i]);
34117     }
34118 #else
34119     gc_heap::destroy_gc_heap (pGenGCHeap);
34120
34121 #endif //MULTIPLE_HEAPS
34122     gc_heap::shutdown_gc();
34123
34124     return S_OK;
34125 }
34126
34127 // Wait until a garbage collection is complete
34128 // returns NOERROR if wait was OK, other error code if failure.
34129 // WARNING: This will not undo the must complete state. If you are
34130 // in a must complete when you call this, you'd better know what you're
34131 // doing.
34132
34133 #ifdef FEATURE_PREMORTEM_FINALIZATION
34134 static
34135 HRESULT AllocateCFinalize(CFinalize **pCFinalize)
34136 {
34137     *pCFinalize = new (nothrow) CFinalize();
34138     if (*pCFinalize == NULL || !(*pCFinalize)->Initialize())
34139         return E_OUTOFMEMORY;
34140
34141     return S_OK;
34142 }
34143 #endif // FEATURE_PREMORTEM_FINALIZATION
34144
34145 // init the instance heap
34146 HRESULT GCHeap::Init(size_t hn)
34147 {
34148     HRESULT hres = S_OK;
34149
34150 #ifdef MULTIPLE_HEAPS
34151     if ((pGenGCHeap = gc_heap::make_gc_heap(this, (int)hn)) == 0)
34152         hres = E_OUTOFMEMORY;
34153 #else
34154     UNREFERENCED_PARAMETER(hn);
34155     if (!gc_heap::make_gc_heap())
34156         hres = E_OUTOFMEMORY;
34157 #endif //MULTIPLE_HEAPS
34158
34159     // Failed.
34160     return hres;
34161 }
34162
34163 //System wide initialization
34164 HRESULT GCHeap::Initialize()
34165 {
34166     HRESULT hr = S_OK;
34167
34168     g_gc_pFreeObjectMethodTable = GCToEEInterface::GetFreeObjectMethodTable();
34169     g_num_processors = GCToOSInterface::GetTotalProcessorCount();
34170     assert(g_num_processors != 0);
34171
34172 //Initialize the static members.
34173 #ifdef TRACE_GC
34174     GcDuration = 0;
34175     CreatedObjectCount = 0;
34176 #endif //TRACE_GC
34177
34178     bool is_restricted; 
34179     gc_heap::total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit (&is_restricted);
34180
34181 #ifdef BIT64
34182     gc_heap::heap_hard_limit = (size_t)GCConfig::GetGCHeapHardLimit();
34183
34184     if (!(gc_heap::heap_hard_limit))
34185     {
34186         uint32_t percent_of_mem = (uint32_t)GCConfig::GetGCHeapHardLimitPercent();
34187         if ((percent_of_mem > 0) && (percent_of_mem < 100))
34188         {
34189             gc_heap::heap_hard_limit = (size_t)(gc_heap::total_physical_mem * (uint64_t)percent_of_mem / (uint64_t)100);
34190         }
34191     }
34192
34193     // If the hard limit is specified, the user is saying even if the process is already
34194     // running in a container, use this limit for the GC heap.
34195     if (!(gc_heap::heap_hard_limit))
34196     {
34197         if (is_restricted)
34198         {
34199             uint64_t physical_mem_for_gc = gc_heap::total_physical_mem * (uint64_t)75 / (uint64_t)100;
34200             //printf ("returned physical mem %I64d, setting it to max (75%%: %I64d, 20mb)\n",
34201             //    gc_heap::total_physical_mem, physical_mem_for_gc);
34202             gc_heap::heap_hard_limit = (size_t)max ((20 * 1024 * 1024), physical_mem_for_gc);
34203         }
34204     }
34205
34206     //printf ("heap_hard_limit is %Id, total physical mem: %Id, %s restricted\n", 
34207     //    gc_heap::heap_hard_limit, gc_heap::total_physical_mem, (is_restricted ? "is" : "is not"));
34208 #endif //BIT64
34209
34210     uint32_t nhp = 1;
34211     uint32_t nhp_from_config = 0;
34212
34213 #ifdef MULTIPLE_HEAPS
34214     nhp_from_config = static_cast<uint32_t>(GCConfig::GetHeapCount());
34215     
34216     // GetCurrentProcessCpuCount only returns up to 64 procs.
34217     uint32_t nhp_from_process = GCToOSInterface::CanEnableGCCPUGroups() ?
34218                                 GCToOSInterface::GetTotalProcessorCount():
34219                                 GCToOSInterface::GetCurrentProcessCpuCount();
34220
34221     if (nhp_from_config)
34222     {
34223         // Even when the user specifies a heap count, it should not be more
34224         // than the number of procs this process can use.
34225         nhp_from_config = min (nhp_from_config, nhp_from_process);
34226     }
34227
34228     nhp = ((nhp_from_config == 0) ? nhp_from_process : nhp_from_config);
34229
34230     nhp = min (nhp, MAX_SUPPORTED_CPUS);
34231 #ifndef FEATURE_REDHAWK
34232     gc_heap::gc_thread_no_affinitize_p = (gc_heap::heap_hard_limit ? false : (GCConfig::GetNoAffinitize() != 0));
34233
34234     size_t gc_thread_affinity_mask = static_cast<size_t>(GCConfig::GetGCHeapAffinitizeMask());
34235
34236     if (gc_heap::heap_hard_limit)
34237     {
34238         gc_heap::gc_thread_no_affinitize_p = (gc_thread_affinity_mask == 0);
34239     }
34240
34241     if (!(gc_heap::gc_thread_no_affinitize_p))
34242     {
34243         if (!(GCToOSInterface::CanEnableGCCPUGroups()))
34244         {
34245             uintptr_t pmask, smask;
34246             if (GCToOSInterface::GetCurrentProcessAffinityMask(&pmask, &smask))
34247             {
34248                 pmask &= smask;
34249
34250 #ifdef FEATURE_PAL
34251                 // GetCurrentProcessAffinityMask can return pmask=0 and smask=0 on
34252                 // systems with more than 1 NUMA node. The pmask decides the
34253                 // number of GC heaps to be used and the processors they are
34254                 // affinitized with. So pmask is now set to reflect that 64
34255                 // processors are available to begin with. The actual processors in
34256                 // the system may be lower and are taken into account before
34257                 // finalizing the number of heaps.
34258                 if (!pmask)
34259                 {
34260                     pmask = SIZE_T_MAX;
34261                 }
34262 #endif // FEATURE_PAL
34263
34264                 if (gc_thread_affinity_mask)
34265                 {
34266                     pmask &= gc_thread_affinity_mask;
34267                 }
34268
34269                 process_mask = pmask;
34270
34271                 unsigned int set_bits_in_pmask = 0;
34272                 while (pmask)
34273                 {
34274                     if (pmask & 1)
34275                         set_bits_in_pmask++;
34276                     pmask >>= 1;
34277                 }
34278
34279                 nhp = min (nhp, set_bits_in_pmask);
34280
34281 #ifdef FEATURE_PAL
34282                 // Limit the GC heaps to the number of processors available in the system.
34283                 nhp = min (nhp, GCToOSInterface::GetTotalProcessorCount());
34284 #endif // FEATURE_PAL
34285             }
34286             else
34287             {
34288                 gc_heap::gc_thread_no_affinitize_p = true;
34289             }
34290         }
34291     }
34292 #endif //!FEATURE_REDHAWK
34293 #endif //MULTIPLE_HEAPS
34294
34295     size_t seg_size = 0;
34296     size_t large_seg_size = 0;
34297
34298     if (gc_heap::heap_hard_limit)
34299     {
34300         seg_size = gc_heap::get_segment_size_hard_limit (&nhp, (nhp_from_config == 0));
34301         gc_heap::soh_segment_size = seg_size;
34302         large_seg_size = seg_size * 2;
34303     }
34304     else
34305     {
34306         seg_size = get_valid_segment_size();
34307         gc_heap::soh_segment_size = seg_size;
34308         large_seg_size = get_valid_segment_size (TRUE);
34309     }
34310
34311     dprintf (1, ("%d heaps, soh seg size: %Id mb, loh: %Id mb\n", 
34312         nhp,
34313         (seg_size / (size_t)1024 / 1024), 
34314         (large_seg_size / 1024 / 1024)));
34315
34316     gc_heap::min_loh_segment_size = large_seg_size;
34317     gc_heap::min_segment_size = min (seg_size, large_seg_size);
34318 #ifdef SEG_MAPPING_TABLE
34319     gc_heap::min_segment_size_shr = index_of_highest_set_bit (gc_heap::min_segment_size);
34320 #endif //SEG_MAPPING_TABLE
34321
34322 #ifdef MULTIPLE_HEAPS
34323     gc_heap::n_heaps = nhp;
34324     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/, nhp);
34325 #else
34326     hr = gc_heap::initialize_gc (seg_size, large_seg_size /*LHEAP_ALLOC*/);
34327 #endif //MULTIPLE_HEAPS
34328
34329     if (hr != S_OK)
34330         return hr;
34331
34332     gc_heap::mem_one_percent = gc_heap::total_physical_mem / 100;
34333 #ifndef MULTIPLE_HEAPS
34334     gc_heap::mem_one_percent /= g_num_processors;
34335 #endif //!MULTIPLE_HEAPS
34336
34337     uint32_t highmem_th_from_config = (uint32_t)GCConfig::GetGCHighMemPercent();
34338     if (highmem_th_from_config)
34339     {
34340         gc_heap::high_memory_load_th = min (99, highmem_th_from_config);
34341         gc_heap::v_high_memory_load_th = min (99, (highmem_th_from_config + 7));
34342     }
34343     else
34344     {
34345         // We should only use this if we are in the "many process" mode which really is only applicable
34346         // to very powerful machines - before that's implemented, temporarily I am only enabling this for 80GB+ memory. 
34347         // For now I am using an estimate to calculate these numbers but this should really be obtained 
34348         // programmatically going forward.
34349         // I am assuming 47 processes using WKS GC and 3 using SVR GC.
34350         // I am assuming 3 in part due to the "very high memory load" is 97%.
34351         int available_mem_th = 10;
34352         if (gc_heap::total_physical_mem >= ((uint64_t)80 * 1024 * 1024 * 1024))
34353         {
34354             int adjusted_available_mem_th = 3 + (int)((float)47 / (float)(GCToOSInterface::GetTotalProcessorCount()));
34355             available_mem_th = min (available_mem_th, adjusted_available_mem_th);
34356         }
34357
34358         gc_heap::high_memory_load_th = 100 - available_mem_th;
34359         gc_heap::v_high_memory_load_th = 97;
34360     }
34361
34362     gc_heap::m_high_memory_load_th = min ((gc_heap::high_memory_load_th + 5), gc_heap::v_high_memory_load_th);
34363
34364     gc_heap::pm_stress_on = (GCConfig::GetGCProvModeStress() != 0);
34365
34366 #if defined(BIT64) 
34367     gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent;
34368 #endif // BIT64
34369
34370     WaitForGCEvent = new (nothrow) GCEvent;
34371
34372     if (!WaitForGCEvent)
34373     {
34374         return E_OUTOFMEMORY;
34375     }
34376
34377     if (!WaitForGCEvent->CreateManualEventNoThrow(TRUE))
34378     {
34379         return E_FAIL;
34380     }
34381
34382 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
34383 #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS)
34384     if (GCStress<cfg_any>::IsEnabled())  {
34385         for(int i = 0; i < GCHeap::NUM_HEAP_STRESS_OBJS; i++)
34386         {
34387             m_StressObjs[i] = CreateGlobalHandle(0);
34388         }
34389         m_CurStressObj = 0;
34390     }
34391 #endif //STRESS_HEAP && !MULTIPLE_HEAPS
34392 #endif // FEATURE_REDHAWK
34393
34394     initGCShadow();         // If we are debugging write barriers, initialize heap shadow
34395
34396 #ifdef MULTIPLE_HEAPS
34397
34398     for (unsigned i = 0; i < nhp; i++)
34399     {
34400         GCHeap* Hp = new (nothrow) GCHeap();
34401         if (!Hp)
34402             return E_OUTOFMEMORY;
34403
34404         if ((hr = Hp->Init (i))!= S_OK)
34405         {
34406             return hr;
34407         }
34408     }
34409     // initialize numa node to heap map
34410     heap_select::init_numa_node_to_heap_map(nhp);
34411 #else
34412     hr = Init (0);
34413 #endif //MULTIPLE_HEAPS
34414
34415     if (hr == S_OK)
34416     {
34417         GCScan::GcRuntimeStructuresValid (TRUE);
34418
34419         GCToEEInterface::DiagUpdateGenerationBounds();
34420     }
34421
34422     return hr;
34423 };
34424
34425 ////
34426 // GC callback functions
34427 bool GCHeap::IsPromoted(Object* object)
34428 {
34429 #ifdef _DEBUG
34430     ((CObjectHeader*)object)->Validate();
34431 #endif //_DEBUG
34432
34433     uint8_t* o = (uint8_t*)object;
34434
34435     if (gc_heap::settings.condemned_generation == max_generation)
34436     {
34437 #ifdef MULTIPLE_HEAPS
34438         gc_heap* hp = gc_heap::g_heaps[0];
34439 #else
34440         gc_heap* hp = pGenGCHeap;
34441 #endif //MULTIPLE_HEAPS
34442
34443 #ifdef BACKGROUND_GC
34444         if (gc_heap::settings.concurrent)
34445         {
34446             bool is_marked = (!((o < hp->background_saved_highest_address) && (o >= hp->background_saved_lowest_address))||
34447                             hp->background_marked (o));
34448             return is_marked;
34449         }
34450         else
34451 #endif //BACKGROUND_GC
34452         {
34453             return (!((o < hp->highest_address) && (o >= hp->lowest_address))
34454                     || hp->is_mark_set (o));
34455         }
34456     }
34457     else
34458     {
34459         gc_heap* hp = gc_heap::heap_of (o);
34460         return (!((o < hp->gc_high) && (o >= hp->gc_low))
34461                 || hp->is_mark_set (o));
34462     }
34463 }
34464
34465 size_t GCHeap::GetPromotedBytes(int heap_index)
34466 {
34467 #ifdef BACKGROUND_GC
34468     if (gc_heap::settings.concurrent)
34469     {
34470         return gc_heap::bpromoted_bytes (heap_index);
34471     }
34472     else
34473 #endif //BACKGROUND_GC
34474     {
34475         return gc_heap::promoted_bytes (heap_index);
34476     }
34477 }
34478
34479 void GCHeap::SetYieldProcessorScalingFactor (float scalingFactor)
34480 {
34481     assert (yp_spin_count_unit != 0);
34482     int saved_yp_spin_count_unit = yp_spin_count_unit;
34483     yp_spin_count_unit = (int)((float)yp_spin_count_unit * scalingFactor / (float)9);
34484
34485     // It's very suspicious if it becomes 0
34486     if (yp_spin_count_unit == 0)
34487     {
34488         yp_spin_count_unit = saved_yp_spin_count_unit;
34489     }
34490 }
34491
34492 unsigned int GCHeap::WhichGeneration (Object* object)
34493 {
34494     gc_heap* hp = gc_heap::heap_of ((uint8_t*)object);
34495     unsigned int g = hp->object_gennum ((uint8_t*)object);
34496     dprintf (3, ("%Ix is in gen %d", (size_t)object, g));
34497     return g;
34498 }
34499
34500 bool GCHeap::IsEphemeral (Object* object)
34501 {
34502     uint8_t* o = (uint8_t*)object;
34503     gc_heap* hp = gc_heap::heap_of (o);
34504     return !!hp->ephemeral_pointer_p (o);
34505 }
34506
34507 // Return NULL if can't find next object. When EE is not suspended,
34508 // the result is not accurate: if the input arg is in gen0, the function could 
34509 // return zeroed out memory as next object
34510 Object * GCHeap::NextObj (Object * object)
34511 {
34512 #ifdef VERIFY_HEAP
34513     uint8_t* o = (uint8_t*)object;
34514
34515 #ifndef FEATURE_BASICFREEZE
34516     if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address)))
34517     {
34518         return NULL;
34519     }
34520 #endif //!FEATURE_BASICFREEZE
34521
34522     heap_segment * hs = gc_heap::find_segment (o, FALSE);
34523     if (!hs)
34524     {
34525         return NULL;
34526     }
34527
34528     BOOL large_object_p = heap_segment_loh_p (hs);
34529     if (large_object_p)
34530         return NULL; //could be racing with another core allocating. 
34531 #ifdef MULTIPLE_HEAPS
34532     gc_heap* hp = heap_segment_heap (hs);
34533 #else //MULTIPLE_HEAPS
34534     gc_heap* hp = 0;
34535 #endif //MULTIPLE_HEAPS
34536     unsigned int g = hp->object_gennum ((uint8_t*)object);
34537     if ((g == 0) && hp->settings.demotion)
34538         return NULL;//could be racing with another core allocating. 
34539     int align_const = get_alignment_constant (!large_object_p);
34540     uint8_t* nextobj = o + Align (size (o), align_const);
34541     if (nextobj <= o) // either overflow or 0 sized object.
34542     {
34543         return NULL;
34544     }
34545
34546     if ((nextobj < heap_segment_mem(hs)) || 
34547         (nextobj >= heap_segment_allocated(hs) && hs != hp->ephemeral_heap_segment) || 
34548         (nextobj >= hp->alloc_allocated))
34549     {
34550         return NULL;
34551     }
34552
34553     return (Object *)nextobj;
34554 #else
34555     return nullptr;
34556 #endif // VERIFY_HEAP
34557 }
34558
34559 // returns TRUE if the pointer is in one of the GC heaps.
34560 bool GCHeap::IsHeapPointer (void* vpObject, bool small_heap_only)
34561 {
34562     // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment 
34563     // no longer calls GCEvent::Wait which eventually takes a lock.
34564
34565     uint8_t* object = (uint8_t*) vpObject;
34566 #ifndef FEATURE_BASICFREEZE
34567     if (!((object < g_gc_highest_address) && (object >= g_gc_lowest_address)))
34568         return FALSE;
34569 #endif //!FEATURE_BASICFREEZE
34570
34571     heap_segment * hs = gc_heap::find_segment (object, small_heap_only);
34572     return !!hs;
34573 }
34574
34575 #ifdef STRESS_PINNING
34576 static n_promote = 0;
34577 #endif //STRESS_PINNING
34578 // promote an object
34579 void GCHeap::Promote(Object** ppObject, ScanContext* sc, uint32_t flags)
34580 {
34581     THREAD_NUMBER_FROM_CONTEXT;
34582 #ifndef MULTIPLE_HEAPS
34583     const int thread = 0;
34584 #endif //!MULTIPLE_HEAPS
34585
34586     uint8_t* o = (uint8_t*)*ppObject;
34587
34588     if (o == 0)
34589         return;
34590
34591 #ifdef DEBUG_DestroyedHandleValue
34592     // we can race with destroy handle during concurrent scan
34593     if (o == (uint8_t*)DEBUG_DestroyedHandleValue)
34594         return;
34595 #endif //DEBUG_DestroyedHandleValue
34596
34597     HEAP_FROM_THREAD;
34598
34599     gc_heap* hp = gc_heap::heap_of (o);
34600
34601     dprintf (3, ("Promote %Ix", (size_t)o));
34602
34603 #ifdef INTERIOR_POINTERS
34604     if (flags & GC_CALL_INTERIOR)
34605     {
34606         if ((o < hp->gc_low) || (o >= hp->gc_high))
34607         {
34608             return;
34609         }
34610         if ( (o = hp->find_object (o, hp->gc_low)) == 0)
34611         {
34612             return;
34613         }
34614
34615     }
34616 #endif //INTERIOR_POINTERS
34617
34618 #ifdef FEATURE_CONSERVATIVE_GC
34619     // For conservative GC, a value on stack may point to middle of a free object.
34620     // In this case, we don't need to promote the pointer.
34621     if (GCConfig::GetConservativeGC()
34622         && ((CObjectHeader*)o)->IsFree())
34623     {
34624         return;
34625     }
34626 #endif
34627
34628 #ifdef _DEBUG
34629     ((CObjectHeader*)o)->ValidatePromote(sc, flags);
34630 #else 
34631     UNREFERENCED_PARAMETER(sc);
34632 #endif //_DEBUG
34633
34634     if (flags & GC_CALL_PINNED)
34635         hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
34636
34637 #ifdef STRESS_PINNING
34638     if ((++n_promote % 20) == 1)
34639             hp->pin_object (o, (uint8_t**) ppObject, hp->gc_low, hp->gc_high);
34640 #endif //STRESS_PINNING
34641
34642 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34643     size_t promoted_size_begin = hp->promoted_bytes (thread);
34644 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34645
34646     if ((o >= hp->gc_low) && (o < hp->gc_high))
34647     {
34648         hpt->mark_object_simple (&o THREAD_NUMBER_ARG);
34649     }
34650
34651 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
34652     size_t promoted_size_end = hp->promoted_bytes (thread);
34653     if (g_fEnableAppDomainMonitoring)
34654     {
34655         if (sc->pCurrentDomain)
34656         {
34657             GCToEEInterface::RecordSurvivedBytesForHeap((promoted_size_end - promoted_size_begin), thread, sc->pCurrentDomain);
34658         }
34659     }
34660 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
34661
34662     STRESS_LOG_ROOT_PROMOTE(ppObject, o, o ? header(o)->GetMethodTable() : NULL);
34663 }
34664
34665 void GCHeap::Relocate (Object** ppObject, ScanContext* sc,
34666                        uint32_t flags)
34667 {
34668     UNREFERENCED_PARAMETER(sc);
34669
34670     uint8_t* object = (uint8_t*)(Object*)(*ppObject);
34671     
34672     THREAD_NUMBER_FROM_CONTEXT;
34673
34674     //dprintf (3, ("Relocate location %Ix\n", (size_t)ppObject));
34675     dprintf (3, ("R: %Ix", (size_t)ppObject));
34676     
34677     if (object == 0)
34678         return;
34679
34680     gc_heap* hp = gc_heap::heap_of (object);
34681
34682 #ifdef _DEBUG
34683     if (!(flags & GC_CALL_INTERIOR))
34684     {
34685         // We cannot validate this object if it's in the condemned gen because it could 
34686         // be one of the objects that were overwritten by an artificial gap due to a pinned plug.
34687         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
34688         {
34689             ((CObjectHeader*)object)->Validate(FALSE);
34690         }
34691     }
34692 #endif //_DEBUG
34693
34694     dprintf (3, ("Relocate %Ix\n", (size_t)object));
34695
34696     uint8_t* pheader;
34697
34698     if ((flags & GC_CALL_INTERIOR) && gc_heap::settings.loh_compaction)
34699     {
34700         if (!((object >= hp->gc_low) && (object < hp->gc_high)))
34701         {
34702             return;
34703         }
34704
34705         if (gc_heap::loh_object_p (object))
34706         {
34707             pheader = hp->find_object (object, 0);
34708             if (pheader == 0)
34709             {
34710                 return;
34711             }
34712
34713             ptrdiff_t ref_offset = object - pheader;
34714             hp->relocate_address(&pheader THREAD_NUMBER_ARG);
34715             *ppObject = (Object*)(pheader + ref_offset);
34716             return;
34717         }
34718     }
34719
34720     {
34721         pheader = object;
34722         hp->relocate_address(&pheader THREAD_NUMBER_ARG);
34723         *ppObject = (Object*)pheader;
34724     }
34725
34726     STRESS_LOG_ROOT_RELOCATE(ppObject, object, pheader, ((!(flags & GC_CALL_INTERIOR)) ? ((Object*)object)->GetGCSafeMethodTable() : 0));
34727 }
34728
34729 /*static*/ bool GCHeap::IsObjectInFixedHeap(Object *pObj)
34730 {
34731     // For now we simply look at the size of the object to determine if it in the
34732     // fixed heap or not. If the bit indicating this gets set at some point
34733     // we should key off that instead.
34734     return size( pObj ) >= loh_size_threshold;
34735 }
34736
34737 #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way
34738 #ifdef STRESS_HEAP
34739
34740 void StressHeapDummy ();
34741
34742 static int32_t GCStressStartCount = -1;
34743 static int32_t GCStressCurCount = 0;
34744 static int32_t GCStressStartAtJit = -1;
34745
34746 // the maximum number of foreground GCs we'll induce during one BGC
34747 // (this number does not include "naturally" occuring GCs).
34748 static int32_t GCStressMaxFGCsPerBGC = -1;
34749
34750 // CLRRandom implementation can produce FPU exceptions if 
34751 // the test/application run by CLR is enabling any FPU exceptions. 
34752 // We want to avoid any unexpected exception coming from stress 
34753 // infrastructure, so CLRRandom is not an option.
34754 // The code below is a replicate of CRT rand() implementation.
34755 // Using CRT rand() is not an option because we will interfere with the user application
34756 // that may also use it. 
34757 int StressRNG(int iMaxValue)
34758 {
34759     static BOOL bisRandInit = FALSE;
34760     static int lHoldrand = 1L;
34761
34762     if (!bisRandInit)
34763     {
34764         lHoldrand = (int)time(NULL);
34765         bisRandInit = TRUE;
34766     }
34767     int randValue = (((lHoldrand = lHoldrand * 214013L + 2531011L) >> 16) & 0x7fff);
34768     return randValue % iMaxValue;
34769 }
34770 #endif // STRESS_HEAP
34771 #endif // !FEATURE_REDHAWK
34772
34773 // free up object so that things will move and then do a GC
34774 //return TRUE if GC actually happens, otherwise FALSE
34775 bool GCHeap::StressHeap(gc_alloc_context * context)
34776 {
34777 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34778     alloc_context* acontext = static_cast<alloc_context*>(context);
34779     assert(context != nullptr);
34780
34781     // if GC stress was dynamically disabled during this run we return FALSE
34782     if (!GCStressPolicy::IsEnabled())
34783         return FALSE;
34784
34785 #ifdef _DEBUG
34786     if (g_pConfig->FastGCStressLevel() && !GCToEEInterface::GetThread()->StressHeapIsEnabled()) {
34787         return FALSE;
34788     }
34789
34790 #endif //_DEBUG
34791
34792     if ((g_pConfig->GetGCStressLevel() & EEConfig::GCSTRESS_UNIQUE)
34793 #ifdef _DEBUG
34794         || g_pConfig->FastGCStressLevel() > 1
34795 #endif //_DEBUG
34796         ) {
34797         if (!Thread::UniqueStack(&acontext)) {
34798             return FALSE;
34799         }
34800     }
34801
34802 #ifdef BACKGROUND_GC
34803         // don't trigger a GC from the GC threads but still trigger GCs from user threads.
34804         if (GCToEEInterface::WasCurrentThreadCreatedByGC())
34805         {
34806             return FALSE;
34807         }
34808 #endif //BACKGROUND_GC
34809
34810         if (GCStressStartAtJit == -1 || GCStressStartCount == -1)
34811         {
34812             GCStressStartCount = CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_GCStressStart);
34813             GCStressStartAtJit = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressStartAtJit);
34814         }
34815
34816         if (GCStressMaxFGCsPerBGC == -1)
34817         {
34818             GCStressMaxFGCsPerBGC = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_GCStressMaxFGCsPerBGC);
34819             if (g_pConfig->IsGCStressMix() && GCStressMaxFGCsPerBGC == -1)
34820                 GCStressMaxFGCsPerBGC = 6;
34821         }
34822
34823 #ifdef _DEBUG
34824         if (g_JitCount < GCStressStartAtJit)
34825             return FALSE;
34826 #endif //_DEBUG
34827
34828         // Allow programmer to skip the first N Stress GCs so that you can
34829         // get to the interesting ones faster.
34830         Interlocked::Increment(&GCStressCurCount);
34831         if (GCStressCurCount < GCStressStartCount)
34832             return FALSE;
34833
34834         // throttle the number of stress-induced GCs by a factor given by GCStressStep
34835         if ((GCStressCurCount % g_pConfig->GetGCStressStep()) != 0)
34836         {
34837             return FALSE;
34838         }
34839
34840 #ifdef BACKGROUND_GC
34841         if (IsConcurrentGCEnabled() && IsConcurrentGCInProgress())
34842         {
34843             // allow a maximum number of stress induced FGCs during one BGC
34844             if (gc_stress_fgcs_in_bgc >= GCStressMaxFGCsPerBGC)
34845                 return FALSE;
34846             ++gc_stress_fgcs_in_bgc;
34847         }
34848 #endif // BACKGROUND_GC
34849
34850     if (g_pStringClass == 0)
34851     {
34852         // If the String class has not been loaded, dont do any stressing. This should
34853         // be kept to a minimum to get as complete coverage as possible.
34854         _ASSERTE(g_fEEInit);
34855         return FALSE;
34856     }
34857
34858 #ifndef MULTIPLE_HEAPS
34859     static int32_t OneAtATime = -1;
34860
34861     // Only bother with this if the stress level is big enough and if nobody else is
34862     // doing it right now.  Note that some callers are inside the AllocLock and are
34863     // guaranteed synchronized.  But others are using AllocationContexts and have no
34864     // particular synchronization.
34865     //
34866     // For this latter case, we want a very high-speed way of limiting this to one
34867     // at a time.  A secondary advantage is that we release part of our StressObjs
34868     // buffer sparingly but just as effectively.
34869
34870     if (Interlocked::Increment(&OneAtATime) == 0 &&
34871         !TrackAllocations()) // Messing with object sizes can confuse the profiler (see ICorProfilerInfo::GetObjectSize)
34872     {
34873         StringObject* str;
34874
34875         // If the current string is used up
34876         if (HndFetchHandle(m_StressObjs[m_CurStressObj]) == 0)
34877         {
34878             // Populate handles with strings
34879             int i = m_CurStressObj;
34880             while(HndFetchHandle(m_StressObjs[i]) == 0)
34881             {
34882                 _ASSERTE(m_StressObjs[i] != 0);
34883                 unsigned strLen = ((unsigned)loh_size_threshold - 32) / sizeof(WCHAR);
34884                 unsigned strSize = PtrAlign(StringObject::GetSize(strLen));
34885                 
34886                 // update the cached type handle before allocating
34887                 SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass));
34888                 str = (StringObject*) pGenGCHeap->allocate (strSize, acontext);
34889                 if (str)
34890                 {
34891                     str->SetMethodTable (g_pStringClass);
34892                     str->SetStringLength (strLen);
34893                     HndAssignHandle(m_StressObjs[i], ObjectToOBJECTREF(str));
34894                 }
34895                 i = (i + 1) % NUM_HEAP_STRESS_OBJS;
34896                 if (i == m_CurStressObj) break;
34897             }
34898
34899             // advance the current handle to the next string
34900             m_CurStressObj = (m_CurStressObj + 1) % NUM_HEAP_STRESS_OBJS;
34901         }
34902
34903         // Get the current string
34904         str = (StringObject*) OBJECTREFToObject(HndFetchHandle(m_StressObjs[m_CurStressObj]));
34905         if (str)
34906         {
34907             // Chop off the end of the string and form a new object out of it.
34908             // This will 'free' an object at the begining of the heap, which will
34909             // force data movement.  Note that we can only do this so many times.
34910             // before we have to move on to the next string.
34911             unsigned sizeOfNewObj = (unsigned)Align(min_obj_size * 31);
34912             if (str->GetStringLength() > sizeOfNewObj / sizeof(WCHAR))
34913             {
34914                 unsigned sizeToNextObj = (unsigned)Align(size(str));
34915                 uint8_t* freeObj = ((uint8_t*) str) + sizeToNextObj - sizeOfNewObj;
34916                 pGenGCHeap->make_unused_array (freeObj, sizeOfNewObj);                    
34917                 str->SetStringLength(str->GetStringLength() - (sizeOfNewObj / sizeof(WCHAR)));
34918             }
34919             else
34920             {
34921                 // Let the string itself become garbage.
34922                 // will be realloced next time around
34923                 HndAssignHandle(m_StressObjs[m_CurStressObj], 0);
34924             }
34925         }
34926     }
34927     Interlocked::Decrement(&OneAtATime);
34928 #endif // !MULTIPLE_HEAPS
34929     if (IsConcurrentGCEnabled())
34930     {
34931         int rgen = StressRNG(10);
34932
34933         // gen0:gen1:gen2 distribution: 40:40:20
34934         if (rgen >= 8)
34935             rgen = 2;
34936         else if (rgen >= 4)
34937             rgen = 1;
34938     else
34939             rgen = 0;
34940
34941         GarbageCollectTry (rgen, FALSE, collection_gcstress);
34942     }
34943     else
34944     {
34945         GarbageCollect(max_generation, FALSE, collection_gcstress);
34946     }
34947
34948     return TRUE;
34949 #else
34950     UNREFERENCED_PARAMETER(context);
34951     return FALSE;
34952 #endif // defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
34953 }
34954
34955
34956 #ifdef FEATURE_PREMORTEM_FINALIZATION
34957 #define REGISTER_FOR_FINALIZATION(_object, _size) \
34958     hp->finalize_queue->RegisterForFinalization (0, (_object), (_size))
34959 #else // FEATURE_PREMORTEM_FINALIZATION
34960 #define REGISTER_FOR_FINALIZATION(_object, _size) true
34961 #endif // FEATURE_PREMORTEM_FINALIZATION
34962
34963 #define CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(_object, _size, _register) do {  \
34964     if ((_object) == NULL || ((_register) && !REGISTER_FOR_FINALIZATION(_object, _size)))   \
34965     {                                                                                       \
34966         STRESS_LOG_OOM_STACK(_size);                                                        \
34967         return NULL;                                                                        \
34968     }                                                                                       \
34969 } while (false)
34970
34971 //
34972 // Small Object Allocator
34973 //
34974 //
34975 // Allocate small object with an alignment requirement of 8-bytes.
34976 Object*
34977 GCHeap::AllocAlign8(gc_alloc_context* ctx, size_t size, uint32_t flags )
34978 {
34979 #ifdef FEATURE_64BIT_ALIGNMENT
34980     CONTRACTL {
34981         NOTHROW;
34982         GC_TRIGGERS;
34983     } CONTRACTL_END;
34984
34985     alloc_context* acontext = static_cast<alloc_context*>(ctx);
34986
34987 #ifdef MULTIPLE_HEAPS
34988     if (acontext->get_alloc_heap() == 0)
34989     {
34990         AssignHeap (acontext);
34991         assert (acontext->get_alloc_heap());
34992     }
34993
34994     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
34995 #else
34996     gc_heap* hp = pGenGCHeap;
34997 #endif //MULTIPLE_HEAPS
34998
34999     return AllocAlign8Common(hp, acontext, size, flags);
35000 #else
35001     UNREFERENCED_PARAMETER(ctx);
35002     UNREFERENCED_PARAMETER(size);
35003     UNREFERENCED_PARAMETER(flags);
35004     assert(!"should not call GCHeap::AllocAlign8 without FEATURE_64BIT_ALIGNMENT defined!");
35005     return nullptr;
35006 #endif  //FEATURE_64BIT_ALIGNMENT
35007 }
35008
35009 // Common code used by both variants of AllocAlign8 above.
35010 Object*
35011 GCHeap::AllocAlign8Common(void* _hp, alloc_context* acontext, size_t size, uint32_t flags)
35012 {
35013 #ifdef FEATURE_64BIT_ALIGNMENT
35014     CONTRACTL {
35015         NOTHROW;
35016         GC_TRIGGERS;
35017     } CONTRACTL_END;
35018
35019     gc_heap* hp = (gc_heap*)_hp;
35020
35021     TRIGGERSGC();
35022
35023     Object* newAlloc = NULL;
35024
35025 #ifdef TRACE_GC
35026 #ifdef COUNT_CYCLES
35027     AllocStart = GetCycleCount32();
35028     unsigned finish;
35029 #elif defined(ENABLE_INSTRUMENTATION)
35030     unsigned AllocStart = GetInstLogTime();
35031     unsigned finish;
35032 #endif //COUNT_CYCLES
35033 #endif //TRACE_GC
35034
35035     if (size < loh_size_threshold)
35036     {
35037 #ifdef TRACE_GC
35038         AllocSmallCount++;
35039 #endif //TRACE_GC
35040
35041         // Depending on where in the object the payload requiring 8-byte alignment resides we might have to
35042         // align the object header on an 8-byte boundary or midway between two such boundaries. The unaligned
35043         // case is indicated to the GC via the GC_ALLOC_ALIGN8_BIAS flag.
35044         size_t desiredAlignment = (flags & GC_ALLOC_ALIGN8_BIAS) ? 4 : 0;
35045
35046         // Retrieve the address of the next allocation from the context (note that we're inside the alloc
35047         // lock at this point).
35048         uint8_t*  result = acontext->alloc_ptr;
35049
35050         // Will an allocation at this point yield the correct alignment and fit into the remainder of the
35051         // context?
35052         if ((((size_t)result & 7) == desiredAlignment) && ((result + size) <= acontext->alloc_limit))
35053         {
35054             // Yes, we can just go ahead and make the allocation.
35055             newAlloc = (Object*) hp->allocate (size, acontext);
35056             ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
35057         }
35058         else
35059         {
35060             // No, either the next available address is not aligned in the way we require it or there's
35061             // not enough space to allocate an object of the required size. In both cases we allocate a
35062             // padding object (marked as a free object). This object's size is such that it will reverse
35063             // the alignment of the next header (asserted below).
35064             //
35065             // We allocate both together then decide based on the result whether we'll format the space as
35066             // free object + real object or real object + free object.
35067             ASSERT((Align(min_obj_size) & 7) == 4);
35068             CObjectHeader *freeobj = (CObjectHeader*) hp->allocate (Align(size) + Align(min_obj_size), acontext);
35069             if (freeobj)
35070             {
35071                 if (((size_t)freeobj & 7) == desiredAlignment)
35072                 {
35073                     // New allocation has desired alignment, return this one and place the free object at the
35074                     // end of the allocated space.
35075                     newAlloc = (Object*)freeobj;
35076                     freeobj = (CObjectHeader*)((uint8_t*)freeobj + Align(size));
35077                 }
35078                 else
35079                 {
35080                     // New allocation is still mis-aligned, format the initial space as a free object and the
35081                     // rest of the space should be correctly aligned for the real object.
35082                     newAlloc = (Object*)((uint8_t*)freeobj + Align(min_obj_size));
35083                     ASSERT(((size_t)newAlloc & 7) == desiredAlignment);
35084                 }
35085                 freeobj->SetFree(min_obj_size);
35086             }
35087         }
35088     }
35089     else
35090     {
35091         // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't
35092         // support mis-aligned object headers so we can't support biased headers as above. Luckily for us
35093         // we've managed to arrange things so the only case where we see a bias is for boxed value types and
35094         // these can never get large enough to be allocated on the LOH.
35095         ASSERT(65536 < loh_size_threshold);
35096         ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0);
35097
35098         alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
35099
35100         newAlloc = (Object*) hp->allocate_large_object (size, acontext->alloc_bytes_loh);
35101         ASSERT(((size_t)newAlloc & 7) == 0);
35102     }
35103
35104     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
35105
35106 #ifdef TRACE_GC
35107 #ifdef COUNT_CYCLES
35108     finish = GetCycleCount32();
35109 #elif defined(ENABLE_INSTRUMENTATION)
35110     finish = GetInstLogTime();
35111 #endif //COUNT_CYCLES
35112     AllocDuration += finish - AllocStart;
35113     AllocCount++;
35114 #endif //TRACE_GC
35115     return newAlloc;
35116 #else
35117     UNREFERENCED_PARAMETER(_hp);
35118     UNREFERENCED_PARAMETER(acontext);
35119     UNREFERENCED_PARAMETER(size);
35120     UNREFERENCED_PARAMETER(flags);
35121     assert(!"Should not call GCHeap::AllocAlign8Common without FEATURE_64BIT_ALIGNMENT defined!");
35122     return nullptr;
35123 #endif // FEATURE_64BIT_ALIGNMENT
35124 }
35125
35126 Object *
35127 GCHeap::AllocLHeap( size_t size, uint32_t flags REQD_ALIGN_DCL)
35128 {
35129     CONTRACTL {
35130         NOTHROW;
35131         GC_TRIGGERS;
35132     } CONTRACTL_END;
35133
35134     TRIGGERSGC();
35135
35136     Object* newAlloc = NULL;
35137
35138 #ifdef TRACE_GC
35139 #ifdef COUNT_CYCLES
35140     AllocStart = GetCycleCount32();
35141     unsigned finish;
35142 #elif defined(ENABLE_INSTRUMENTATION)
35143     unsigned AllocStart = GetInstLogTime();
35144     unsigned finish;
35145 #endif //COUNT_CYCLES
35146 #endif //TRACE_GC
35147
35148 #ifdef MULTIPLE_HEAPS
35149     //take the first heap....
35150     gc_heap* hp = gc_heap::g_heaps[0];
35151 #else
35152     gc_heap* hp = pGenGCHeap;
35153 #ifdef _PREFAST_
35154     // prefix complains about us dereferencing hp in wks build even though we only access static members
35155     // this way. not sure how to shut it up except for this ugly workaround:
35156     PREFIX_ASSUME(hp != NULL);
35157 #endif //_PREFAST_
35158 #endif //MULTIPLE_HEAPS
35159
35160     alloc_context* acontext = generation_alloc_context (hp->generation_of (max_generation+1));
35161
35162     newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
35163 #ifdef FEATURE_STRUCTALIGN
35164     newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
35165 #endif // FEATURE_STRUCTALIGN
35166     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
35167
35168 #ifdef TRACE_GC
35169 #ifdef COUNT_CYCLES
35170     finish = GetCycleCount32();
35171 #elif defined(ENABLE_INSTRUMENTATION)
35172     finish = GetInstLogTime();
35173 #endif //COUNT_CYCLES
35174     AllocDuration += finish - AllocStart;
35175     AllocCount++;
35176 #endif //TRACE_GC
35177     return newAlloc;
35178 }
35179
35180 Object*
35181 GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_DCL)
35182 {
35183     CONTRACTL {
35184         NOTHROW;
35185         GC_TRIGGERS;
35186     } CONTRACTL_END;
35187
35188     TRIGGERSGC();
35189
35190     Object* newAlloc = NULL;
35191     alloc_context* acontext = static_cast<alloc_context*>(context);
35192
35193 #ifdef TRACE_GC
35194 #ifdef COUNT_CYCLES
35195     AllocStart = GetCycleCount32();
35196     unsigned finish;
35197 #elif defined(ENABLE_INSTRUMENTATION)
35198     unsigned AllocStart = GetInstLogTime();
35199     unsigned finish;
35200 #endif //COUNT_CYCLES
35201 #endif //TRACE_GC
35202
35203 #ifdef MULTIPLE_HEAPS
35204     if (acontext->get_alloc_heap() == 0)
35205     {
35206         AssignHeap (acontext);
35207         assert (acontext->get_alloc_heap());
35208     }
35209 #endif //MULTIPLE_HEAPS
35210
35211 #ifdef MULTIPLE_HEAPS
35212     gc_heap* hp = acontext->get_alloc_heap()->pGenGCHeap;
35213 #else
35214     gc_heap* hp = pGenGCHeap;
35215 #ifdef _PREFAST_
35216     // prefix complains about us dereferencing hp in wks build even though we only access static members
35217     // this way. not sure how to shut it up except for this ugly workaround:
35218     PREFIX_ASSUME(hp != NULL);
35219 #endif //_PREFAST_
35220 #endif //MULTIPLE_HEAPS
35221
35222     if (size < loh_size_threshold)
35223     {
35224
35225 #ifdef TRACE_GC
35226         AllocSmallCount++;
35227 #endif //TRACE_GC
35228         newAlloc = (Object*) hp->allocate (size + ComputeMaxStructAlignPad(requiredAlignment), acontext);
35229 #ifdef FEATURE_STRUCTALIGN
35230         newAlloc = (Object*) hp->pad_for_alignment ((uint8_t*) newAlloc, requiredAlignment, size, acontext);
35231 #endif // FEATURE_STRUCTALIGN
35232 //        ASSERT (newAlloc);
35233     }
35234     else 
35235     {
35236         newAlloc = (Object*) hp->allocate_large_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), acontext->alloc_bytes_loh);
35237 #ifdef FEATURE_STRUCTALIGN
35238         newAlloc = (Object*) hp->pad_for_alignment_large ((uint8_t*) newAlloc, requiredAlignment, size);
35239 #endif // FEATURE_STRUCTALIGN
35240     }
35241
35242     CHECK_ALLOC_AND_POSSIBLY_REGISTER_FOR_FINALIZATION(newAlloc, size, flags & GC_ALLOC_FINALIZE);
35243
35244 #ifdef TRACE_GC
35245 #ifdef COUNT_CYCLES
35246     finish = GetCycleCount32();
35247 #elif defined(ENABLE_INSTRUMENTATION)
35248     finish = GetInstLogTime();
35249 #endif //COUNT_CYCLES
35250     AllocDuration += finish - AllocStart;
35251     AllocCount++;
35252 #endif //TRACE_GC
35253     return newAlloc;
35254 }
35255
35256 void
35257 GCHeap::FixAllocContext (gc_alloc_context* context, void* arg, void *heap)
35258 {
35259     alloc_context* acontext = static_cast<alloc_context*>(context);
35260 #ifdef MULTIPLE_HEAPS
35261
35262     if (arg != 0)
35263         acontext->alloc_count = 0;
35264
35265     uint8_t * alloc_ptr = acontext->alloc_ptr;
35266
35267     if (!alloc_ptr)
35268         return;
35269
35270     // The acontext->alloc_heap can be out of sync with the ptrs because
35271     // of heap re-assignment in allocate
35272     gc_heap* hp = gc_heap::heap_of (alloc_ptr);
35273 #else
35274     gc_heap* hp = pGenGCHeap;
35275 #endif //MULTIPLE_HEAPS
35276
35277     if (heap == NULL || heap == hp)
35278     {
35279         hp->fix_allocation_context (acontext, ((arg != 0)? TRUE : FALSE),
35280                                     get_alignment_constant(TRUE));
35281     }
35282 }
35283
35284 Object*
35285 GCHeap::GetContainingObject (void *pInteriorPtr, bool fCollectedGenOnly)
35286 {
35287     uint8_t *o = (uint8_t*)pInteriorPtr;
35288
35289     gc_heap* hp = gc_heap::heap_of (o);
35290
35291     uint8_t* lowest = (fCollectedGenOnly ? hp->gc_low : hp->lowest_address);
35292     uint8_t* highest = (fCollectedGenOnly ? hp->gc_high : hp->highest_address);
35293
35294     if (o >= lowest && o < highest)
35295     {
35296         o = hp->find_object (o, lowest);
35297     }
35298     else
35299     {
35300         o = NULL;
35301     }
35302     
35303     return (Object *)o;
35304 }
35305
35306 BOOL should_collect_optimized (dynamic_data* dd, BOOL low_memory_p)
35307 {
35308     if (dd_new_allocation (dd) < 0)
35309     {
35310         return TRUE;
35311     }
35312
35313     if (((float)(dd_new_allocation (dd)) / (float)dd_desired_allocation (dd)) < (low_memory_p ? 0.7 : 0.3))
35314     {
35315         return TRUE;
35316     }
35317
35318     return FALSE;
35319 }
35320
35321 //----------------------------------------------------------------------------
35322 // #GarbageCollector
35323 //
35324 //  API to ensure that a complete new garbage collection takes place
35325 //
35326 HRESULT
35327 GCHeap::GarbageCollect (int generation, bool low_memory_p, int mode)
35328 {
35329 #if defined(BIT64) 
35330     if (low_memory_p)
35331     {
35332         size_t total_allocated = 0;
35333         size_t total_desired = 0;
35334 #ifdef MULTIPLE_HEAPS
35335         int hn = 0;
35336         for (hn = 0; hn < gc_heap::n_heaps; hn++)
35337         {
35338             gc_heap* hp = gc_heap::g_heaps [hn];
35339             total_desired += dd_desired_allocation (hp->dynamic_data_of (0));
35340             total_allocated += dd_desired_allocation (hp->dynamic_data_of (0))-
35341                 dd_new_allocation (hp->dynamic_data_of (0));
35342         }
35343 #else
35344         gc_heap* hp = pGenGCHeap;
35345         total_desired = dd_desired_allocation (hp->dynamic_data_of (0));
35346         total_allocated = dd_desired_allocation (hp->dynamic_data_of (0))-
35347             dd_new_allocation (hp->dynamic_data_of (0));
35348 #endif //MULTIPLE_HEAPS
35349
35350         if ((total_desired > gc_heap::mem_one_percent) && (total_allocated < gc_heap::mem_one_percent))
35351         {
35352             dprintf (2, ("Async low mem but we've only allocated %d (< 10%% of physical mem) out of %d, returning",
35353                          total_allocated, total_desired));
35354
35355             return S_OK;
35356         }
35357     }
35358 #endif // BIT64 
35359
35360 #ifdef MULTIPLE_HEAPS
35361     gc_heap* hpt = gc_heap::g_heaps[0];
35362 #else
35363     gc_heap* hpt = 0;
35364 #endif //MULTIPLE_HEAPS
35365
35366     generation = (generation < 0) ? max_generation : min (generation, max_generation);
35367     dynamic_data* dd = hpt->dynamic_data_of (generation);
35368
35369 #ifdef BACKGROUND_GC
35370     if (recursive_gc_sync::background_running_p())
35371     {
35372         if ((mode == collection_optimized) || (mode & collection_non_blocking))
35373         {
35374             return S_OK;
35375         }
35376         if (mode & collection_blocking)
35377         {
35378             pGenGCHeap->background_gc_wait();
35379             if (mode & collection_optimized)
35380             {
35381                 return S_OK;
35382             }
35383         }
35384     }
35385 #endif //BACKGROUND_GC
35386
35387     if (mode & collection_optimized)
35388     {
35389         if (pGenGCHeap->gc_started)
35390         {
35391             return S_OK;
35392         }
35393         else 
35394         {
35395             BOOL should_collect = FALSE;
35396             BOOL should_check_loh = (generation == max_generation);
35397 #ifdef MULTIPLE_HEAPS
35398             for (int i = 0; i < gc_heap::n_heaps; i++)
35399             {
35400                 dynamic_data* dd1 = gc_heap::g_heaps [i]->dynamic_data_of (generation);
35401                 dynamic_data* dd2 = (should_check_loh ? 
35402                                      (gc_heap::g_heaps [i]->dynamic_data_of (max_generation + 1)) :
35403                                      0);
35404
35405                 if (should_collect_optimized (dd1, low_memory_p))
35406                 {
35407                     should_collect = TRUE;
35408                     break;
35409                 }
35410                 if (dd2 && should_collect_optimized (dd2, low_memory_p))
35411                 {
35412                     should_collect = TRUE;
35413                     break;
35414                 }
35415             }
35416 #else
35417             should_collect = should_collect_optimized (dd, low_memory_p);
35418             if (!should_collect && should_check_loh)
35419             {
35420                 should_collect = 
35421                     should_collect_optimized (hpt->dynamic_data_of (max_generation + 1), low_memory_p);
35422             }
35423 #endif //MULTIPLE_HEAPS
35424             if (!should_collect)
35425             {
35426                 return S_OK;
35427             }
35428         }
35429     }
35430
35431     size_t CollectionCountAtEntry = dd_collection_count (dd);
35432     size_t BlockingCollectionCountAtEntry = gc_heap::full_gc_counts[gc_type_blocking];
35433     size_t CurrentCollectionCount = 0;
35434
35435 retry:
35436
35437     CurrentCollectionCount = GarbageCollectTry(generation, low_memory_p, mode);
35438     
35439     if ((mode & collection_blocking) && 
35440         (generation == max_generation) && 
35441         (gc_heap::full_gc_counts[gc_type_blocking] == BlockingCollectionCountAtEntry))
35442     {
35443 #ifdef BACKGROUND_GC
35444         if (recursive_gc_sync::background_running_p())
35445         {
35446             pGenGCHeap->background_gc_wait();
35447         }
35448 #endif //BACKGROUND_GC
35449
35450         goto retry;
35451     }
35452
35453     if (CollectionCountAtEntry == CurrentCollectionCount)
35454     {
35455         goto retry;
35456     }
35457
35458     return S_OK;
35459 }
35460
35461 size_t
35462 GCHeap::GarbageCollectTry (int generation, BOOL low_memory_p, int mode)
35463 {
35464     int gen = (generation < 0) ? 
35465                max_generation : min (generation, max_generation);
35466
35467     gc_reason reason = reason_empty;
35468     
35469     if (low_memory_p) 
35470     {
35471         if (mode & collection_blocking)
35472         {
35473             reason = reason_lowmemory_blocking;
35474         }
35475         else
35476         {
35477             reason = reason_lowmemory;
35478         }
35479     }
35480     else
35481     {
35482         reason = reason_induced;
35483     }
35484
35485     if (reason == reason_induced)
35486     {
35487         if (mode & collection_compacting)
35488         {
35489             reason = reason_induced_compacting;
35490         }
35491         else if (mode & collection_non_blocking)
35492         {
35493             reason = reason_induced_noforce;
35494         }
35495 #ifdef STRESS_HEAP
35496         else if (mode & collection_gcstress)
35497         {
35498             reason = reason_gcstress;
35499         }
35500 #endif
35501     }
35502
35503     return GarbageCollectGeneration (gen, reason);
35504 }
35505
35506 void gc_heap::do_pre_gc()
35507 {
35508     STRESS_LOG_GC_STACK;
35509
35510 #ifdef STRESS_LOG
35511     STRESS_LOG_GC_START(VolatileLoad(&settings.gc_index),
35512                         (uint32_t)settings.condemned_generation,
35513                         (uint32_t)settings.reason);
35514 #endif // STRESS_LOG
35515
35516 #ifdef MULTIPLE_HEAPS
35517     gc_heap* hp = g_heaps[0];
35518 #else
35519     gc_heap* hp = 0;
35520 #endif //MULTIPLE_HEAPS
35521
35522 #ifdef BACKGROUND_GC
35523     settings.b_state = hp->current_bgc_state;
35524 #endif //BACKGROUND_GC
35525
35526 #ifdef TRACE_GC
35527     size_t total_allocated_since_last_gc = get_total_allocated_since_last_gc();
35528 #ifdef BACKGROUND_GC
35529     dprintf (1, ("*GC* %d(gen0:%d)(%d)(alloc: %Id)(%s)(%d)", 
35530         VolatileLoad(&settings.gc_index), 
35531         dd_collection_count (hp->dynamic_data_of (0)),
35532         settings.condemned_generation,
35533         total_allocated_since_last_gc,
35534         (settings.concurrent ? "BGC" : (recursive_gc_sync::background_running_p() ? "FGC" : "NGC")),
35535         settings.b_state));
35536 #else
35537     dprintf (1, ("*GC* %d(gen0:%d)(%d)(alloc: %Id)", 
35538         VolatileLoad(&settings.gc_index), 
35539         dd_collection_count(hp->dynamic_data_of(0)),
35540         settings.condemned_generation,
35541         total_allocated_since_last_gc));
35542 #endif //BACKGROUND_GC
35543
35544     if (heap_hard_limit)
35545     {
35546         size_t total_heap_committed = get_total_committed_size();
35547         size_t total_heap_committed_recorded = current_total_committed - current_total_committed_bookkeeping;
35548         dprintf (1, ("(%d)GC commit BEG #%Id: %Id (recorded: %Id)", 
35549             settings.condemned_generation,
35550             (size_t)settings.gc_index, total_heap_committed, total_heap_committed_recorded));
35551     }
35552 #endif //TRACE_GC
35553
35554     // TODO: this can happen...it's because of the way we are calling
35555     // do_pre_gc, will fix later.
35556     //if (last_gc_index > VolatileLoad(&settings.gc_index))
35557     //{
35558     //    FATAL_GC_ERROR();
35559     //}
35560
35561     last_gc_index = VolatileLoad(&settings.gc_index);
35562     GCHeap::UpdatePreGCCounters();
35563 #if defined(__linux__)
35564     GCToEEInterface::UpdateGCEventStatus(static_cast<int>(GCEventStatus::GetEnabledLevel(GCEventProvider_Default)),
35565                                          static_cast<int>(GCEventStatus::GetEnabledKeywords(GCEventProvider_Default)),
35566                                          static_cast<int>(GCEventStatus::GetEnabledLevel(GCEventProvider_Private)),
35567                                          static_cast<int>(GCEventStatus::GetEnabledKeywords(GCEventProvider_Private)));
35568 #endif // __linux__
35569
35570     if (settings.concurrent)
35571     {
35572 #ifdef BACKGROUND_GC
35573         full_gc_counts[gc_type_background]++;
35574 #if defined(STRESS_HEAP) && !defined(FEATURE_REDHAWK)
35575         GCHeap::gc_stress_fgcs_in_bgc = 0;
35576 #endif // STRESS_HEAP && !FEATURE_REDHAWK
35577 #endif // BACKGROUND_GC
35578     }
35579     else
35580     {
35581         if (settings.condemned_generation == max_generation)
35582         {
35583             full_gc_counts[gc_type_blocking]++;
35584         }
35585         else
35586         {
35587 #ifdef BACKGROUND_GC
35588             if (settings.background_p)
35589             {
35590                 ephemeral_fgc_counts[settings.condemned_generation]++;
35591             }
35592 #endif //BACKGROUND_GC
35593         }
35594     }
35595
35596 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
35597     if (g_fEnableAppDomainMonitoring)
35598     {
35599         GCToEEInterface::ResetTotalSurvivedBytes();
35600     }
35601 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
35602 }
35603
35604 #ifdef GC_CONFIG_DRIVEN
35605 void gc_heap::record_interesting_info_per_heap()
35606 {
35607     // datapoints are always from the last blocking GC so don't record again
35608     // for BGCs.
35609     if (!(settings.concurrent))
35610     {
35611         for (int i = 0; i < max_idp_count; i++)
35612         {
35613             interesting_data_per_heap[i] += interesting_data_per_gc[i];
35614         }
35615     }
35616
35617     int compact_reason = get_gc_data_per_heap()->get_mechanism (gc_heap_compact);
35618     if (compact_reason >= 0)
35619         (compact_reasons_per_heap[compact_reason])++;
35620     int expand_mechanism = get_gc_data_per_heap()->get_mechanism (gc_heap_expand);
35621     if (expand_mechanism >= 0)
35622         (expand_mechanisms_per_heap[expand_mechanism])++;
35623
35624     for (int i = 0; i < max_gc_mechanism_bits_count; i++)
35625     {
35626         if (get_gc_data_per_heap()->is_mechanism_bit_set ((gc_mechanism_bit_per_heap)i))
35627             (interesting_mechanism_bits_per_heap[i])++;
35628     }
35629
35630     //         h#  | GC  | gen | C   | EX  | NF  | BF  | ML  | DM  || PreS | PostS | Merge | Conv | Pre | Post | PrPo | PreP | PostP |
35631     cprintf (("%2d | %6d | %1d | %1s | %2s | %2s | %2s | %2s | %2s || %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id | %5Id |",
35632             heap_number,
35633             (size_t)settings.gc_index,
35634             settings.condemned_generation,
35635             // TEMP - I am just doing this for wks GC 'cuase I wanna see the pattern of doing C/S GCs.
35636             (settings.compaction ? (((compact_reason >= 0) && gc_heap_compact_reason_mandatory_p[compact_reason]) ? "M" : "W") : ""), // compaction
35637             ((expand_mechanism >= 0)? "X" : ""), // EX
35638             ((expand_mechanism == expand_reuse_normal) ? "X" : ""), // NF
35639             ((expand_mechanism == expand_reuse_bestfit) ? "X" : ""), // BF
35640             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_mark_list_bit) ? "X" : ""), // ML
35641             (get_gc_data_per_heap()->is_mechanism_bit_set (gc_demotion_bit) ? "X" : ""), // DM
35642             interesting_data_per_gc[idp_pre_short],
35643             interesting_data_per_gc[idp_post_short],
35644             interesting_data_per_gc[idp_merged_pin],
35645             interesting_data_per_gc[idp_converted_pin],
35646             interesting_data_per_gc[idp_pre_pin],
35647             interesting_data_per_gc[idp_post_pin],
35648             interesting_data_per_gc[idp_pre_and_post_pin],
35649             interesting_data_per_gc[idp_pre_short_padded],
35650             interesting_data_per_gc[idp_post_short_padded]));
35651 }
35652
35653 void gc_heap::record_global_mechanisms()
35654 {
35655     for (int i = 0; i < max_global_mechanisms_count; i++)
35656     {
35657         if (gc_data_global.get_mechanism_p ((gc_global_mechanism_p)i))
35658         {
35659             ::record_global_mechanism (i);
35660         }
35661     }
35662 }
35663
35664 BOOL gc_heap::should_do_sweeping_gc (BOOL compact_p)
35665 {
35666     if (!compact_ratio)
35667         return (!compact_p);
35668
35669     size_t compact_count = compact_or_sweep_gcs[0];
35670     size_t sweep_count = compact_or_sweep_gcs[1];
35671
35672     size_t total_count = compact_count + sweep_count;
35673     BOOL should_compact = compact_p;
35674     if (total_count > 3)
35675     {
35676         if (compact_p)
35677         {
35678             int temp_ratio = (int)((compact_count + 1) * 100 / (total_count + 1));
35679             if (temp_ratio > compact_ratio)
35680             {
35681                 // cprintf (("compact would be: %d, total_count: %d, ratio would be %d%% > target\n",
35682                 //     (compact_count + 1), (total_count + 1), temp_ratio));
35683                 should_compact = FALSE;
35684             }
35685         }
35686         else
35687         {
35688             int temp_ratio = (int)((sweep_count + 1) * 100 / (total_count + 1));
35689             if (temp_ratio > (100 - compact_ratio))
35690             {
35691                 // cprintf (("sweep would be: %d, total_count: %d, ratio would be %d%% > target\n",
35692                 //     (sweep_count + 1), (total_count + 1), temp_ratio));
35693                 should_compact = TRUE;
35694             }
35695         }
35696     }
35697
35698     return !should_compact;
35699 }
35700 #endif //GC_CONFIG_DRIVEN
35701
35702 bool gc_heap::is_pm_ratio_exceeded()
35703 {
35704     size_t maxgen_frag = 0;
35705     size_t maxgen_size = 0;
35706     size_t total_heap_size = get_total_heap_size();
35707
35708 #ifdef MULTIPLE_HEAPS
35709     for (int i = 0; i < gc_heap::n_heaps; i++)
35710     {
35711         gc_heap* hp = gc_heap::g_heaps[i];
35712 #else //MULTIPLE_HEAPS
35713     {
35714         gc_heap* hp = pGenGCHeap;
35715 #endif //MULTIPLE_HEAPS
35716
35717         maxgen_frag += dd_fragmentation (hp->dynamic_data_of (max_generation));
35718         maxgen_size += hp->generation_size (max_generation);
35719     }
35720
35721     double maxgen_ratio = (double)maxgen_size / (double)total_heap_size;
35722     double maxgen_frag_ratio = (double)maxgen_frag / (double)maxgen_size;
35723     dprintf (GTC_LOG, ("maxgen %Id(%d%% total heap), frag: %Id (%d%% maxgen)",
35724         maxgen_size, (int)(maxgen_ratio * 100.0), 
35725         maxgen_frag, (int)(maxgen_frag_ratio * 100.0)));
35726
35727     bool maxgen_highfrag_p = ((maxgen_ratio > 0.5) && (maxgen_frag_ratio > 0.1));
35728
35729     // We need to adjust elevation here because if there's enough fragmentation it's not
35730     // unproductive.
35731     if (maxgen_highfrag_p)
35732     {
35733         settings.should_lock_elevation = FALSE;
35734         dprintf (GTC_LOG, ("high frag gen2, turn off elevation"));
35735     }
35736
35737     return maxgen_highfrag_p;
35738 }
35739
35740 void gc_heap::do_post_gc()
35741 {
35742     if (!settings.concurrent)
35743     {
35744         initGCShadow();
35745     }
35746
35747 #ifdef TRACE_GC
35748 #ifdef COUNT_CYCLES
35749     AllocStart = GetCycleCount32();
35750 #else
35751     AllocStart = clock();
35752 #endif //COUNT_CYCLES
35753 #endif //TRACE_GC
35754
35755 #ifdef MULTIPLE_HEAPS
35756     gc_heap* hp = g_heaps[0];
35757 #else
35758     gc_heap* hp = 0;
35759 #endif //MULTIPLE_HEAPS
35760     
35761     GCToEEInterface::GcDone(settings.condemned_generation);
35762
35763     GCToEEInterface::DiagGCEnd(VolatileLoad(&settings.gc_index),
35764                          (uint32_t)settings.condemned_generation,
35765                          (uint32_t)settings.reason,
35766                          !!settings.concurrent);
35767
35768     //dprintf (1, (" ****end of Garbage Collection**** %d(gen0:%d)(%d)", 
35769     dprintf (1, ("*EGC* %d(gen0:%d)(%d)(%s)", 
35770         VolatileLoad(&settings.gc_index), 
35771         dd_collection_count(hp->dynamic_data_of(0)),
35772         settings.condemned_generation,
35773         (settings.concurrent ? "BGC" : "GC")));
35774
35775     if (settings.exit_memory_load != 0)
35776         last_gc_memory_load = settings.exit_memory_load;
35777     else if (settings.entry_memory_load != 0)
35778         last_gc_memory_load = settings.entry_memory_load;
35779
35780     last_gc_heap_size = get_total_heap_size();
35781     last_gc_fragmentation = get_total_fragmentation();
35782
35783 #ifdef TRACE_GC
35784     if (heap_hard_limit)
35785     {
35786         size_t total_heap_committed = get_total_committed_size();
35787         size_t total_heap_committed_recorded = current_total_committed - current_total_committed_bookkeeping;
35788         dprintf (1, ("(%d)GC commit END #%Id: %Id (recorded: %Id), heap %Id, frag: %Id", 
35789             settings.condemned_generation,
35790             (size_t)settings.gc_index, total_heap_committed, total_heap_committed_recorded,
35791             last_gc_heap_size, last_gc_fragmentation));
35792     }
35793 #endif //TRACE_GC
35794
35795     // Note we only do this at the end of full blocking GCs because we do not want
35796     // to turn on this provisional mode during the middle of a BGC.
35797     if ((settings.condemned_generation == max_generation) && (!settings.concurrent))
35798     {
35799         if (pm_stress_on)
35800         {
35801             size_t full_compacting_gc_count = full_gc_counts[gc_type_compacting];
35802             if (provisional_mode_triggered)
35803             {
35804                 uint64_t r = gc_rand::get_rand(10);
35805                 if ((full_compacting_gc_count - provisional_triggered_gc_count) >= r)
35806                 {
35807                     provisional_mode_triggered = false;
35808                     provisional_off_gc_count = full_compacting_gc_count;
35809                     dprintf (GTC_LOG, ("%Id NGC2s when turned on, %Id NGCs since(%Id)",
35810                         provisional_triggered_gc_count, (full_compacting_gc_count - provisional_triggered_gc_count),
35811                         num_provisional_triggered));
35812                 }
35813             }
35814             else
35815             {
35816                 uint64_t r = gc_rand::get_rand(5);
35817                 if ((full_compacting_gc_count - provisional_off_gc_count) >= r)
35818                 {
35819                     provisional_mode_triggered = true;
35820                     provisional_triggered_gc_count = full_compacting_gc_count;
35821                     num_provisional_triggered++;
35822                     dprintf (GTC_LOG, ("%Id NGC2s when turned off, %Id NGCs since(%Id)",
35823                         provisional_off_gc_count, (full_compacting_gc_count - provisional_off_gc_count),
35824                         num_provisional_triggered));
35825                 }
35826             }
35827         }
35828         else
35829         {
35830             if (provisional_mode_triggered)
35831             {
35832                 if ((settings.entry_memory_load < high_memory_load_th) ||
35833                     !is_pm_ratio_exceeded())
35834                 {
35835                     dprintf (GTC_LOG, ("turning off PM"));
35836                     provisional_mode_triggered = false;
35837                 }
35838             }
35839             else if ((settings.entry_memory_load >= high_memory_load_th) && is_pm_ratio_exceeded())
35840             {
35841                 dprintf (GTC_LOG, ("highmem && highfrag - turning on PM"));
35842                 provisional_mode_triggered = true;
35843                 num_provisional_triggered++;
35844             }
35845         }
35846     }
35847
35848     GCHeap::UpdatePostGCCounters();
35849 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
35850     //if (g_fEnableARM)
35851     //{
35852     //    SystemDomain::GetADSurvivedBytes();
35853     //}
35854 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
35855
35856 #ifdef STRESS_LOG
35857     STRESS_LOG_GC_END(VolatileLoad(&settings.gc_index),
35858                       (uint32_t)settings.condemned_generation,
35859                       (uint32_t)settings.reason);
35860 #endif // STRESS_LOG
35861
35862 #ifdef GC_CONFIG_DRIVEN
35863     if (!settings.concurrent)
35864     {
35865         if (settings.compaction)
35866             (compact_or_sweep_gcs[0])++;
35867         else
35868             (compact_or_sweep_gcs[1])++;
35869     }
35870
35871 #ifdef MULTIPLE_HEAPS
35872     for (int i = 0; i < n_heaps; i++)
35873         g_heaps[i]->record_interesting_info_per_heap();
35874 #else
35875     record_interesting_info_per_heap();
35876 #endif //MULTIPLE_HEAPS
35877     record_global_mechanisms();
35878 #endif //GC_CONFIG_DRIVEN
35879 }
35880
35881 unsigned GCHeap::GetGcCount()
35882 {
35883     return (unsigned int)VolatileLoad(&pGenGCHeap->settings.gc_index);
35884 }
35885
35886 size_t
35887 GCHeap::GarbageCollectGeneration (unsigned int gen, gc_reason reason)
35888 {
35889     dprintf (2, ("triggered a GC!"));
35890
35891 #ifdef MULTIPLE_HEAPS
35892     gc_heap* hpt = gc_heap::g_heaps[0];
35893 #else
35894     gc_heap* hpt = 0;
35895 #endif //MULTIPLE_HEAPS
35896     bool cooperative_mode = true;
35897     dynamic_data* dd = hpt->dynamic_data_of (gen);
35898     size_t localCount = dd_collection_count (dd);
35899
35900     enter_spin_lock (&gc_heap::gc_lock);
35901     dprintf (SPINLOCK_LOG, ("GC Egc"));
35902     ASSERT_HOLDING_SPIN_LOCK(&gc_heap::gc_lock);
35903
35904     //don't trigger another GC if one was already in progress
35905     //while waiting for the lock
35906     {
35907         size_t col_count = dd_collection_count (dd);
35908
35909         if (localCount != col_count)
35910         {
35911 #ifdef SYNCHRONIZATION_STATS
35912             gc_lock_contended++;
35913 #endif //SYNCHRONIZATION_STATS
35914             dprintf (SPINLOCK_LOG, ("no need GC Lgc"));
35915             leave_spin_lock (&gc_heap::gc_lock);
35916
35917             // We don't need to release msl here 'cause this means a GC
35918             // has happened and would have release all msl's.
35919             return col_count;
35920          }
35921     }
35922
35923 #ifdef COUNT_CYCLES
35924     int gc_start = GetCycleCount32();
35925 #endif //COUNT_CYCLES
35926
35927 #ifdef TRACE_GC
35928 #ifdef COUNT_CYCLES
35929     AllocDuration += GetCycleCount32() - AllocStart;
35930 #else
35931     AllocDuration += clock() - AllocStart;
35932 #endif //COUNT_CYCLES
35933 #endif //TRACE_GC
35934
35935         gc_heap::g_low_memory_status = (reason == reason_lowmemory) || 
35936                                        (reason == reason_lowmemory_blocking) ||
35937                                        (gc_heap::latency_level == latency_level_memory_footprint);
35938
35939         gc_trigger_reason = reason;
35940
35941 #ifdef MULTIPLE_HEAPS
35942     for (int i = 0; i < gc_heap::n_heaps; i++)
35943     {
35944         gc_heap::g_heaps[i]->reset_gc_done();
35945     }
35946 #else
35947     gc_heap::reset_gc_done();
35948 #endif //MULTIPLE_HEAPS
35949
35950     gc_heap::gc_started = TRUE;
35951
35952     {
35953         init_sync_log_stats();
35954
35955 #ifndef MULTIPLE_HEAPS
35956         cooperative_mode = gc_heap::enable_preemptive ();
35957
35958         dprintf (2, ("Suspending EE"));
35959         BEGIN_TIMING(suspend_ee_during_log);
35960         GCToEEInterface::SuspendEE(SUSPEND_FOR_GC);
35961         END_TIMING(suspend_ee_during_log);
35962         gc_heap::proceed_with_gc_p = gc_heap::should_proceed_with_gc();
35963         gc_heap::disable_preemptive (cooperative_mode);
35964         if (gc_heap::proceed_with_gc_p)
35965             pGenGCHeap->settings.init_mechanisms();
35966         else
35967             gc_heap::update_collection_counts_for_no_gc();
35968
35969 #endif //!MULTIPLE_HEAPS
35970     }
35971
35972 // MAP_EVENT_MONITORS(EE_MONITOR_GARBAGE_COLLECTIONS, NotifyEvent(EE_EVENT_TYPE_GC_STARTED, 0));
35973
35974 #ifdef TRACE_GC
35975 #ifdef COUNT_CYCLES
35976     unsigned start;
35977     unsigned finish;
35978     start = GetCycleCount32();
35979 #else
35980     clock_t start;
35981     clock_t finish;
35982     start = clock();
35983 #endif //COUNT_CYCLES
35984     PromotedObjectCount = 0;
35985 #endif //TRACE_GC
35986
35987     unsigned int condemned_generation_number = gen;
35988
35989     // We want to get a stack from the user thread that triggered the GC
35990     // instead of on the GC thread which is the case for Server GC.
35991     // But we are doing it for Workstation GC as well to be uniform.
35992     FIRE_EVENT(GCTriggered, static_cast<uint32_t>(reason));
35993
35994 #ifdef MULTIPLE_HEAPS
35995     GcCondemnedGeneration = condemned_generation_number;
35996
35997     cooperative_mode = gc_heap::enable_preemptive ();
35998
35999     BEGIN_TIMING(gc_during_log);
36000     gc_heap::ee_suspend_event.Set();
36001     gc_heap::wait_for_gc_done();
36002     END_TIMING(gc_during_log);
36003
36004     gc_heap::disable_preemptive (cooperative_mode);
36005
36006     condemned_generation_number = GcCondemnedGeneration;
36007 #else
36008         if (gc_heap::proceed_with_gc_p)
36009         {
36010             BEGIN_TIMING(gc_during_log);
36011             pGenGCHeap->garbage_collect (condemned_generation_number);
36012             if (gc_heap::pm_trigger_full_gc)
36013             {
36014                 pGenGCHeap->garbage_collect_pm_full_gc();
36015             }
36016             END_TIMING(gc_during_log);
36017         }
36018 #endif //MULTIPLE_HEAPS
36019
36020 #ifdef TRACE_GC
36021 #ifdef COUNT_CYCLES
36022     finish = GetCycleCount32();
36023 #else
36024     finish = clock();
36025 #endif //COUNT_CYCLES
36026     GcDuration += finish - start;
36027     dprintf (3,
36028              ("<GC# %d> Condemned: %d, Duration: %d, total: %d Alloc Avg: %d, Small Objects:%d Large Objects:%d",
36029               VolatileLoad(&pGenGCHeap->settings.gc_index), condemned_generation_number,
36030               finish - start, GcDuration,
36031               AllocCount ? (AllocDuration / AllocCount) : 0,
36032               AllocSmallCount, AllocBigCount));
36033     AllocCount = 0;
36034     AllocDuration = 0;
36035 #endif // TRACE_GC
36036
36037 #ifdef BACKGROUND_GC
36038     // We are deciding whether we should fire the alloc wait end event here
36039     // because in begin_foreground we could be calling end_foreground 
36040     // if we need to retry.
36041     if (gc_heap::alloc_wait_event_p)
36042     {
36043         hpt->fire_alloc_wait_event_end (awr_fgc_wait_for_bgc);
36044         gc_heap::alloc_wait_event_p = FALSE;
36045     }
36046 #endif //BACKGROUND_GC
36047
36048 #ifndef MULTIPLE_HEAPS
36049 #ifdef BACKGROUND_GC
36050     if (!gc_heap::dont_restart_ee_p)
36051     {
36052 #endif //BACKGROUND_GC
36053         BEGIN_TIMING(restart_ee_during_log);
36054         GCToEEInterface::RestartEE(TRUE);
36055         END_TIMING(restart_ee_during_log);
36056 #ifdef BACKGROUND_GC
36057     }
36058 #endif //BACKGROUND_GC
36059 #endif //!MULTIPLE_HEAPS
36060
36061 #ifdef COUNT_CYCLES
36062     printf ("GC: %d Time: %d\n", GcCondemnedGeneration,
36063             GetCycleCount32() - gc_start);
36064 #endif //COUNT_CYCLES
36065
36066 #ifndef MULTIPLE_HEAPS
36067     process_sync_log_stats();
36068     gc_heap::gc_started = FALSE;
36069     gc_heap::set_gc_done();
36070     dprintf (SPINLOCK_LOG, ("GC Lgc"));
36071     leave_spin_lock (&gc_heap::gc_lock);    
36072 #endif //!MULTIPLE_HEAPS
36073
36074 #ifdef FEATURE_PREMORTEM_FINALIZATION
36075     GCToEEInterface::EnableFinalization(!pGenGCHeap->settings.concurrent && pGenGCHeap->settings.found_finalizers);
36076 #endif // FEATURE_PREMORTEM_FINALIZATION
36077
36078     return dd_collection_count (dd);
36079 }
36080
36081 size_t      GCHeap::GetTotalBytesInUse ()
36082 {
36083 #ifdef MULTIPLE_HEAPS
36084     //enumarate all the heaps and get their size.
36085     size_t tot_size = 0;
36086     for (int i = 0; i < gc_heap::n_heaps; i++)
36087     {
36088         GCHeap* Hp = gc_heap::g_heaps [i]->vm_heap;
36089         tot_size += Hp->ApproxTotalBytesInUse (FALSE);
36090     }
36091     return tot_size;
36092 #else
36093     return ApproxTotalBytesInUse ();
36094 #endif //MULTIPLE_HEAPS
36095 }
36096
36097 int GCHeap::CollectionCount (int generation, int get_bgc_fgc_count)
36098 {
36099     if (get_bgc_fgc_count != 0)
36100     {
36101 #ifdef BACKGROUND_GC
36102         if (generation == max_generation)
36103         {
36104             return (int)(gc_heap::full_gc_counts[gc_type_background]);
36105         }
36106         else
36107         {
36108             return (int)(gc_heap::ephemeral_fgc_counts[generation]);
36109         }
36110 #else
36111         return 0;
36112 #endif //BACKGROUND_GC
36113     }
36114
36115 #ifdef MULTIPLE_HEAPS
36116     gc_heap* hp = gc_heap::g_heaps [0];
36117 #else  //MULTIPLE_HEAPS
36118     gc_heap* hp = pGenGCHeap;
36119 #endif //MULTIPLE_HEAPS
36120     if (generation > max_generation)
36121         return 0;
36122     else
36123         return (int)dd_collection_count (hp->dynamic_data_of (generation));
36124 }
36125
36126 size_t GCHeap::ApproxTotalBytesInUse(BOOL small_heap_only)
36127 {
36128     size_t totsize = 0;
36129     //GCTODO
36130     //ASSERT(InMustComplete());
36131     enter_spin_lock (&pGenGCHeap->gc_lock);
36132
36133     heap_segment* eph_seg = generation_allocation_segment (pGenGCHeap->generation_of (0));
36134     // Get small block heap size info
36135     totsize = (pGenGCHeap->alloc_allocated - heap_segment_mem (eph_seg));
36136     heap_segment* seg1 = generation_start_segment (pGenGCHeap->generation_of (max_generation));
36137     while (seg1 != eph_seg)
36138     {
36139         totsize += heap_segment_allocated (seg1) -
36140             heap_segment_mem (seg1);
36141         seg1 = heap_segment_next (seg1);
36142     }
36143
36144     //discount the fragmentation
36145     for (int i = 0; i <= max_generation; i++)
36146     {
36147         generation* gen = pGenGCHeap->generation_of (i);
36148         totsize -= (generation_free_list_space (gen) + generation_free_obj_space (gen));
36149     }
36150
36151     if (!small_heap_only)
36152     {
36153         heap_segment* seg2 = generation_start_segment (pGenGCHeap->generation_of (max_generation+1));
36154
36155         while (seg2 != 0)
36156         {
36157             totsize += heap_segment_allocated (seg2) -
36158                 heap_segment_mem (seg2);
36159             seg2 = heap_segment_next (seg2);
36160         }
36161
36162         //discount the fragmentation
36163         generation* loh_gen = pGenGCHeap->generation_of (max_generation+1);
36164         size_t frag = generation_free_list_space (loh_gen) + generation_free_obj_space (loh_gen);
36165         totsize -= frag;
36166     }
36167     leave_spin_lock (&pGenGCHeap->gc_lock);
36168     return totsize;
36169 }
36170
36171 #ifdef MULTIPLE_HEAPS
36172 void GCHeap::AssignHeap (alloc_context* acontext)
36173 {
36174     // Assign heap based on processor
36175     acontext->set_alloc_heap(GetHeap(heap_select::select_heap(acontext, 0)));
36176     acontext->set_home_heap(acontext->get_alloc_heap());
36177 }
36178 GCHeap* GCHeap::GetHeap (int n)
36179 {
36180     assert (n < gc_heap::n_heaps);
36181     return gc_heap::g_heaps [n]->vm_heap;
36182 }
36183 #endif //MULTIPLE_HEAPS
36184
36185 bool GCHeap::IsThreadUsingAllocationContextHeap(gc_alloc_context* context, int thread_number)
36186 {
36187     alloc_context* acontext = static_cast<alloc_context*>(context);
36188 #ifdef MULTIPLE_HEAPS
36189     return ((acontext->get_home_heap() == GetHeap(thread_number)) ||
36190             ((acontext->get_home_heap() == 0) && (thread_number == 0)));
36191 #else
36192     UNREFERENCED_PARAMETER(acontext);
36193     UNREFERENCED_PARAMETER(thread_number);
36194     return true;
36195 #endif //MULTIPLE_HEAPS
36196 }
36197
36198 // Returns the number of processors required to trigger the use of thread based allocation contexts
36199 int GCHeap::GetNumberOfHeaps ()
36200 {
36201 #ifdef MULTIPLE_HEAPS
36202     return gc_heap::n_heaps;
36203 #else
36204     return 1;
36205 #endif //MULTIPLE_HEAPS
36206 }
36207
36208 /*
36209   in this way we spend extra time cycling through all the heaps while create the handle
36210   it ought to be changed by keeping alloc_context.home_heap as number (equals heap_number)
36211 */
36212 int GCHeap::GetHomeHeapNumber ()
36213 {
36214 #ifdef MULTIPLE_HEAPS
36215     gc_alloc_context* ctx = GCToEEInterface::GetAllocContext();
36216     if (!ctx)
36217     {
36218         return 0;
36219     }
36220
36221     GCHeap *hp = static_cast<alloc_context*>(ctx)->get_home_heap();
36222     return (hp ? hp->pGenGCHeap->heap_number : 0);
36223 #else
36224     return 0;
36225 #endif //MULTIPLE_HEAPS
36226 }
36227
36228 unsigned int GCHeap::GetCondemnedGeneration()
36229
36230     return gc_heap::settings.condemned_generation;
36231 }
36232
36233 void GCHeap::GetMemoryInfo(uint32_t* highMemLoadThreshold, 
36234                            uint64_t* totalPhysicalMem, 
36235                            uint32_t* lastRecordedMemLoad,
36236                            size_t* lastRecordedHeapSize,
36237                            size_t* lastRecordedFragmentation)
36238 {
36239     *highMemLoadThreshold = gc_heap::high_memory_load_th;
36240     *totalPhysicalMem = gc_heap::total_physical_mem;
36241     *lastRecordedMemLoad = gc_heap::last_gc_memory_load;
36242     *lastRecordedHeapSize = gc_heap::last_gc_heap_size;
36243     *lastRecordedFragmentation = gc_heap::last_gc_fragmentation;
36244 }
36245
36246 int GCHeap::GetGcLatencyMode()
36247 {
36248     return (int)(pGenGCHeap->settings.pause_mode);
36249 }
36250
36251 int GCHeap::SetGcLatencyMode (int newLatencyMode)
36252 {
36253     if (gc_heap::settings.pause_mode == pause_no_gc)
36254         return (int)set_pause_mode_no_gc;
36255
36256     gc_pause_mode new_mode = (gc_pause_mode)newLatencyMode;
36257
36258     if (new_mode == pause_low_latency)
36259     {
36260 #ifndef MULTIPLE_HEAPS
36261         pGenGCHeap->settings.pause_mode = new_mode;
36262 #endif //!MULTIPLE_HEAPS
36263     }
36264     else if (new_mode == pause_sustained_low_latency)
36265     {
36266 #ifdef BACKGROUND_GC
36267         if (gc_heap::gc_can_use_concurrent)
36268         {
36269             pGenGCHeap->settings.pause_mode = new_mode;
36270         }
36271 #endif //BACKGROUND_GC
36272     }
36273     else
36274     {
36275         pGenGCHeap->settings.pause_mode = new_mode;
36276     }
36277
36278 #ifdef BACKGROUND_GC
36279     if (recursive_gc_sync::background_running_p())
36280     {
36281         // If we get here, it means we are doing an FGC. If the pause
36282         // mode was altered we will need to save it in the BGC settings.
36283         if (gc_heap::saved_bgc_settings.pause_mode != new_mode)
36284         {
36285             gc_heap::saved_bgc_settings.pause_mode = new_mode;
36286         }
36287     }
36288 #endif //BACKGROUND_GC
36289
36290     return (int)set_pause_mode_success;
36291 }
36292
36293 int GCHeap::GetLOHCompactionMode()
36294 {
36295     return pGenGCHeap->loh_compaction_mode;
36296 }
36297
36298 void GCHeap::SetLOHCompactionMode (int newLOHCompactionyMode)
36299 {
36300 #ifdef FEATURE_LOH_COMPACTION
36301     pGenGCHeap->loh_compaction_mode = (gc_loh_compaction_mode)newLOHCompactionyMode;
36302 #endif //FEATURE_LOH_COMPACTION
36303 }
36304
36305 bool GCHeap::RegisterForFullGCNotification(uint32_t gen2Percentage,
36306                                            uint32_t lohPercentage)
36307 {
36308 #ifdef MULTIPLE_HEAPS
36309     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36310     {
36311         gc_heap* hp = gc_heap::g_heaps [hn];
36312         hp->fgn_last_alloc = dd_new_allocation (hp->dynamic_data_of (0));
36313     }
36314 #else //MULTIPLE_HEAPS
36315     pGenGCHeap->fgn_last_alloc = dd_new_allocation (pGenGCHeap->dynamic_data_of (0));
36316 #endif //MULTIPLE_HEAPS
36317
36318     pGenGCHeap->full_gc_approach_event.Reset();
36319     pGenGCHeap->full_gc_end_event.Reset();
36320     pGenGCHeap->full_gc_approach_event_set = false;
36321
36322     pGenGCHeap->fgn_maxgen_percent = gen2Percentage;
36323     pGenGCHeap->fgn_loh_percent = lohPercentage;
36324
36325     return TRUE;
36326 }
36327
36328 bool GCHeap::CancelFullGCNotification()
36329 {
36330     pGenGCHeap->fgn_maxgen_percent = 0;
36331     pGenGCHeap->fgn_loh_percent = 0;
36332
36333     pGenGCHeap->full_gc_approach_event.Set();
36334     pGenGCHeap->full_gc_end_event.Set();
36335     
36336     return TRUE;
36337 }
36338
36339 int GCHeap::WaitForFullGCApproach(int millisecondsTimeout)
36340 {
36341     dprintf (2, ("WFGA: Begin wait"));
36342     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_approach_event), millisecondsTimeout);
36343     dprintf (2, ("WFGA: End wait"));
36344     return result;
36345 }
36346
36347 int GCHeap::WaitForFullGCComplete(int millisecondsTimeout)
36348 {
36349     dprintf (2, ("WFGE: Begin wait"));
36350     int result = gc_heap::full_gc_wait (&(pGenGCHeap->full_gc_end_event), millisecondsTimeout);
36351     dprintf (2, ("WFGE: End wait"));
36352     return result;
36353 }
36354
36355 int GCHeap::StartNoGCRegion(uint64_t totalSize, bool lohSizeKnown, uint64_t lohSize, bool disallowFullBlockingGC)
36356 {
36357     NoGCRegionLockHolder lh;
36358
36359     dprintf (1, ("begin no gc called"));
36360     start_no_gc_region_status status = gc_heap::prepare_for_no_gc_region (totalSize, lohSizeKnown, lohSize, disallowFullBlockingGC);
36361     if (status == start_no_gc_success)
36362     {
36363         GarbageCollect (max_generation);
36364         status = gc_heap::get_start_no_gc_region_status();
36365     }
36366
36367     if (status != start_no_gc_success)
36368         gc_heap::handle_failure_for_no_gc();
36369
36370     return (int)status;
36371 }
36372
36373 int GCHeap::EndNoGCRegion()
36374 {
36375     NoGCRegionLockHolder lh;
36376     return (int)gc_heap::end_no_gc_region();
36377 }
36378
36379 void GCHeap::PublishObject (uint8_t* Obj)
36380 {
36381 #ifdef BACKGROUND_GC
36382     gc_heap* hp = gc_heap::heap_of (Obj);
36383     hp->bgc_alloc_lock->loh_alloc_done (Obj);
36384     hp->bgc_untrack_loh_alloc();
36385 #endif //BACKGROUND_GC
36386 }
36387
36388 // The spec for this one isn't clear. This function
36389 // returns the size that can be allocated without
36390 // triggering a GC of any kind.
36391 size_t GCHeap::ApproxFreeBytes()
36392 {
36393     //GCTODO
36394     //ASSERT(InMustComplete());
36395     enter_spin_lock (&pGenGCHeap->gc_lock);
36396
36397     generation* gen = pGenGCHeap->generation_of (0);
36398     size_t res = generation_allocation_limit (gen) - generation_allocation_pointer (gen);
36399
36400     leave_spin_lock (&pGenGCHeap->gc_lock);
36401
36402     return res;
36403 }
36404
36405 HRESULT GCHeap::GetGcCounters(int gen, gc_counters* counters)
36406 {
36407     if ((gen < 0) || (gen > max_generation))
36408         return E_FAIL;
36409 #ifdef MULTIPLE_HEAPS
36410     counters->current_size = 0;
36411     counters->promoted_size = 0;
36412     counters->collection_count = 0;
36413
36414     //enumarate all the heaps and get their counters.
36415     for (int i = 0; i < gc_heap::n_heaps; i++)
36416     {
36417         dynamic_data* dd = gc_heap::g_heaps [i]->dynamic_data_of (gen);
36418
36419         counters->current_size += dd_current_size (dd);
36420         counters->promoted_size += dd_promoted_size (dd);
36421         if (i == 0)
36422         counters->collection_count += dd_collection_count (dd);
36423     }
36424 #else
36425     dynamic_data* dd = pGenGCHeap->dynamic_data_of (gen);
36426     counters->current_size = dd_current_size (dd);
36427     counters->promoted_size = dd_promoted_size (dd);
36428     counters->collection_count = dd_collection_count (dd);
36429 #endif //MULTIPLE_HEAPS
36430     return S_OK;
36431 }
36432
36433 // Get the segment size to use, making sure it conforms.
36434 size_t GCHeap::GetValidSegmentSize(bool large_seg)
36435 {
36436     return (large_seg ? gc_heap::min_loh_segment_size : gc_heap::soh_segment_size);
36437 }
36438
36439 // Get the max gen0 heap size, making sure it conforms.
36440 size_t gc_heap::get_gen0_min_size()
36441 {
36442     size_t gen0size = static_cast<size_t>(GCConfig::GetGen0Size());
36443     bool is_config_invalid = ((gen0size == 0) || !g_theGCHeap->IsValidGen0MaxSize(gen0size));
36444     if (is_config_invalid)
36445     {
36446 #ifdef SERVER_GC
36447         // performance data seems to indicate halving the size results
36448         // in optimal perf.  Ask for adjusted gen0 size.
36449         gen0size = max(GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),(256*1024));
36450
36451         // if gen0 size is too large given the available memory, reduce it.
36452         // Get true cache size, as we don't want to reduce below this.
36453         size_t trueSize = max(GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE),(256*1024));
36454         dprintf (1, ("cache: %Id-%Id", 
36455             GCToOSInterface::GetCacheSizePerLogicalCpu(FALSE),
36456             GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE)));
36457
36458         int n_heaps = gc_heap::n_heaps;
36459 #else //SERVER_GC
36460         size_t trueSize = GCToOSInterface::GetCacheSizePerLogicalCpu(TRUE);
36461         gen0size = max((4*trueSize/5),(256*1024));
36462         trueSize = max(trueSize, (256*1024));
36463         int n_heaps = 1;
36464 #endif //SERVER_GC
36465
36466         dprintf (1, ("gen0size: %Id * %d = %Id, physical mem: %Id / 6 = %Id",
36467                 gen0size, n_heaps, (gen0size * n_heaps), 
36468                 gc_heap::total_physical_mem,
36469                 gc_heap::total_physical_mem / 6));
36470
36471         // if the total min GC across heaps will exceed 1/6th of available memory,
36472         // then reduce the min GC size until it either fits or has been reduced to cache size.
36473         while ((gen0size * n_heaps) > (gc_heap::total_physical_mem / 6))
36474         {
36475             gen0size = gen0size / 2;
36476             if (gen0size <= trueSize)
36477             {
36478                 gen0size = trueSize;
36479                 break;
36480             }
36481         }
36482     }
36483
36484     size_t seg_size = gc_heap::soh_segment_size;
36485     assert (seg_size);
36486
36487     // Generation 0 must never be more than 1/2 the segment size.
36488     if (gen0size >= (seg_size / 2))
36489         gen0size = seg_size / 2;
36490
36491     // If the value from config is valid we use it as is without this adjustment.
36492     if (is_config_invalid)
36493     {
36494         if (heap_hard_limit)
36495         {
36496             size_t gen0size_seg = seg_size / 8;
36497             if (gen0size >= gen0size_seg)
36498             {
36499                 dprintf (1, ("gen0 limited by seg size %Id->%Id", gen0size, gen0size_seg));
36500                 gen0size = gen0size_seg;
36501             }
36502         }
36503
36504         gen0size = gen0size / 8 * 5;
36505     }
36506
36507     gen0size = Align (gen0size);
36508
36509     return gen0size;
36510 }
36511
36512 void GCHeap::SetReservedVMLimit (size_t vmlimit)
36513 {
36514     gc_heap::reserved_memory_limit = vmlimit;
36515 }
36516
36517 //versions of same method on each heap
36518
36519 #ifdef FEATURE_PREMORTEM_FINALIZATION
36520
36521 Object* GCHeap::GetNextFinalizableObject()
36522 {
36523
36524 #ifdef MULTIPLE_HEAPS
36525
36526     //return the first non critical one in the first queue.
36527     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36528     {
36529         gc_heap* hp = gc_heap::g_heaps [hn];
36530         Object* O = hp->finalize_queue->GetNextFinalizableObject(TRUE);
36531         if (O)
36532             return O;
36533     }
36534     //return the first non crtitical/critical one in the first queue.
36535     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36536     {
36537         gc_heap* hp = gc_heap::g_heaps [hn];
36538         Object* O = hp->finalize_queue->GetNextFinalizableObject(FALSE);
36539         if (O)
36540             return O;
36541     }
36542     return 0;
36543
36544
36545 #else //MULTIPLE_HEAPS
36546     return pGenGCHeap->finalize_queue->GetNextFinalizableObject();
36547 #endif //MULTIPLE_HEAPS
36548
36549 }
36550
36551 size_t GCHeap::GetNumberFinalizableObjects()
36552 {
36553 #ifdef MULTIPLE_HEAPS
36554     size_t cnt = 0;
36555     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36556     {
36557         gc_heap* hp = gc_heap::g_heaps [hn];
36558         cnt += hp->finalize_queue->GetNumberFinalizableObjects();
36559     }
36560     return cnt;
36561
36562
36563 #else //MULTIPLE_HEAPS
36564     return pGenGCHeap->finalize_queue->GetNumberFinalizableObjects();
36565 #endif //MULTIPLE_HEAPS
36566 }
36567
36568 size_t GCHeap::GetFinalizablePromotedCount()
36569 {
36570 #ifdef MULTIPLE_HEAPS
36571     size_t cnt = 0;
36572
36573     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36574     {
36575         gc_heap* hp = gc_heap::g_heaps [hn];
36576         cnt += hp->finalize_queue->GetPromotedCount();
36577     }
36578     return cnt;
36579
36580 #else //MULTIPLE_HEAPS
36581     return pGenGCHeap->finalize_queue->GetPromotedCount();
36582 #endif //MULTIPLE_HEAPS
36583 }
36584
36585 bool GCHeap::FinalizeAppDomain(void *pDomain, bool fRunFinalizers)
36586 {
36587 #ifdef MULTIPLE_HEAPS
36588     bool foundp = false;
36589     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36590     {
36591         gc_heap* hp = gc_heap::g_heaps [hn];
36592         if (hp->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers))
36593             foundp = true;
36594     }
36595     return foundp;
36596
36597 #else //MULTIPLE_HEAPS
36598     return pGenGCHeap->finalize_queue->FinalizeAppDomain (pDomain, fRunFinalizers);
36599 #endif //MULTIPLE_HEAPS
36600 }
36601
36602 bool GCHeap::ShouldRestartFinalizerWatchDog()
36603 {
36604     // This condition was historically used as part of the condition to detect finalizer thread timeouts
36605     return gc_heap::gc_lock.lock != -1;
36606 }
36607
36608 void GCHeap::SetFinalizeQueueForShutdown(bool fHasLock)
36609 {
36610 #ifdef MULTIPLE_HEAPS
36611     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
36612     {
36613         gc_heap* hp = gc_heap::g_heaps [hn];
36614         hp->finalize_queue->SetSegForShutDown(fHasLock);
36615     }
36616
36617 #else //MULTIPLE_HEAPS
36618     pGenGCHeap->finalize_queue->SetSegForShutDown(fHasLock);
36619 #endif //MULTIPLE_HEAPS
36620 }
36621
36622 //---------------------------------------------------------------------------
36623 // Finalized class tracking
36624 //---------------------------------------------------------------------------
36625
36626 bool GCHeap::RegisterForFinalization (int gen, Object* obj)
36627 {
36628     if (gen == -1)
36629         gen = 0;
36630     if (((((CObjectHeader*)obj)->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN))
36631     {
36632         //just reset the bit
36633         ((CObjectHeader*)obj)->GetHeader()->ClrBit(BIT_SBLK_FINALIZER_RUN);
36634         return true;
36635     }
36636     else
36637     {
36638         gc_heap* hp = gc_heap::heap_of ((uint8_t*)obj);
36639         return hp->finalize_queue->RegisterForFinalization (gen, obj);
36640     }
36641 }
36642
36643 void GCHeap::SetFinalizationRun (Object* obj)
36644 {
36645     ((CObjectHeader*)obj)->GetHeader()->SetBit(BIT_SBLK_FINALIZER_RUN);
36646 }
36647
36648
36649 //--------------------------------------------------------------------
36650 //
36651 //          Support for finalization
36652 //
36653 //--------------------------------------------------------------------
36654
36655 inline
36656 unsigned int gen_segment (int gen)
36657 {
36658     assert (((signed)NUMBERGENERATIONS - gen - 1)>=0);
36659     return (NUMBERGENERATIONS - gen - 1);
36660 }
36661
36662 bool CFinalize::Initialize()
36663 {
36664     CONTRACTL {
36665         NOTHROW;
36666         GC_NOTRIGGER;
36667     } CONTRACTL_END;
36668
36669     m_Array = new (nothrow)(Object*[100]);
36670
36671     if (!m_Array)
36672     {
36673         ASSERT (m_Array);
36674         STRESS_LOG_OOM_STACK(sizeof(Object*[100]));
36675         if (GCConfig::GetBreakOnOOM())
36676         {
36677             GCToOSInterface::DebugBreak();
36678         }
36679         return false;
36680     }
36681     m_EndArray = &m_Array[100];
36682
36683     for (int i =0; i < FreeList; i++)
36684     {
36685         SegQueueLimit (i) = m_Array;
36686     }
36687     m_PromotedCount = 0;
36688     lock = -1;
36689 #ifdef _DEBUG
36690     lockowner_threadid.Clear();
36691 #endif // _DEBUG
36692
36693     return true;
36694 }
36695
36696 CFinalize::~CFinalize()
36697 {
36698     delete m_Array;
36699 }
36700
36701 size_t CFinalize::GetPromotedCount ()
36702 {
36703     return m_PromotedCount;
36704 }
36705
36706 inline
36707 void CFinalize::EnterFinalizeLock()
36708 {
36709     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
36710              GCToEEInterface::GetThread() == 0 ||
36711              GCToEEInterface::IsPreemptiveGCDisabled());
36712
36713 retry:
36714     if (Interlocked::CompareExchange(&lock, 0, -1) >= 0)
36715     {
36716         unsigned int i = 0;
36717         while (lock >= 0)
36718         {
36719             YieldProcessor();           // indicate to the processor that we are spinning
36720             if (++i & 7)
36721                 GCToOSInterface::YieldThread (0);
36722             else
36723                 GCToOSInterface::Sleep (5);
36724         }
36725         goto retry;
36726     }
36727
36728 #ifdef _DEBUG
36729     lockowner_threadid.SetToCurrentThread();
36730 #endif // _DEBUG
36731 }
36732
36733 inline
36734 void CFinalize::LeaveFinalizeLock()
36735 {
36736     _ASSERTE(dbgOnly_IsSpecialEEThread() ||
36737              GCToEEInterface::GetThread() == 0 ||
36738              GCToEEInterface::IsPreemptiveGCDisabled());
36739
36740 #ifdef _DEBUG
36741     lockowner_threadid.Clear();
36742 #endif // _DEBUG
36743     lock = -1;
36744 }
36745
36746 bool
36747 CFinalize::RegisterForFinalization (int gen, Object* obj, size_t size)
36748 {
36749     CONTRACTL {
36750         NOTHROW;
36751         GC_NOTRIGGER;
36752     } CONTRACTL_END;
36753
36754     EnterFinalizeLock();
36755     // Adjust gen
36756     unsigned int dest = 0;
36757
36758     if (g_fFinalizerRunOnShutDown)
36759     {
36760         //no method table available yet,
36761         //put it in the finalizer queue and sort out when
36762         //dequeueing
36763         dest = FinalizerListSeg;
36764     }
36765
36766     else
36767         dest = gen_segment (gen);
36768
36769     // Adjust boundary for segments so that GC will keep objects alive.
36770     Object*** s_i = &SegQueue (FreeList);
36771     if ((*s_i) == m_EndArray)
36772     {
36773         if (!GrowArray())
36774         {
36775             LeaveFinalizeLock();
36776             if (method_table(obj) == NULL)
36777             {
36778                 // If the object is uninitialized, a valid size should have been passed.
36779                 assert (size >= Align (min_obj_size));
36780                 dprintf (3, ("Making unused array [%Ix, %Ix[", (size_t)obj, (size_t)(obj+size)));
36781                 ((CObjectHeader*)obj)->SetFree(size);
36782             }
36783             STRESS_LOG_OOM_STACK(0);
36784             if (GCConfig::GetBreakOnOOM())
36785             {
36786                 GCToOSInterface::DebugBreak();
36787             }
36788             return false;
36789         }
36790     }
36791     Object*** end_si = &SegQueueLimit (dest);
36792     do
36793     {
36794         //is the segment empty?
36795         if (!(*s_i == *(s_i-1)))
36796         {
36797             //no, swap the end elements.
36798             *(*s_i) = *(*(s_i-1));
36799         }
36800         //increment the fill pointer
36801         (*s_i)++;
36802         //go to the next segment.
36803         s_i--;
36804     } while (s_i > end_si);
36805
36806     // We have reached the destination segment
36807     // store the object
36808     **s_i = obj;
36809     // increment the fill pointer
36810     (*s_i)++;
36811
36812     LeaveFinalizeLock();
36813
36814     return true;
36815 }
36816
36817 Object*
36818 CFinalize::GetNextFinalizableObject (BOOL only_non_critical)
36819 {
36820     Object* obj = 0;
36821     //serialize
36822     EnterFinalizeLock();
36823
36824 retry:
36825     if (!IsSegEmpty(FinalizerListSeg))
36826     {
36827         if (g_fFinalizerRunOnShutDown)
36828         {
36829             obj = *(SegQueueLimit (FinalizerListSeg)-1);
36830             if (method_table(obj)->HasCriticalFinalizer())
36831             {
36832                 MoveItem ((SegQueueLimit (FinalizerListSeg)-1),
36833                           FinalizerListSeg, CriticalFinalizerListSeg);
36834                 goto retry;
36835             }
36836             else
36837                 --SegQueueLimit (FinalizerListSeg);
36838         }
36839         else
36840             obj =  *(--SegQueueLimit (FinalizerListSeg));
36841
36842     }
36843     else if (!only_non_critical && !IsSegEmpty(CriticalFinalizerListSeg))
36844     {
36845         //the FinalizerList is empty, we can adjust both
36846         // limit instead of moving the object to the free list
36847         obj =  *(--SegQueueLimit (CriticalFinalizerListSeg));
36848         --SegQueueLimit (FinalizerListSeg);
36849     }
36850     if (obj)
36851     {
36852         dprintf (3, ("running finalizer for %Ix (mt: %Ix)", obj, method_table (obj)));
36853     }
36854     LeaveFinalizeLock();
36855     return obj;
36856 }
36857
36858 void
36859 CFinalize::SetSegForShutDown(BOOL fHasLock)
36860 {
36861     int i;
36862
36863     if (!fHasLock)
36864         EnterFinalizeLock();
36865     for (i = 0; i <= max_generation; i++)
36866     {
36867         unsigned int seg = gen_segment (i);
36868         Object** startIndex = SegQueueLimit (seg)-1;
36869         Object** stopIndex  = SegQueue (seg);
36870         for (Object** po = startIndex; po >= stopIndex; po--)
36871         {
36872             Object* obj = *po;
36873             if (method_table(obj)->HasCriticalFinalizer())
36874             {
36875                 MoveItem (po, seg, CriticalFinalizerListSeg);
36876             }
36877             else
36878             {
36879                 MoveItem (po, seg, FinalizerListSeg);
36880             }
36881         }
36882     }
36883     if (!fHasLock)
36884         LeaveFinalizeLock();
36885 }
36886
36887 void
36888 CFinalize::DiscardNonCriticalObjects()
36889 {
36890     //empty the finalization queue
36891     Object** startIndex = SegQueueLimit (FinalizerListSeg)-1;
36892     Object** stopIndex  = SegQueue (FinalizerListSeg);
36893     for (Object** po = startIndex; po >= stopIndex; po--)
36894     {
36895         MoveItem (po, FinalizerListSeg, FreeList);
36896     }
36897 }
36898
36899 size_t
36900 CFinalize::GetNumberFinalizableObjects()
36901 {
36902     return SegQueueLimit (FinalizerListSeg) -
36903         (g_fFinalizerRunOnShutDown ? m_Array : SegQueue(FinalizerListSeg));
36904 }
36905
36906 BOOL
36907 CFinalize::FinalizeSegForAppDomain (void *pDomain, 
36908                                     BOOL fRunFinalizers, 
36909                                     unsigned int Seg)
36910 {
36911     BOOL finalizedFound = FALSE;
36912     Object** endIndex = SegQueue (Seg);
36913     for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
36914     {
36915         CObjectHeader* obj = (CObjectHeader*)*i;
36916
36917         // Objects are put into the finalization queue before they are complete (ie their methodtable
36918         // may be null) so we must check that the object we found has a method table before checking
36919         // if it has the index we are looking for. If the methodtable is null, it can't be from the
36920         // unloading domain, so skip it.
36921         if (method_table(obj) == NULL)
36922         {
36923             continue;
36924         }
36925
36926         // does the EE actually want us to finalize this object?
36927         if (!GCToEEInterface::ShouldFinalizeObjectForUnload(pDomain, obj))
36928         {
36929             continue;
36930         }
36931
36932         if (!fRunFinalizers || (obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
36933         {
36934             //remove the object because we don't want to
36935             //run the finalizer
36936             MoveItem (i, Seg, FreeList);
36937             //Reset the bit so it will be put back on the queue
36938             //if resurrected and re-registered.
36939             obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
36940         }
36941         else
36942         {
36943             if (method_table(obj)->HasCriticalFinalizer())
36944             {
36945                 finalizedFound = TRUE;
36946                 MoveItem (i, Seg, CriticalFinalizerListSeg);
36947             }
36948             else
36949             {
36950                 if (GCToEEInterface::AppDomainIsRudeUnload(pDomain))
36951                 {
36952                     MoveItem (i, Seg, FreeList);
36953                 }
36954                 else
36955                 {
36956                     finalizedFound = TRUE;
36957                     MoveItem (i, Seg, FinalizerListSeg);
36958                 }
36959             }
36960         }
36961     }
36962
36963     return finalizedFound;
36964 }
36965
36966 bool
36967 CFinalize::FinalizeAppDomain (void *pDomain, bool fRunFinalizers)
36968 {
36969     bool finalizedFound = false;
36970
36971     unsigned int startSeg = gen_segment (max_generation);
36972
36973     EnterFinalizeLock();
36974
36975     for (unsigned int Seg = startSeg; Seg <= gen_segment (0); Seg++)
36976     {
36977         if (FinalizeSegForAppDomain (pDomain, fRunFinalizers, Seg))
36978         {
36979             finalizedFound = true;
36980         }
36981     }
36982
36983     LeaveFinalizeLock();
36984
36985     return finalizedFound;
36986 }
36987
36988 void
36989 CFinalize::MoveItem (Object** fromIndex,
36990                      unsigned int fromSeg,
36991                      unsigned int toSeg)
36992 {
36993
36994     int step;
36995     ASSERT (fromSeg != toSeg);
36996     if (fromSeg > toSeg)
36997         step = -1;
36998     else
36999         step = +1;
37000     // Place the element at the boundary closest to dest
37001     Object** srcIndex = fromIndex;
37002     for (unsigned int i = fromSeg; i != toSeg; i+= step)
37003     {
37004         Object**& destFill = m_FillPointers[i+(step - 1 )/2];
37005         Object** destIndex = destFill - (step + 1)/2;
37006         if (srcIndex != destIndex)
37007         {
37008             Object* tmp = *srcIndex;
37009             *srcIndex = *destIndex;
37010             *destIndex = tmp;
37011         }
37012         destFill -= step;
37013         srcIndex = destIndex;
37014     }
37015 }
37016
37017 void
37018 CFinalize::GcScanRoots (promote_func* fn, int hn, ScanContext *pSC)
37019 {
37020     ScanContext sc;
37021     if (pSC == 0)
37022         pSC = &sc;
37023
37024     pSC->thread_number = hn;
37025
37026     //scan the finalization queue
37027     Object** startIndex  = SegQueue (CriticalFinalizerListSeg);
37028     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
37029
37030     for (Object** po = startIndex; po < stopIndex; po++)
37031     {
37032         Object* o = *po;
37033         //dprintf (3, ("scan freacheable %Ix", (size_t)o));
37034         dprintf (3, ("scan f %Ix", (size_t)o));
37035 #ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING
37036         if (g_fEnableAppDomainMonitoring)
37037         {
37038             pSC->pCurrentDomain = GCToEEInterface::GetAppDomainForObject(o);
37039         }
37040 #endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
37041
37042         (*fn)(po, pSC, 0);
37043     }
37044 }
37045
37046 void CFinalize::WalkFReachableObjects (fq_walk_fn fn)
37047 {
37048     Object** startIndex = SegQueue (CriticalFinalizerListSeg);
37049     Object** stopCriticalIndex = SegQueueLimit (CriticalFinalizerListSeg);
37050     Object** stopIndex  = SegQueueLimit (FinalizerListSeg);
37051     for (Object** po = startIndex; po < stopIndex; po++)
37052     {
37053         //report *po
37054         fn(po < stopCriticalIndex, *po);
37055     }
37056 }
37057
37058 BOOL
37059 CFinalize::ScanForFinalization (promote_func* pfn, int gen, BOOL mark_only_p,
37060                                 gc_heap* hp)
37061 {
37062     ScanContext sc;
37063     sc.promotion = TRUE;
37064 #ifdef MULTIPLE_HEAPS
37065     sc.thread_number = hp->heap_number;
37066 #else
37067     UNREFERENCED_PARAMETER(hp);
37068 #endif //MULTIPLE_HEAPS
37069
37070     BOOL finalizedFound = FALSE;
37071
37072     //start with gen and explore all the younger generations.
37073     unsigned int startSeg = gen_segment (gen);
37074     {
37075         m_PromotedCount = 0;
37076         for (unsigned int Seg = startSeg; Seg <= gen_segment(0); Seg++)
37077         {
37078             Object** endIndex = SegQueue (Seg);
37079             for (Object** i = SegQueueLimit (Seg)-1; i >= endIndex ;i--)
37080             {
37081                 CObjectHeader* obj = (CObjectHeader*)*i;
37082                 dprintf (3, ("scanning: %Ix", (size_t)obj));
37083                 if (!g_theGCHeap->IsPromoted (obj))
37084                 {
37085                     dprintf (3, ("freacheable: %Ix", (size_t)obj));
37086
37087                     assert (method_table(obj)->HasFinalizer());
37088
37089                     if (GCToEEInterface::EagerFinalized(obj))
37090                     {
37091                         MoveItem (i, Seg, FreeList);
37092                     }
37093                     else if ((obj->GetHeader()->GetBits()) & BIT_SBLK_FINALIZER_RUN)
37094                     {
37095                         //remove the object because we don't want to
37096                         //run the finalizer
37097
37098                         MoveItem (i, Seg, FreeList);
37099
37100                         //Reset the bit so it will be put back on the queue
37101                         //if resurrected and re-registered.
37102                         obj->GetHeader()->ClrBit (BIT_SBLK_FINALIZER_RUN);
37103
37104                     }
37105                     else
37106                     {
37107                         m_PromotedCount++;
37108
37109                         if (method_table(obj)->HasCriticalFinalizer())
37110                         {
37111                             MoveItem (i, Seg, CriticalFinalizerListSeg);
37112                         }
37113                         else
37114                         {
37115                             MoveItem (i, Seg, FinalizerListSeg);
37116                         }
37117                     }
37118                 }
37119 #ifdef BACKGROUND_GC
37120                 else
37121                 {
37122                     if ((gen == max_generation) && (recursive_gc_sync::background_running_p()))
37123                     {
37124                         // TODO - fix the following line.
37125                         //assert (gc_heap::background_object_marked ((uint8_t*)obj, FALSE));
37126                         dprintf (3, ("%Ix is marked", (size_t)obj));
37127                     }
37128                 }
37129 #endif //BACKGROUND_GC
37130             }
37131         }
37132     }
37133     finalizedFound = !IsSegEmpty(FinalizerListSeg) ||
37134                      !IsSegEmpty(CriticalFinalizerListSeg);
37135                     
37136     if (finalizedFound)
37137     {
37138         //Promote the f-reachable objects
37139         GcScanRoots (pfn,
37140 #ifdef MULTIPLE_HEAPS
37141                      hp->heap_number
37142 #else
37143                      0
37144 #endif //MULTIPLE_HEAPS
37145                      , 0);
37146
37147         hp->settings.found_finalizers = TRUE;
37148
37149 #ifdef BACKGROUND_GC
37150         if (hp->settings.concurrent)
37151         {
37152             hp->settings.found_finalizers = !(IsSegEmpty(FinalizerListSeg) && IsSegEmpty(CriticalFinalizerListSeg));
37153         }
37154 #endif //BACKGROUND_GC
37155         if (hp->settings.concurrent && hp->settings.found_finalizers)
37156         {
37157             if (!mark_only_p)
37158                 GCToEEInterface::EnableFinalization(true);
37159         }
37160     }
37161
37162     return finalizedFound;
37163 }
37164
37165 //Relocates all of the objects in the finalization array
37166 void
37167 CFinalize::RelocateFinalizationData (int gen, gc_heap* hp)
37168 {
37169     ScanContext sc;
37170     sc.promotion = FALSE;
37171 #ifdef MULTIPLE_HEAPS
37172     sc.thread_number = hp->heap_number;
37173 #else
37174     UNREFERENCED_PARAMETER(hp);
37175 #endif //MULTIPLE_HEAPS
37176
37177     unsigned int Seg = gen_segment (gen);
37178
37179     Object** startIndex = SegQueue (Seg);
37180     for (Object** po = startIndex; po < SegQueue (FreeList);po++)
37181     {
37182         GCHeap::Relocate (po, &sc);
37183     }
37184 }
37185
37186 void
37187 CFinalize::UpdatePromotedGenerations (int gen, BOOL gen_0_empty_p)
37188 {
37189     // update the generation fill pointers.
37190     // if gen_0_empty is FALSE, test each object to find out if
37191     // it was promoted or not
37192     if (gen_0_empty_p)
37193     {
37194         for (int i = min (gen+1, max_generation); i > 0; i--)
37195         {
37196             m_FillPointers [gen_segment(i)] = m_FillPointers [gen_segment(i-1)];
37197         }
37198     }
37199     else
37200     {
37201         //Look for demoted or promoted plugs
37202
37203         for (int i = gen; i >= 0; i--)
37204         {
37205             unsigned int Seg = gen_segment (i);
37206             Object** startIndex = SegQueue (Seg);
37207
37208             for (Object** po = startIndex;
37209                  po < SegQueueLimit (gen_segment(i)); po++)
37210             {
37211                 int new_gen = g_theGCHeap->WhichGeneration (*po);
37212                 if (new_gen != i)
37213                 {
37214                     if (new_gen > i)
37215                     {
37216                         //promotion
37217                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
37218                     }
37219                     else
37220                     {
37221                         //demotion
37222                         MoveItem (po, gen_segment (i), gen_segment (new_gen));
37223                         //back down in order to see all objects.
37224                         po--;
37225                     }
37226                 }
37227
37228             }
37229         }
37230     }
37231 }
37232
37233 BOOL
37234 CFinalize::GrowArray()
37235 {
37236     size_t oldArraySize = (m_EndArray - m_Array);
37237     size_t newArraySize =  (size_t)(((float)oldArraySize / 10) * 12);
37238
37239     Object** newArray = new (nothrow) Object*[newArraySize];
37240     if (!newArray)
37241     {
37242         // It's not safe to throw here, because of the FinalizeLock.  Tell our caller
37243         // to throw for us.
37244 //        ASSERT (newArray);
37245         return FALSE;
37246     }
37247     memcpy (newArray, m_Array, oldArraySize*sizeof(Object*));
37248
37249     //adjust the fill pointers
37250     for (int i = 0; i < FreeList; i++)
37251     {
37252         m_FillPointers [i] += (newArray - m_Array);
37253     }
37254     delete m_Array;
37255     m_Array = newArray;
37256     m_EndArray = &m_Array [newArraySize];
37257
37258     return TRUE;
37259 }
37260
37261 #ifdef VERIFY_HEAP
37262 void CFinalize::CheckFinalizerObjects()
37263 {
37264     for (int i = 0; i <= max_generation; i++)
37265     {
37266         Object **startIndex = SegQueue (gen_segment (i));
37267         Object **stopIndex  = SegQueueLimit (gen_segment (i));
37268
37269         for (Object **po = startIndex; po < stopIndex; po++)
37270         {
37271             if ((int)g_theGCHeap->WhichGeneration (*po) < i)
37272                 FATAL_GC_ERROR ();
37273             ((CObjectHeader*)*po)->Validate();
37274         }
37275     }
37276 }
37277 #endif //VERIFY_HEAP
37278
37279 #endif // FEATURE_PREMORTEM_FINALIZATION
37280
37281
37282 //------------------------------------------------------------------------------
37283 //
37284 //                      End of VM specific support
37285 //
37286 //------------------------------------------------------------------------------
37287 void gc_heap::walk_heap_per_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
37288 {
37289     generation* gen = gc_heap::generation_of (gen_number);
37290     heap_segment*    seg = generation_start_segment (gen);
37291     uint8_t*       x = ((gen_number == max_generation) ? heap_segment_mem (seg) :
37292                      generation_allocation_start (gen));
37293
37294     uint8_t*       end = heap_segment_allocated (seg);
37295     BOOL small_object_segments = TRUE;
37296     int align_const = get_alignment_constant (small_object_segments);
37297
37298     while (1)
37299
37300     {
37301         if (x >= end)
37302         {
37303             if ((seg = heap_segment_next (seg)) != 0)
37304             {
37305                 x = heap_segment_mem (seg);
37306                 end = heap_segment_allocated (seg);
37307                 continue;
37308             }
37309             else
37310             {
37311                 if (small_object_segments && walk_large_object_heap_p)
37312
37313                 {
37314                     small_object_segments = FALSE;
37315                     align_const = get_alignment_constant (small_object_segments);
37316                     seg = generation_start_segment (large_object_generation);
37317                     x = heap_segment_mem (seg);
37318                     end = heap_segment_allocated (seg);
37319                     continue;
37320                 }
37321                 else
37322                 {
37323                     break;
37324                 }
37325             }
37326         }
37327
37328         size_t s = size (x);
37329         CObjectHeader* o = (CObjectHeader*)x;
37330
37331         if (!o->IsFree())
37332
37333         {
37334             _ASSERTE(((size_t)o & 0x3) == 0); // Last two bits should never be set at this point
37335
37336             if (!fn (o->GetObjectBase(), context))
37337                 return;
37338         }
37339         x = x + Align (s, align_const);
37340     }
37341 }
37342
37343 void gc_heap::walk_finalize_queue (fq_walk_fn fn)
37344 {
37345 #ifdef FEATURE_PREMORTEM_FINALIZATION
37346     finalize_queue->WalkFReachableObjects (fn);
37347 #endif //FEATURE_PREMORTEM_FINALIZATION
37348 }
37349
37350 void gc_heap::walk_heap (walk_fn fn, void* context, int gen_number, BOOL walk_large_object_heap_p)
37351 {
37352 #ifdef MULTIPLE_HEAPS
37353     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
37354     {
37355         gc_heap* hp = gc_heap::g_heaps [hn];
37356
37357         hp->walk_heap_per_heap (fn, context, gen_number, walk_large_object_heap_p);
37358     }
37359 #else
37360     walk_heap_per_heap(fn, context, gen_number, walk_large_object_heap_p);
37361 #endif //MULTIPLE_HEAPS
37362 }
37363
37364 void GCHeap::DiagWalkObject (Object* obj, walk_fn fn, void* context)
37365 {
37366     uint8_t* o = (uint8_t*)obj;
37367     if (o)
37368     {
37369         go_through_object_cl (method_table (o), o, size(o), oo,
37370                                     {
37371                                         if (*oo)
37372                                         {
37373                                             Object *oh = (Object*)*oo;
37374                                             if (!fn (oh, context))
37375                                                 return;
37376                                         }
37377                                     }
37378             );
37379     }
37380 }
37381
37382 void GCHeap::DiagWalkSurvivorsWithType (void* gc_context, record_surv_fn fn, void* diag_context, walk_surv_type type)
37383 {
37384     gc_heap* hp = (gc_heap*)gc_context;
37385     hp->walk_survivors (fn, diag_context, type);
37386 }
37387
37388 void GCHeap::DiagWalkHeap (walk_fn fn, void* context, int gen_number, bool walk_large_object_heap_p)
37389 {
37390     gc_heap::walk_heap (fn, context, gen_number, walk_large_object_heap_p);
37391 }
37392
37393 void GCHeap::DiagWalkFinalizeQueue (void* gc_context, fq_walk_fn fn)
37394 {
37395     gc_heap* hp = (gc_heap*)gc_context;
37396     hp->walk_finalize_queue (fn);
37397 }
37398
37399 void GCHeap::DiagScanFinalizeQueue (fq_scan_fn fn, ScanContext* sc)
37400 {
37401 #ifdef MULTIPLE_HEAPS
37402     for (int hn = 0; hn < gc_heap::n_heaps; hn++)
37403     {
37404         gc_heap* hp = gc_heap::g_heaps [hn];
37405         hp->finalize_queue->GcScanRoots(fn, hn, sc);
37406     }
37407 #else
37408         pGenGCHeap->finalize_queue->GcScanRoots(fn, 0, sc);
37409 #endif //MULTIPLE_HEAPS
37410 }
37411
37412 void GCHeap::DiagScanHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
37413 {
37414     UNREFERENCED_PARAMETER(gen_number);
37415     GCScan::GcScanHandlesForProfilerAndETW (max_generation, context, fn);
37416 }
37417
37418 void GCHeap::DiagScanDependentHandles (handle_scan_fn fn, int gen_number, ScanContext* context)
37419 {
37420     UNREFERENCED_PARAMETER(gen_number);
37421     GCScan::GcScanDependentHandlesForProfilerAndETW (max_generation, context, fn);
37422 }
37423
37424 // Go through and touch (read) each page straddled by a memory block.
37425 void TouchPages(void * pStart, size_t cb)
37426 {
37427     const uint32_t pagesize = OS_PAGE_SIZE;
37428     _ASSERTE(0 == (pagesize & (pagesize-1))); // Must be a power of 2.
37429     if (cb)
37430     {
37431         VOLATILE(char)* pEnd = (VOLATILE(char)*)(cb + (char*)pStart);
37432         VOLATILE(char)* p = (VOLATILE(char)*)(((char*)pStart) -  (((size_t)pStart) & (pagesize-1)));
37433         while (p < pEnd)
37434         {
37435             char a;
37436             a = VolatileLoad(p);
37437             //printf("Touching page %lxh\n", (uint32_t)p);
37438             p += pagesize;
37439         }
37440     }
37441 }
37442
37443 #if defined(WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
37444     // This code is designed to catch the failure to update the write barrier
37445     // The way it works is to copy the whole heap right after every GC.  The write
37446     // barrier code has been modified so that it updates the shadow as well as the
37447     // real GC heap.  Before doing the next GC, we walk the heap, looking for pointers
37448     // that were updated in the real heap, but not the shadow.  A mismatch indicates
37449     // an error.  The offending code can be found by breaking after the correct GC,
37450     // and then placing a data breakpoint on the Heap location that was updated without
37451     // going through the write barrier.
37452
37453     // Called at process shutdown
37454 void deleteGCShadow()
37455 {
37456     if (g_GCShadow != 0)
37457         GCToOSInterface::VirtualRelease (g_GCShadow, g_GCShadowEnd - g_GCShadow);
37458     g_GCShadow = 0;
37459     g_GCShadowEnd = 0;
37460 }
37461
37462     // Called at startup and right after a GC, get a snapshot of the GC Heap
37463 void initGCShadow()
37464 {
37465     if (!(GCConfig::GetHeapVerifyLevel() & GCConfig::HEAPVERIFY_BARRIERCHECK))
37466         return;
37467
37468     size_t len = g_gc_highest_address - g_gc_lowest_address;
37469     if (len > (size_t)(g_GCShadowEnd - g_GCShadow)) 
37470     {
37471         deleteGCShadow();
37472         g_GCShadowEnd = g_GCShadow = (uint8_t *)GCToOSInterface::VirtualReserve(len, 0, VirtualReserveFlags::None);
37473         if (g_GCShadow == NULL || !GCToOSInterface::VirtualCommit(g_GCShadow, len))
37474         {
37475             _ASSERTE(!"Not enough memory to run HeapVerify level 2");
37476             // If after the assert we decide to allow the program to continue 
37477             // running we need to be in a state that will not trigger any 
37478             // additional AVs while we fail to allocate a shadow segment, i.e. 
37479             // ensure calls to updateGCShadow() checkGCWriteBarrier() don't AV
37480             deleteGCShadow();
37481             return;
37482         }
37483
37484         g_GCShadowEnd += len;
37485     }
37486
37487     // save the value of g_gc_lowest_address at this time.  If this value changes before
37488     // the next call to checkGCWriteBarrier() it means we extended the heap (with a
37489     // large object segment most probably), and the whole shadow segment is inconsistent.
37490     g_shadow_lowest_address = g_gc_lowest_address;
37491
37492         //****** Copy the whole GC heap ******
37493     //
37494     // NOTE: This is the one situation where the combination of heap_segment_rw(gen_start_segment())
37495     // can produce a NULL result.  This is because the initialization has not completed.
37496     //
37497     generation* gen = gc_heap::generation_of (max_generation);
37498     heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
37499
37500     ptrdiff_t delta = g_GCShadow - g_gc_lowest_address;
37501     BOOL small_object_segments = TRUE;
37502     while(1)
37503     {
37504         if (!seg)
37505         {
37506             if (small_object_segments)
37507             {
37508                 small_object_segments = FALSE;
37509                 seg = heap_segment_rw (generation_start_segment (gc_heap::generation_of (max_generation+1)));
37510                 continue;
37511             }
37512             else
37513                 break;
37514         }
37515             // Copy the segment
37516         uint8_t* start = heap_segment_mem(seg);
37517         uint8_t* end = heap_segment_allocated (seg);
37518         memcpy(start + delta, start, end - start);
37519         seg = heap_segment_next_rw (seg);
37520     }
37521 }
37522
37523 #define INVALIDGCVALUE (void*)((size_t)0xcccccccd)
37524
37525     // test to see if 'ptr' was only updated via the write barrier.
37526 inline void testGCShadow(Object** ptr)
37527 {
37528     Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_gc_lowest_address)];
37529     if (*ptr != 0 && (uint8_t*) shadow < g_GCShadowEnd && *ptr != *shadow)
37530     {
37531
37532         // If you get this assertion, someone updated a GC pointer in the heap without
37533         // using the write barrier.  To find out who, check the value of 
37534         // dd_collection_count (dynamic_data_of (0)). Also
37535         // note the value of 'ptr'.  Rerun the App that the previous GC just occurred.
37536         // Then put a data breakpoint for the value of 'ptr'  Then check every write
37537         // to pointer between the two GCs.  The last one is not using the write barrier.
37538
37539         // If the memory of interest does not exist at system startup,
37540         // you need to set the data breakpoint right after the memory gets committed
37541         // Set a breakpoint at the end of grow_heap_segment, and put the value of 'ptr'
37542         // in the memory window.  run until the memory gets mapped. Then you can set
37543         // your breakpoint
37544
37545         // Note a recent change, we've identified race conditions when updating the gc shadow.
37546         // Throughout the runtime, code will update an address in the gc heap, then erect the
37547         // write barrier, which calls updateGCShadow. With an app that pounds one heap location
37548         // from multiple threads, you can hit this assert even though all involved are using the
37549         // write barrier properly. Thusly, we detect the race and set this location to INVALIDGCVALUE.
37550         // TODO: the code in jithelp.asm doesn't call updateGCShadow, and hasn't been
37551         // TODO: fixed to detect the race. We've only seen this race from VolatileWritePtr,
37552         // TODO: so elect not to fix jithelp.asm at this time. It should be done if we start hitting
37553         // TODO: erroneous asserts in here.
37554
37555         if(*shadow!=INVALIDGCVALUE)
37556         {
37557 #ifdef FEATURE_BASICFREEZE
37558             // Write barriers for stores of references to frozen objects may be optimized away.
37559             if (!gc_heap::frozen_object_p(*ptr))
37560 #endif // FEATURE_BASICFREEZE
37561             {
37562                 _ASSERTE(!"Pointer updated without using write barrier");
37563             }
37564         }
37565         /*
37566         else
37567         {
37568              printf("saw a INVALIDGCVALUE. (just to let you know)\n");
37569         }
37570         */
37571     }
37572 }
37573
37574 void testGCShadowHelper (uint8_t* x)
37575 {
37576     size_t s = size (x);
37577     if (contain_pointers (x))
37578     {
37579         go_through_object_nostart (method_table(x), x, s, oo,
37580                            { testGCShadow((Object**) oo); });
37581     }
37582 }
37583
37584     // Walk the whole heap, looking for pointers that were not updated with the write barrier.
37585 void checkGCWriteBarrier()
37586 {
37587     // g_shadow_lowest_address != g_gc_lowest_address means the GC heap was extended by a segment
37588     // and the GC shadow segment did not track that change!
37589     if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_gc_lowest_address)
37590     {
37591         // No shadow stack, nothing to check.
37592         return;
37593     }
37594
37595     {
37596         generation* gen = gc_heap::generation_of (max_generation);
37597         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
37598
37599         PREFIX_ASSUME(seg != NULL);
37600
37601         while(seg)
37602         {
37603             uint8_t* x = heap_segment_mem(seg);
37604             while (x < heap_segment_allocated (seg))
37605             {
37606                 size_t s = size (x);
37607                 testGCShadowHelper (x);
37608                 x = x + Align (s);
37609             }
37610             seg = heap_segment_next_rw (seg);
37611         }
37612     }
37613
37614     {
37615         // go through large object heap
37616         int alignment = get_alignment_constant(FALSE);
37617         generation* gen = gc_heap::generation_of (max_generation+1);
37618         heap_segment* seg = heap_segment_rw (generation_start_segment (gen));
37619
37620         PREFIX_ASSUME(seg != NULL);
37621
37622         while(seg)
37623         {
37624             uint8_t* x = heap_segment_mem(seg);
37625             while (x < heap_segment_allocated (seg))
37626             {
37627                 size_t s = size (x);
37628                 testGCShadowHelper (x);
37629                 x = x + Align (s, alignment);
37630             }
37631             seg = heap_segment_next_rw (seg);
37632         }
37633     }
37634 }
37635 #endif //WRITE_BARRIER_CHECK && !SERVER_GC
37636
37637 #endif // !DACCESS_COMPILE
37638
37639 #ifdef FEATURE_BASICFREEZE
37640 void gc_heap::walk_read_only_segment(heap_segment *seg, void *pvContext, object_callback_func pfnMethodTable, object_callback_func pfnObjRef)
37641 {
37642 #ifdef DACCESS_COMPILE
37643     UNREFERENCED_PARAMETER(seg);
37644     UNREFERENCED_PARAMETER(pvContext);
37645     UNREFERENCED_PARAMETER(pfnMethodTable);
37646     UNREFERENCED_PARAMETER(pfnObjRef);
37647 #else
37648     uint8_t *o = heap_segment_mem(seg);
37649
37650     // small heap alignment constant
37651     int alignment = get_alignment_constant(TRUE);
37652
37653     while (o < heap_segment_allocated(seg))
37654     {
37655         pfnMethodTable(pvContext, o);
37656
37657         if (contain_pointers (o))
37658         {
37659             go_through_object_nostart (method_table (o), o, size(o), oo,
37660                    {
37661                        if (*oo)
37662                            pfnObjRef(pvContext, oo);
37663                    }
37664             );
37665         }
37666
37667         o += Align(size(o), alignment);
37668     }
37669 #endif //!DACCESS_COMPILE
37670 }
37671 #endif // FEATURE_BASICFREEZE
37672
37673 #ifndef DACCESS_COMPILE
37674 HRESULT GCHeap::WaitUntilConcurrentGCCompleteAsync(int millisecondsTimeout)
37675 {
37676 #ifdef BACKGROUND_GC
37677     if (recursive_gc_sync::background_running_p())
37678     {
37679         uint32_t dwRet = pGenGCHeap->background_gc_wait(awr_ignored, millisecondsTimeout);
37680         if (dwRet == WAIT_OBJECT_0)
37681             return S_OK;
37682         else if (dwRet == WAIT_TIMEOUT)
37683             return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
37684         else
37685             return E_FAIL;      // It is not clear if what the last error would be if the wait failed,
37686                                 // as there are too many layers in between. The best we can do is to return E_FAIL;
37687     }
37688 #endif
37689
37690     return S_OK;
37691 }
37692 #endif // !DACCESS_COMPILE
37693
37694 void GCHeap::TemporaryEnableConcurrentGC()
37695 {
37696 #ifdef BACKGROUND_GC
37697     gc_heap::temp_disable_concurrent_p = false;
37698 #endif //BACKGROUND_GC
37699 }
37700
37701 void GCHeap::TemporaryDisableConcurrentGC()
37702 {
37703 #ifdef BACKGROUND_GC
37704     gc_heap::temp_disable_concurrent_p = true;
37705 #endif //BACKGROUND_GC
37706 }
37707
37708 bool GCHeap::IsConcurrentGCEnabled()
37709 {
37710 #ifdef BACKGROUND_GC
37711     return (gc_heap::gc_can_use_concurrent && !(gc_heap::temp_disable_concurrent_p));
37712 #else
37713     return FALSE;
37714 #endif //BACKGROUND_GC
37715 }
37716
37717 void GCHeap::SetFinalizeRunOnShutdown(bool value)
37718 {
37719     g_fFinalizerRunOnShutDown = value;
37720 }
37721
37722 void PopulateDacVars(GcDacVars *gcDacVars)
37723 {
37724 #ifndef DACCESS_COMPILE
37725     assert(gcDacVars != nullptr);
37726     *gcDacVars = {};
37727     gcDacVars->major_version_number = 1;
37728     gcDacVars->minor_version_number = 0;
37729     gcDacVars->built_with_svr = &g_built_with_svr_gc;
37730     gcDacVars->build_variant = &g_build_variant;
37731     gcDacVars->gc_structures_invalid_cnt = const_cast<int32_t*>(&GCScan::m_GcStructuresInvalidCnt);
37732     gcDacVars->generation_size = sizeof(generation);
37733     gcDacVars->max_gen = &g_max_generation;
37734 #ifndef MULTIPLE_HEAPS
37735     gcDacVars->mark_array = &gc_heap::mark_array;
37736     gcDacVars->ephemeral_heap_segment = reinterpret_cast<dac_heap_segment**>(&gc_heap::ephemeral_heap_segment);
37737     gcDacVars->current_c_gc_state = const_cast<c_gc_state*>(&gc_heap::current_c_gc_state);
37738     gcDacVars->saved_sweep_ephemeral_seg = reinterpret_cast<dac_heap_segment**>(&gc_heap::saved_sweep_ephemeral_seg);
37739     gcDacVars->saved_sweep_ephemeral_start = &gc_heap::saved_sweep_ephemeral_start;
37740     gcDacVars->background_saved_lowest_address = &gc_heap::background_saved_lowest_address;
37741     gcDacVars->background_saved_highest_address = &gc_heap::background_saved_highest_address;
37742     gcDacVars->alloc_allocated = &gc_heap::alloc_allocated;
37743     gcDacVars->next_sweep_obj = &gc_heap::next_sweep_obj;
37744     gcDacVars->oom_info = &gc_heap::oom_info;
37745     gcDacVars->finalize_queue = reinterpret_cast<dac_finalize_queue**>(&gc_heap::finalize_queue);
37746     gcDacVars->generation_table = reinterpret_cast<dac_generation**>(&gc_heap::generation_table);
37747 #ifdef GC_CONFIG_DRIVEN
37748     gcDacVars->gc_global_mechanisms = reinterpret_cast<size_t**>(&gc_global_mechanisms);
37749     gcDacVars->interesting_data_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_data_per_heap);
37750     gcDacVars->compact_reasons_per_heap = reinterpret_cast<size_t**>(&gc_heap::compact_reasons_per_heap);
37751     gcDacVars->expand_mechanisms_per_heap = reinterpret_cast<size_t**>(&gc_heap::expand_mechanisms_per_heap);
37752     gcDacVars->interesting_mechanism_bits_per_heap = reinterpret_cast<size_t**>(&gc_heap::interesting_mechanism_bits_per_heap);
37753 #endif // GC_CONFIG_DRIVEN
37754 #ifdef HEAP_ANALYZE
37755     gcDacVars->internal_root_array = &gc_heap::internal_root_array;
37756     gcDacVars->internal_root_array_index = &gc_heap::internal_root_array_index;
37757     gcDacVars->heap_analyze_success = &gc_heap::heap_analyze_success;
37758 #endif // HEAP_ANALYZE
37759 #else
37760     gcDacVars->n_heaps = &gc_heap::n_heaps;
37761     gcDacVars->g_heaps = reinterpret_cast<dac_gc_heap***>(&gc_heap::g_heaps);
37762 #endif // MULTIPLE_HEAPS
37763 #endif // DACCESS_COMPILE
37764 }